roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     /**
1994      * using 'cn' the nested child reader read the child array into it's child stores.
1995      * @param {Object} rec The record with a 'children array
1996      */
1997     loadDataFromChildren: function(rec)
1998     {
1999         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2000         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2001         return this.loadData({ data : data, total : data.length });
2002         
2003     }
2004 });/*
2005  * Based on:
2006  * Ext JS Library 1.1.1
2007  * Copyright(c) 2006-2007, Ext JS, LLC.
2008  *
2009  * Originally Released Under LGPL - original licence link has changed is not relivant.
2010  *
2011  * Fork - LGPL
2012  * <script type="text/javascript">
2013  */
2014
2015 /**
2016  * @class Roo.data.XmlReader
2017  * @extends Roo.data.DataReader
2018  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2019  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2020  * <p>
2021  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2022  * header in the HTTP response must be set to "text/xml".</em>
2023  * <p>
2024  * Example code:
2025  * <pre><code>
2026 var RecordDef = Roo.data.Record.create([
2027    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2028    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2029 ]);
2030 var myReader = new Roo.data.XmlReader({
2031    totalRecords: "results", // The element which contains the total dataset size (optional)
2032    record: "row",           // The repeated element which contains row information
2033    id: "id"                 // The element within the row that provides an ID for the record (optional)
2034 }, RecordDef);
2035 </code></pre>
2036  * <p>
2037  * This would consume an XML file like this:
2038  * <pre><code>
2039 &lt;?xml?>
2040 &lt;dataset>
2041  &lt;results>2&lt;/results>
2042  &lt;row>
2043    &lt;id>1&lt;/id>
2044    &lt;name>Bill&lt;/name>
2045    &lt;occupation>Gardener&lt;/occupation>
2046  &lt;/row>
2047  &lt;row>
2048    &lt;id>2&lt;/id>
2049    &lt;name>Ben&lt;/name>
2050    &lt;occupation>Horticulturalist&lt;/occupation>
2051  &lt;/row>
2052 &lt;/dataset>
2053 </code></pre>
2054  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2055  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2056  * paged from the remote server.
2057  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2058  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2059  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2060  * a record identifier value.
2061  * @constructor
2062  * Create a new XmlReader
2063  * @param {Object} meta Metadata configuration options
2064  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2065  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2066  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2067  */
2068 Roo.data.XmlReader = function(meta, recordType){
2069     meta = meta || {};
2070     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2071 };
2072 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2073     
2074     readerType : 'Xml',
2075     
2076     /**
2077      * This method is only used by a DataProxy which has retrieved data from a remote server.
2078          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2079          * to contain a method called 'responseXML' that returns an XML document object.
2080      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2081      * a cache of Roo.data.Records.
2082      */
2083     read : function(response){
2084         var doc = response.responseXML;
2085         if(!doc) {
2086             throw {message: "XmlReader.read: XML Document not available"};
2087         }
2088         return this.readRecords(doc);
2089     },
2090
2091     /**
2092      * Create a data block containing Roo.data.Records from an XML document.
2093          * @param {Object} doc A parsed XML document.
2094      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2095      * a cache of Roo.data.Records.
2096      */
2097     readRecords : function(doc){
2098         /**
2099          * After any data loads/reads, the raw XML Document is available for further custom processing.
2100          * @type XMLDocument
2101          */
2102         this.xmlData = doc;
2103         var root = doc.documentElement || doc;
2104         var q = Roo.DomQuery;
2105         var recordType = this.recordType, fields = recordType.prototype.fields;
2106         var sid = this.meta.id;
2107         var totalRecords = 0, success = true;
2108         if(this.meta.totalRecords){
2109             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2110         }
2111         
2112         if(this.meta.success){
2113             var sv = q.selectValue(this.meta.success, root, true);
2114             success = sv !== false && sv !== 'false';
2115         }
2116         var records = [];
2117         var ns = q.select(this.meta.record, root);
2118         for(var i = 0, len = ns.length; i < len; i++) {
2119                 var n = ns[i];
2120                 var values = {};
2121                 var id = sid ? q.selectValue(sid, n) : undefined;
2122                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2123                     var f = fields.items[j];
2124                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2125                     v = f.convert(v);
2126                     values[f.name] = v;
2127                 }
2128                 var record = new recordType(values, id);
2129                 record.node = n;
2130                 records[records.length] = record;
2131             }
2132
2133             return {
2134                 success : success,
2135                 records : records,
2136                 totalRecords : totalRecords || records.length
2137             };
2138     }
2139 });/*
2140  * Based on:
2141  * Ext JS Library 1.1.1
2142  * Copyright(c) 2006-2007, Ext JS, LLC.
2143  *
2144  * Originally Released Under LGPL - original licence link has changed is not relivant.
2145  *
2146  * Fork - LGPL
2147  * <script type="text/javascript">
2148  */
2149
2150 /**
2151  * @class Roo.data.ArrayReader
2152  * @extends Roo.data.DataReader
2153  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2154  * Each element of that Array represents a row of data fields. The
2155  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2156  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2157  * <p>
2158  * Example code:.
2159  * <pre><code>
2160 var RecordDef = Roo.data.Record.create([
2161     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2162     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2163 ]);
2164 var myReader = new Roo.data.ArrayReader({
2165     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2166 }, RecordDef);
2167 </code></pre>
2168  * <p>
2169  * This would consume an Array like this:
2170  * <pre><code>
2171 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2172   </code></pre>
2173  
2174  * @constructor
2175  * Create a new JsonReader
2176  * @param {Object} meta Metadata configuration options.
2177  * @param {Object|Array} recordType Either an Array of field definition objects
2178  * 
2179  * @cfg {Array} fields Array of field definition objects
2180  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2181  * as specified to {@link Roo.data.Record#create},
2182  * or an {@link Roo.data.Record} object
2183  *
2184  * 
2185  * created using {@link Roo.data.Record#create}.
2186  */
2187 Roo.data.ArrayReader = function(meta, recordType)
2188 {    
2189     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2190 };
2191
2192 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2193     
2194       /**
2195      * Create a data block containing Roo.data.Records from an XML document.
2196      * @param {Object} o An Array of row objects which represents the dataset.
2197      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2198      * a cache of Roo.data.Records.
2199      */
2200     readRecords : function(o)
2201     {
2202         var sid = this.meta ? this.meta.id : null;
2203         var recordType = this.recordType, fields = recordType.prototype.fields;
2204         var records = [];
2205         var root = o;
2206         for(var i = 0; i < root.length; i++){
2207                 var n = root[i];
2208             var values = {};
2209             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2210             for(var j = 0, jlen = fields.length; j < jlen; j++){
2211                 var f = fields.items[j];
2212                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2213                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2214                 v = f.convert(v);
2215                 values[f.name] = v;
2216             }
2217             var record = new recordType(values, id);
2218             record.json = n;
2219             records[records.length] = record;
2220         }
2221         return {
2222             records : records,
2223             totalRecords : records.length
2224         };
2225     },
2226     /**
2227      * using 'cn' the nested child reader read the child array into it's child stores.
2228      * @param {Object} rec The record with a 'children array
2229      */
2230     loadDataFromChildren: function(rec)
2231     {
2232         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2233         return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
2234         
2235     }
2236     
2237     
2238 });/*
2239  * Based on:
2240  * Ext JS Library 1.1.1
2241  * Copyright(c) 2006-2007, Ext JS, LLC.
2242  *
2243  * Originally Released Under LGPL - original licence link has changed is not relivant.
2244  *
2245  * Fork - LGPL
2246  * <script type="text/javascript">
2247  */
2248
2249
2250 /**
2251  * @class Roo.data.Tree
2252  * @extends Roo.util.Observable
2253  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2254  * in the tree have most standard DOM functionality.
2255  * @constructor
2256  * @param {Node} root (optional) The root node
2257  */
2258 Roo.data.Tree = function(root){
2259    this.nodeHash = {};
2260    /**
2261     * The root node for this tree
2262     * @type Node
2263     */
2264    this.root = null;
2265    if(root){
2266        this.setRootNode(root);
2267    }
2268    this.addEvents({
2269        /**
2270         * @event append
2271         * Fires when a new child node is appended to a node in this tree.
2272         * @param {Tree} tree The owner tree
2273         * @param {Node} parent The parent node
2274         * @param {Node} node The newly appended node
2275         * @param {Number} index The index of the newly appended node
2276         */
2277        "append" : true,
2278        /**
2279         * @event remove
2280         * Fires when a child node is removed from a node in this tree.
2281         * @param {Tree} tree The owner tree
2282         * @param {Node} parent The parent node
2283         * @param {Node} node The child node removed
2284         */
2285        "remove" : true,
2286        /**
2287         * @event move
2288         * Fires when a node is moved to a new location in the tree
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} node The node moved
2291         * @param {Node} oldParent The old parent of this node
2292         * @param {Node} newParent The new parent of this node
2293         * @param {Number} index The index it was moved to
2294         */
2295        "move" : true,
2296        /**
2297         * @event insert
2298         * Fires when a new child node is inserted in a node in this tree.
2299         * @param {Tree} tree The owner tree
2300         * @param {Node} parent The parent node
2301         * @param {Node} node The child node inserted
2302         * @param {Node} refNode The child node the node was inserted before
2303         */
2304        "insert" : true,
2305        /**
2306         * @event beforeappend
2307         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} parent The parent node
2310         * @param {Node} node The child node to be appended
2311         */
2312        "beforeappend" : true,
2313        /**
2314         * @event beforeremove
2315         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2316         * @param {Tree} tree The owner tree
2317         * @param {Node} parent The parent node
2318         * @param {Node} node The child node to be removed
2319         */
2320        "beforeremove" : true,
2321        /**
2322         * @event beforemove
2323         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2324         * @param {Tree} tree The owner tree
2325         * @param {Node} node The node being moved
2326         * @param {Node} oldParent The parent of the node
2327         * @param {Node} newParent The new parent the node is moving to
2328         * @param {Number} index The index it is being moved to
2329         */
2330        "beforemove" : true,
2331        /**
2332         * @event beforeinsert
2333         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2334         * @param {Tree} tree The owner tree
2335         * @param {Node} parent The parent node
2336         * @param {Node} node The child node to be inserted
2337         * @param {Node} refNode The child node the node is being inserted before
2338         */
2339        "beforeinsert" : true
2340    });
2341
2342     Roo.data.Tree.superclass.constructor.call(this);
2343 };
2344
2345 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2346     pathSeparator: "/",
2347
2348     proxyNodeEvent : function(){
2349         return this.fireEvent.apply(this, arguments);
2350     },
2351
2352     /**
2353      * Returns the root node for this tree.
2354      * @return {Node}
2355      */
2356     getRootNode : function(){
2357         return this.root;
2358     },
2359
2360     /**
2361      * Sets the root node for this tree.
2362      * @param {Node} node
2363      * @return {Node}
2364      */
2365     setRootNode : function(node){
2366         this.root = node;
2367         node.ownerTree = this;
2368         node.isRoot = true;
2369         this.registerNode(node);
2370         return node;
2371     },
2372
2373     /**
2374      * Gets a node in this tree by its id.
2375      * @param {String} id
2376      * @return {Node}
2377      */
2378     getNodeById : function(id){
2379         return this.nodeHash[id];
2380     },
2381
2382     registerNode : function(node){
2383         this.nodeHash[node.id] = node;
2384     },
2385
2386     unregisterNode : function(node){
2387         delete this.nodeHash[node.id];
2388     },
2389
2390     toString : function(){
2391         return "[Tree"+(this.id?" "+this.id:"")+"]";
2392     }
2393 });
2394
2395 /**
2396  * @class Roo.data.Node
2397  * @extends Roo.util.Observable
2398  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2399  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2400  * @constructor
2401  * @param {Object} attributes The attributes/config for the node
2402  */
2403 Roo.data.Node = function(attributes){
2404     /**
2405      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2406      * @type {Object}
2407      */
2408     this.attributes = attributes || {};
2409     this.leaf = this.attributes.leaf;
2410     /**
2411      * The node id. @type String
2412      */
2413     this.id = this.attributes.id;
2414     if(!this.id){
2415         this.id = Roo.id(null, "ynode-");
2416         this.attributes.id = this.id;
2417     }
2418      
2419     
2420     /**
2421      * All child nodes of this node. @type Array
2422      */
2423     this.childNodes = [];
2424     if(!this.childNodes.indexOf){ // indexOf is a must
2425         this.childNodes.indexOf = function(o){
2426             for(var i = 0, len = this.length; i < len; i++){
2427                 if(this[i] == o) {
2428                     return i;
2429                 }
2430             }
2431             return -1;
2432         };
2433     }
2434     /**
2435      * The parent node for this node. @type Node
2436      */
2437     this.parentNode = null;
2438     /**
2439      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2440      */
2441     this.firstChild = null;
2442     /**
2443      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2444      */
2445     this.lastChild = null;
2446     /**
2447      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2448      */
2449     this.previousSibling = null;
2450     /**
2451      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2452      */
2453     this.nextSibling = null;
2454
2455     this.addEvents({
2456        /**
2457         * @event append
2458         * Fires when a new child node is appended
2459         * @param {Tree} tree The owner tree
2460         * @param {Node} this This node
2461         * @param {Node} node The newly appended node
2462         * @param {Number} index The index of the newly appended node
2463         */
2464        "append" : true,
2465        /**
2466         * @event remove
2467         * Fires when a child node is removed
2468         * @param {Tree} tree The owner tree
2469         * @param {Node} this This node
2470         * @param {Node} node The removed node
2471         */
2472        "remove" : true,
2473        /**
2474         * @event move
2475         * Fires when this node is moved to a new location in the tree
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} oldParent The old parent of this node
2479         * @param {Node} newParent The new parent of this node
2480         * @param {Number} index The index it was moved to
2481         */
2482        "move" : true,
2483        /**
2484         * @event insert
2485         * Fires when a new child node is inserted.
2486         * @param {Tree} tree The owner tree
2487         * @param {Node} this This node
2488         * @param {Node} node The child node inserted
2489         * @param {Node} refNode The child node the node was inserted before
2490         */
2491        "insert" : true,
2492        /**
2493         * @event beforeappend
2494         * Fires before a new child is appended, return false to cancel the append.
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} node The child node to be appended
2498         */
2499        "beforeappend" : true,
2500        /**
2501         * @event beforeremove
2502         * Fires before a child is removed, return false to cancel the remove.
2503         * @param {Tree} tree The owner tree
2504         * @param {Node} this This node
2505         * @param {Node} node The child node to be removed
2506         */
2507        "beforeremove" : true,
2508        /**
2509         * @event beforemove
2510         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2511         * @param {Tree} tree The owner tree
2512         * @param {Node} this This node
2513         * @param {Node} oldParent The parent of this node
2514         * @param {Node} newParent The new parent this node is moving to
2515         * @param {Number} index The index it is being moved to
2516         */
2517        "beforemove" : true,
2518        /**
2519         * @event beforeinsert
2520         * Fires before a new child is inserted, return false to cancel the insert.
2521         * @param {Tree} tree The owner tree
2522         * @param {Node} this This node
2523         * @param {Node} node The child node to be inserted
2524         * @param {Node} refNode The child node the node is being inserted before
2525         */
2526        "beforeinsert" : true
2527    });
2528     this.listeners = this.attributes.listeners;
2529     Roo.data.Node.superclass.constructor.call(this);
2530 };
2531
2532 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2533     fireEvent : function(evtName){
2534         // first do standard event for this node
2535         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2536             return false;
2537         }
2538         // then bubble it up to the tree if the event wasn't cancelled
2539         var ot = this.getOwnerTree();
2540         if(ot){
2541             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2542                 return false;
2543             }
2544         }
2545         return true;
2546     },
2547
2548     /**
2549      * Returns true if this node is a leaf
2550      * @return {Boolean}
2551      */
2552     isLeaf : function(){
2553         return this.leaf === true;
2554     },
2555
2556     // private
2557     setFirstChild : function(node){
2558         this.firstChild = node;
2559     },
2560
2561     //private
2562     setLastChild : function(node){
2563         this.lastChild = node;
2564     },
2565
2566
2567     /**
2568      * Returns true if this node is the last child of its parent
2569      * @return {Boolean}
2570      */
2571     isLast : function(){
2572        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2573     },
2574
2575     /**
2576      * Returns true if this node is the first child of its parent
2577      * @return {Boolean}
2578      */
2579     isFirst : function(){
2580        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2581     },
2582
2583     hasChildNodes : function(){
2584         return !this.isLeaf() && this.childNodes.length > 0;
2585     },
2586
2587     /**
2588      * Insert node(s) as the last child node of this node.
2589      * @param {Node/Array} node The node or Array of nodes to append
2590      * @return {Node} The appended node if single append, or null if an array was passed
2591      */
2592     appendChild : function(node){
2593         var multi = false;
2594         if(node instanceof Array){
2595             multi = node;
2596         }else if(arguments.length > 1){
2597             multi = arguments;
2598         }
2599         
2600         // if passed an array or multiple args do them one by one
2601         if(multi){
2602             for(var i = 0, len = multi.length; i < len; i++) {
2603                 this.appendChild(multi[i]);
2604             }
2605         }else{
2606             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2607                 return false;
2608             }
2609             var index = this.childNodes.length;
2610             var oldParent = node.parentNode;
2611             // it's a move, make sure we move it cleanly
2612             if(oldParent){
2613                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2614                     return false;
2615                 }
2616                 oldParent.removeChild(node);
2617             }
2618             
2619             index = this.childNodes.length;
2620             if(index == 0){
2621                 this.setFirstChild(node);
2622             }
2623             this.childNodes.push(node);
2624             node.parentNode = this;
2625             var ps = this.childNodes[index-1];
2626             if(ps){
2627                 node.previousSibling = ps;
2628                 ps.nextSibling = node;
2629             }else{
2630                 node.previousSibling = null;
2631             }
2632             node.nextSibling = null;
2633             this.setLastChild(node);
2634             node.setOwnerTree(this.getOwnerTree());
2635             this.fireEvent("append", this.ownerTree, this, node, index);
2636             if(this.ownerTree) {
2637                 this.ownerTree.fireEvent("appendnode", this, node, index);
2638             }
2639             if(oldParent){
2640                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2641             }
2642             return node;
2643         }
2644     },
2645
2646     /**
2647      * Removes a child node from this node.
2648      * @param {Node} node The node to remove
2649      * @return {Node} The removed node
2650      */
2651     removeChild : function(node){
2652         var index = this.childNodes.indexOf(node);
2653         if(index == -1){
2654             return false;
2655         }
2656         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2657             return false;
2658         }
2659
2660         // remove it from childNodes collection
2661         this.childNodes.splice(index, 1);
2662
2663         // update siblings
2664         if(node.previousSibling){
2665             node.previousSibling.nextSibling = node.nextSibling;
2666         }
2667         if(node.nextSibling){
2668             node.nextSibling.previousSibling = node.previousSibling;
2669         }
2670
2671         // update child refs
2672         if(this.firstChild == node){
2673             this.setFirstChild(node.nextSibling);
2674         }
2675         if(this.lastChild == node){
2676             this.setLastChild(node.previousSibling);
2677         }
2678
2679         node.setOwnerTree(null);
2680         // clear any references from the node
2681         node.parentNode = null;
2682         node.previousSibling = null;
2683         node.nextSibling = null;
2684         this.fireEvent("remove", this.ownerTree, this, node);
2685         return node;
2686     },
2687
2688     /**
2689      * Inserts the first node before the second node in this nodes childNodes collection.
2690      * @param {Node} node The node to insert
2691      * @param {Node} refNode The node to insert before (if null the node is appended)
2692      * @return {Node} The inserted node
2693      */
2694     insertBefore : function(node, refNode){
2695         if(!refNode){ // like standard Dom, refNode can be null for append
2696             return this.appendChild(node);
2697         }
2698         // nothing to do
2699         if(node == refNode){
2700             return false;
2701         }
2702
2703         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2704             return false;
2705         }
2706         var index = this.childNodes.indexOf(refNode);
2707         var oldParent = node.parentNode;
2708         var refIndex = index;
2709
2710         // when moving internally, indexes will change after remove
2711         if(oldParent == this && this.childNodes.indexOf(node) < index){
2712             refIndex--;
2713         }
2714
2715         // it's a move, make sure we move it cleanly
2716         if(oldParent){
2717             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2718                 return false;
2719             }
2720             oldParent.removeChild(node);
2721         }
2722         if(refIndex == 0){
2723             this.setFirstChild(node);
2724         }
2725         this.childNodes.splice(refIndex, 0, node);
2726         node.parentNode = this;
2727         var ps = this.childNodes[refIndex-1];
2728         if(ps){
2729             node.previousSibling = ps;
2730             ps.nextSibling = node;
2731         }else{
2732             node.previousSibling = null;
2733         }
2734         node.nextSibling = refNode;
2735         refNode.previousSibling = node;
2736         node.setOwnerTree(this.getOwnerTree());
2737         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2738         if(oldParent){
2739             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2740         }
2741         return node;
2742     },
2743
2744     /**
2745      * Returns the child node at the specified index.
2746      * @param {Number} index
2747      * @return {Node}
2748      */
2749     item : function(index){
2750         return this.childNodes[index];
2751     },
2752
2753     /**
2754      * Replaces one child node in this node with another.
2755      * @param {Node} newChild The replacement node
2756      * @param {Node} oldChild The node to replace
2757      * @return {Node} The replaced node
2758      */
2759     replaceChild : function(newChild, oldChild){
2760         this.insertBefore(newChild, oldChild);
2761         this.removeChild(oldChild);
2762         return oldChild;
2763     },
2764
2765     /**
2766      * Returns the index of a child node
2767      * @param {Node} node
2768      * @return {Number} The index of the node or -1 if it was not found
2769      */
2770     indexOf : function(child){
2771         return this.childNodes.indexOf(child);
2772     },
2773
2774     /**
2775      * Returns the tree this node is in.
2776      * @return {Tree}
2777      */
2778     getOwnerTree : function(){
2779         // if it doesn't have one, look for one
2780         if(!this.ownerTree){
2781             var p = this;
2782             while(p){
2783                 if(p.ownerTree){
2784                     this.ownerTree = p.ownerTree;
2785                     break;
2786                 }
2787                 p = p.parentNode;
2788             }
2789         }
2790         return this.ownerTree;
2791     },
2792
2793     /**
2794      * Returns depth of this node (the root node has a depth of 0)
2795      * @return {Number}
2796      */
2797     getDepth : function(){
2798         var depth = 0;
2799         var p = this;
2800         while(p.parentNode){
2801             ++depth;
2802             p = p.parentNode;
2803         }
2804         return depth;
2805     },
2806
2807     // private
2808     setOwnerTree : function(tree){
2809         // if it's move, we need to update everyone
2810         if(tree != this.ownerTree){
2811             if(this.ownerTree){
2812                 this.ownerTree.unregisterNode(this);
2813             }
2814             this.ownerTree = tree;
2815             var cs = this.childNodes;
2816             for(var i = 0, len = cs.length; i < len; i++) {
2817                 cs[i].setOwnerTree(tree);
2818             }
2819             if(tree){
2820                 tree.registerNode(this);
2821             }
2822         }
2823     },
2824
2825     /**
2826      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2827      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2828      * @return {String} The path
2829      */
2830     getPath : function(attr){
2831         attr = attr || "id";
2832         var p = this.parentNode;
2833         var b = [this.attributes[attr]];
2834         while(p){
2835             b.unshift(p.attributes[attr]);
2836             p = p.parentNode;
2837         }
2838         var sep = this.getOwnerTree().pathSeparator;
2839         return sep + b.join(sep);
2840     },
2841
2842     /**
2843      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2844      * function call will be the scope provided or the current node. The arguments to the function
2845      * will be the args provided or the current node. If the function returns false at any point,
2846      * the bubble is stopped.
2847      * @param {Function} fn The function to call
2848      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2849      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2850      */
2851     bubble : function(fn, scope, args){
2852         var p = this;
2853         while(p){
2854             if(fn.call(scope || p, args || p) === false){
2855                 break;
2856             }
2857             p = p.parentNode;
2858         }
2859     },
2860
2861     /**
2862      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the cascade is stopped on that branch.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     cascade : function(fn, scope, args){
2871         if(fn.call(scope || this, args || this) !== false){
2872             var cs = this.childNodes;
2873             for(var i = 0, len = cs.length; i < len; i++) {
2874                 cs[i].cascade(fn, scope, args);
2875             }
2876         }
2877     },
2878
2879     /**
2880      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2881      * function call will be the scope provided or the current node. The arguments to the function
2882      * will be the args provided or the current node. If the function returns false at any point,
2883      * the iteration stops.
2884      * @param {Function} fn The function to call
2885      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2886      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2887      */
2888     eachChild : function(fn, scope, args){
2889         var cs = this.childNodes;
2890         for(var i = 0, len = cs.length; i < len; i++) {
2891                 if(fn.call(scope || this, args || cs[i]) === false){
2892                     break;
2893                 }
2894         }
2895     },
2896
2897     /**
2898      * Finds the first child that has the attribute with the specified value.
2899      * @param {String} attribute The attribute name
2900      * @param {Mixed} value The value to search for
2901      * @return {Node} The found child or null if none was found
2902      */
2903     findChild : function(attribute, value){
2904         var cs = this.childNodes;
2905         for(var i = 0, len = cs.length; i < len; i++) {
2906                 if(cs[i].attributes[attribute] == value){
2907                     return cs[i];
2908                 }
2909         }
2910         return null;
2911     },
2912
2913     /**
2914      * Finds the first child by a custom function. The child matches if the function passed
2915      * returns true.
2916      * @param {Function} fn
2917      * @param {Object} scope (optional)
2918      * @return {Node} The found child or null if none was found
2919      */
2920     findChildBy : function(fn, scope){
2921         var cs = this.childNodes;
2922         for(var i = 0, len = cs.length; i < len; i++) {
2923                 if(fn.call(scope||cs[i], cs[i]) === true){
2924                     return cs[i];
2925                 }
2926         }
2927         return null;
2928     },
2929
2930     /**
2931      * Sorts this nodes children using the supplied sort function
2932      * @param {Function} fn
2933      * @param {Object} scope (optional)
2934      */
2935     sort : function(fn, scope){
2936         var cs = this.childNodes;
2937         var len = cs.length;
2938         if(len > 0){
2939             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2940             cs.sort(sortFn);
2941             for(var i = 0; i < len; i++){
2942                 var n = cs[i];
2943                 n.previousSibling = cs[i-1];
2944                 n.nextSibling = cs[i+1];
2945                 if(i == 0){
2946                     this.setFirstChild(n);
2947                 }
2948                 if(i == len-1){
2949                     this.setLastChild(n);
2950                 }
2951             }
2952         }
2953     },
2954
2955     /**
2956      * Returns true if this node is an ancestor (at any point) of the passed node.
2957      * @param {Node} node
2958      * @return {Boolean}
2959      */
2960     contains : function(node){
2961         return node.isAncestor(this);
2962     },
2963
2964     /**
2965      * Returns true if the passed node is an ancestor (at any point) of this node.
2966      * @param {Node} node
2967      * @return {Boolean}
2968      */
2969     isAncestor : function(node){
2970         var p = this.parentNode;
2971         while(p){
2972             if(p == node){
2973                 return true;
2974             }
2975             p = p.parentNode;
2976         }
2977         return false;
2978     },
2979
2980     toString : function(){
2981         return "[Node"+(this.id?" "+this.id:"")+"]";
2982     }
2983 });/*
2984  * Based on:
2985  * Ext JS Library 1.1.1
2986  * Copyright(c) 2006-2007, Ext JS, LLC.
2987  *
2988  * Originally Released Under LGPL - original licence link has changed is not relivant.
2989  *
2990  * Fork - LGPL
2991  * <script type="text/javascript">
2992  */
2993  (function(){ 
2994 /**
2995  * @class Roo.Layer
2996  * @extends Roo.Element
2997  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2998  * automatic maintaining of shadow/shim positions.
2999  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
3000  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
3001  * you can pass a string with a CSS class name. False turns off the shadow.
3002  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
3003  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
3004  * @cfg {String} cls CSS class to add to the element
3005  * @cfg {Number} zindex Starting z-index (defaults to 11000)
3006  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
3007  * @constructor
3008  * @param {Object} config An object with config options.
3009  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
3010  */
3011
3012 Roo.Layer = function(config, existingEl){
3013     config = config || {};
3014     var dh = Roo.DomHelper;
3015     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
3016     if(existingEl){
3017         this.dom = Roo.getDom(existingEl);
3018     }
3019     if(!this.dom){
3020         var o = config.dh || {tag: "div", cls: "x-layer"};
3021         this.dom = dh.append(pel, o);
3022     }
3023     if(config.cls){
3024         this.addClass(config.cls);
3025     }
3026     this.constrain = config.constrain !== false;
3027     this.visibilityMode = Roo.Element.VISIBILITY;
3028     if(config.id){
3029         this.id = this.dom.id = config.id;
3030     }else{
3031         this.id = Roo.id(this.dom);
3032     }
3033     this.zindex = config.zindex || this.getZIndex();
3034     this.position("absolute", this.zindex);
3035     if(config.shadow){
3036         this.shadowOffset = config.shadowOffset || 4;
3037         this.shadow = new Roo.Shadow({
3038             offset : this.shadowOffset,
3039             mode : config.shadow
3040         });
3041     }else{
3042         this.shadowOffset = 0;
3043     }
3044     this.useShim = config.shim !== false && Roo.useShims;
3045     this.useDisplay = config.useDisplay;
3046     this.hide();
3047 };
3048
3049 var supr = Roo.Element.prototype;
3050
3051 // shims are shared among layer to keep from having 100 iframes
3052 var shims = [];
3053
3054 Roo.extend(Roo.Layer, Roo.Element, {
3055
3056     getZIndex : function(){
3057         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3058     },
3059
3060     getShim : function(){
3061         if(!this.useShim){
3062             return null;
3063         }
3064         if(this.shim){
3065             return this.shim;
3066         }
3067         var shim = shims.shift();
3068         if(!shim){
3069             shim = this.createShim();
3070             shim.enableDisplayMode('block');
3071             shim.dom.style.display = 'none';
3072             shim.dom.style.visibility = 'visible';
3073         }
3074         var pn = this.dom.parentNode;
3075         if(shim.dom.parentNode != pn){
3076             pn.insertBefore(shim.dom, this.dom);
3077         }
3078         shim.setStyle('z-index', this.getZIndex()-2);
3079         this.shim = shim;
3080         return shim;
3081     },
3082
3083     hideShim : function(){
3084         if(this.shim){
3085             this.shim.setDisplayed(false);
3086             shims.push(this.shim);
3087             delete this.shim;
3088         }
3089     },
3090
3091     disableShadow : function(){
3092         if(this.shadow){
3093             this.shadowDisabled = true;
3094             this.shadow.hide();
3095             this.lastShadowOffset = this.shadowOffset;
3096             this.shadowOffset = 0;
3097         }
3098     },
3099
3100     enableShadow : function(show){
3101         if(this.shadow){
3102             this.shadowDisabled = false;
3103             this.shadowOffset = this.lastShadowOffset;
3104             delete this.lastShadowOffset;
3105             if(show){
3106                 this.sync(true);
3107             }
3108         }
3109     },
3110
3111     // private
3112     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3113     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3114     sync : function(doShow){
3115         var sw = this.shadow;
3116         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3117             var sh = this.getShim();
3118
3119             var w = this.getWidth(),
3120                 h = this.getHeight();
3121
3122             var l = this.getLeft(true),
3123                 t = this.getTop(true);
3124
3125             if(sw && !this.shadowDisabled){
3126                 if(doShow && !sw.isVisible()){
3127                     sw.show(this);
3128                 }else{
3129                     sw.realign(l, t, w, h);
3130                 }
3131                 if(sh){
3132                     if(doShow){
3133                        sh.show();
3134                     }
3135                     // fit the shim behind the shadow, so it is shimmed too
3136                     var a = sw.adjusts, s = sh.dom.style;
3137                     s.left = (Math.min(l, l+a.l))+"px";
3138                     s.top = (Math.min(t, t+a.t))+"px";
3139                     s.width = (w+a.w)+"px";
3140                     s.height = (h+a.h)+"px";
3141                 }
3142             }else if(sh){
3143                 if(doShow){
3144                    sh.show();
3145                 }
3146                 sh.setSize(w, h);
3147                 sh.setLeftTop(l, t);
3148             }
3149             
3150         }
3151     },
3152
3153     // private
3154     destroy : function(){
3155         this.hideShim();
3156         if(this.shadow){
3157             this.shadow.hide();
3158         }
3159         this.removeAllListeners();
3160         var pn = this.dom.parentNode;
3161         if(pn){
3162             pn.removeChild(this.dom);
3163         }
3164         Roo.Element.uncache(this.id);
3165     },
3166
3167     remove : function(){
3168         this.destroy();
3169     },
3170
3171     // private
3172     beginUpdate : function(){
3173         this.updating = true;
3174     },
3175
3176     // private
3177     endUpdate : function(){
3178         this.updating = false;
3179         this.sync(true);
3180     },
3181
3182     // private
3183     hideUnders : function(negOffset){
3184         if(this.shadow){
3185             this.shadow.hide();
3186         }
3187         this.hideShim();
3188     },
3189
3190     // private
3191     constrainXY : function(){
3192         if(this.constrain){
3193             var vw = Roo.lib.Dom.getViewWidth(),
3194                 vh = Roo.lib.Dom.getViewHeight();
3195             var s = Roo.get(document).getScroll();
3196
3197             var xy = this.getXY();
3198             var x = xy[0], y = xy[1];   
3199             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3200             // only move it if it needs it
3201             var moved = false;
3202             // first validate right/bottom
3203             if((x + w) > vw+s.left){
3204                 x = vw - w - this.shadowOffset;
3205                 moved = true;
3206             }
3207             if((y + h) > vh+s.top){
3208                 y = vh - h - this.shadowOffset;
3209                 moved = true;
3210             }
3211             // then make sure top/left isn't negative
3212             if(x < s.left){
3213                 x = s.left;
3214                 moved = true;
3215             }
3216             if(y < s.top){
3217                 y = s.top;
3218                 moved = true;
3219             }
3220             if(moved){
3221                 if(this.avoidY){
3222                     var ay = this.avoidY;
3223                     if(y <= ay && (y+h) >= ay){
3224                         y = ay-h-5;   
3225                     }
3226                 }
3227                 xy = [x, y];
3228                 this.storeXY(xy);
3229                 supr.setXY.call(this, xy);
3230                 this.sync();
3231             }
3232         }
3233     },
3234
3235     isVisible : function(){
3236         return this.visible;    
3237     },
3238
3239     // private
3240     showAction : function(){
3241         this.visible = true; // track visibility to prevent getStyle calls
3242         if(this.useDisplay === true){
3243             this.setDisplayed("");
3244         }else if(this.lastXY){
3245             supr.setXY.call(this, this.lastXY);
3246         }else if(this.lastLT){
3247             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3248         }
3249     },
3250
3251     // private
3252     hideAction : function(){
3253         this.visible = false;
3254         if(this.useDisplay === true){
3255             this.setDisplayed(false);
3256         }else{
3257             this.setLeftTop(-10000,-10000);
3258         }
3259     },
3260
3261     // overridden Element method
3262     setVisible : function(v, a, d, c, e){
3263         if(v){
3264             this.showAction();
3265         }
3266         if(a && v){
3267             var cb = function(){
3268                 this.sync(true);
3269                 if(c){
3270                     c();
3271                 }
3272             }.createDelegate(this);
3273             supr.setVisible.call(this, true, true, d, cb, e);
3274         }else{
3275             if(!v){
3276                 this.hideUnders(true);
3277             }
3278             var cb = c;
3279             if(a){
3280                 cb = function(){
3281                     this.hideAction();
3282                     if(c){
3283                         c();
3284                     }
3285                 }.createDelegate(this);
3286             }
3287             supr.setVisible.call(this, v, a, d, cb, e);
3288             if(v){
3289                 this.sync(true);
3290             }else if(!a){
3291                 this.hideAction();
3292             }
3293         }
3294     },
3295
3296     storeXY : function(xy){
3297         delete this.lastLT;
3298         this.lastXY = xy;
3299     },
3300
3301     storeLeftTop : function(left, top){
3302         delete this.lastXY;
3303         this.lastLT = [left, top];
3304     },
3305
3306     // private
3307     beforeFx : function(){
3308         this.beforeAction();
3309         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3310     },
3311
3312     // private
3313     afterFx : function(){
3314         Roo.Layer.superclass.afterFx.apply(this, arguments);
3315         this.sync(this.isVisible());
3316     },
3317
3318     // private
3319     beforeAction : function(){
3320         if(!this.updating && this.shadow){
3321             this.shadow.hide();
3322         }
3323     },
3324
3325     // overridden Element method
3326     setLeft : function(left){
3327         this.storeLeftTop(left, this.getTop(true));
3328         supr.setLeft.apply(this, arguments);
3329         this.sync();
3330     },
3331
3332     setTop : function(top){
3333         this.storeLeftTop(this.getLeft(true), top);
3334         supr.setTop.apply(this, arguments);
3335         this.sync();
3336     },
3337
3338     setLeftTop : function(left, top){
3339         this.storeLeftTop(left, top);
3340         supr.setLeftTop.apply(this, arguments);
3341         this.sync();
3342     },
3343
3344     setXY : function(xy, a, d, c, e){
3345         this.fixDisplay();
3346         this.beforeAction();
3347         this.storeXY(xy);
3348         var cb = this.createCB(c);
3349         supr.setXY.call(this, xy, a, d, cb, e);
3350         if(!a){
3351             cb();
3352         }
3353     },
3354
3355     // private
3356     createCB : function(c){
3357         var el = this;
3358         return function(){
3359             el.constrainXY();
3360             el.sync(true);
3361             if(c){
3362                 c();
3363             }
3364         };
3365     },
3366
3367     // overridden Element method
3368     setX : function(x, a, d, c, e){
3369         this.setXY([x, this.getY()], a, d, c, e);
3370     },
3371
3372     // overridden Element method
3373     setY : function(y, a, d, c, e){
3374         this.setXY([this.getX(), y], a, d, c, e);
3375     },
3376
3377     // overridden Element method
3378     setSize : function(w, h, a, d, c, e){
3379         this.beforeAction();
3380         var cb = this.createCB(c);
3381         supr.setSize.call(this, w, h, a, d, cb, e);
3382         if(!a){
3383             cb();
3384         }
3385     },
3386
3387     // overridden Element method
3388     setWidth : function(w, a, d, c, e){
3389         this.beforeAction();
3390         var cb = this.createCB(c);
3391         supr.setWidth.call(this, w, a, d, cb, e);
3392         if(!a){
3393             cb();
3394         }
3395     },
3396
3397     // overridden Element method
3398     setHeight : function(h, a, d, c, e){
3399         this.beforeAction();
3400         var cb = this.createCB(c);
3401         supr.setHeight.call(this, h, a, d, cb, e);
3402         if(!a){
3403             cb();
3404         }
3405     },
3406
3407     // overridden Element method
3408     setBounds : function(x, y, w, h, a, d, c, e){
3409         this.beforeAction();
3410         var cb = this.createCB(c);
3411         if(!a){
3412             this.storeXY([x, y]);
3413             supr.setXY.call(this, [x, y]);
3414             supr.setSize.call(this, w, h, a, d, cb, e);
3415             cb();
3416         }else{
3417             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3418         }
3419         return this;
3420     },
3421     
3422     /**
3423      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3424      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3425      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3426      * @param {Number} zindex The new z-index to set
3427      * @return {this} The Layer
3428      */
3429     setZIndex : function(zindex){
3430         this.zindex = zindex;
3431         this.setStyle("z-index", zindex + 2);
3432         if(this.shadow){
3433             this.shadow.setZIndex(zindex + 1);
3434         }
3435         if(this.shim){
3436             this.shim.setStyle("z-index", zindex);
3437         }
3438     }
3439 });
3440 })();/*
3441  * Based on:
3442  * Ext JS Library 1.1.1
3443  * Copyright(c) 2006-2007, Ext JS, LLC.
3444  *
3445  * Originally Released Under LGPL - original licence link has changed is not relivant.
3446  *
3447  * Fork - LGPL
3448  * <script type="text/javascript">
3449  */
3450
3451
3452 /**
3453  * @class Roo.Shadow
3454  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3455  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3456  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3457  * @constructor
3458  * Create a new Shadow
3459  * @param {Object} config The config object
3460  */
3461 Roo.Shadow = function(config){
3462     Roo.apply(this, config);
3463     if(typeof this.mode != "string"){
3464         this.mode = this.defaultMode;
3465     }
3466     var o = this.offset, a = {h: 0};
3467     var rad = Math.floor(this.offset/2);
3468     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3469         case "drop":
3470             a.w = 0;
3471             a.l = a.t = o;
3472             a.t -= 1;
3473             if(Roo.isIE){
3474                 a.l -= this.offset + rad;
3475                 a.t -= this.offset + rad;
3476                 a.w -= rad;
3477                 a.h -= rad;
3478                 a.t += 1;
3479             }
3480         break;
3481         case "sides":
3482             a.w = (o*2);
3483             a.l = -o;
3484             a.t = o-1;
3485             if(Roo.isIE){
3486                 a.l -= (this.offset - rad);
3487                 a.t -= this.offset + rad;
3488                 a.l += 1;
3489                 a.w -= (this.offset - rad)*2;
3490                 a.w -= rad + 1;
3491                 a.h -= 1;
3492             }
3493         break;
3494         case "frame":
3495             a.w = a.h = (o*2);
3496             a.l = a.t = -o;
3497             a.t += 1;
3498             a.h -= 2;
3499             if(Roo.isIE){
3500                 a.l -= (this.offset - rad);
3501                 a.t -= (this.offset - rad);
3502                 a.l += 1;
3503                 a.w -= (this.offset + rad + 1);
3504                 a.h -= (this.offset + rad);
3505                 a.h += 1;
3506             }
3507         break;
3508     };
3509
3510     this.adjusts = a;
3511 };
3512
3513 Roo.Shadow.prototype = {
3514     /**
3515      * @cfg {String} mode
3516      * The shadow display mode.  Supports the following options:<br />
3517      * sides: Shadow displays on both sides and bottom only<br />
3518      * frame: Shadow displays equally on all four sides<br />
3519      * drop: Traditional bottom-right drop shadow (default)
3520      */
3521     /**
3522      * @cfg {String} offset
3523      * The number of pixels to offset the shadow from the element (defaults to 4)
3524      */
3525     offset: 4,
3526
3527     // private
3528     defaultMode: "drop",
3529
3530     /**
3531      * Displays the shadow under the target element
3532      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3533      */
3534     show : function(target){
3535         target = Roo.get(target);
3536         if(!this.el){
3537             this.el = Roo.Shadow.Pool.pull();
3538             if(this.el.dom.nextSibling != target.dom){
3539                 this.el.insertBefore(target);
3540             }
3541         }
3542         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3543         if(Roo.isIE){
3544             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3545         }
3546         this.realign(
3547             target.getLeft(true),
3548             target.getTop(true),
3549             target.getWidth(),
3550             target.getHeight()
3551         );
3552         this.el.dom.style.display = "block";
3553     },
3554
3555     /**
3556      * Returns true if the shadow is visible, else false
3557      */
3558     isVisible : function(){
3559         return this.el ? true : false;  
3560     },
3561
3562     /**
3563      * Direct alignment when values are already available. Show must be called at least once before
3564      * calling this method to ensure it is initialized.
3565      * @param {Number} left The target element left position
3566      * @param {Number} top The target element top position
3567      * @param {Number} width The target element width
3568      * @param {Number} height The target element height
3569      */
3570     realign : function(l, t, w, h){
3571         if(!this.el){
3572             return;
3573         }
3574         var a = this.adjusts, d = this.el.dom, s = d.style;
3575         var iea = 0;
3576         s.left = (l+a.l)+"px";
3577         s.top = (t+a.t)+"px";
3578         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3579  
3580         if(s.width != sws || s.height != shs){
3581             s.width = sws;
3582             s.height = shs;
3583             if(!Roo.isIE){
3584                 var cn = d.childNodes;
3585                 var sww = Math.max(0, (sw-12))+"px";
3586                 cn[0].childNodes[1].style.width = sww;
3587                 cn[1].childNodes[1].style.width = sww;
3588                 cn[2].childNodes[1].style.width = sww;
3589                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3590             }
3591         }
3592     },
3593
3594     /**
3595      * Hides this shadow
3596      */
3597     hide : function(){
3598         if(this.el){
3599             this.el.dom.style.display = "none";
3600             Roo.Shadow.Pool.push(this.el);
3601             delete this.el;
3602         }
3603     },
3604
3605     /**
3606      * Adjust the z-index of this shadow
3607      * @param {Number} zindex The new z-index
3608      */
3609     setZIndex : function(z){
3610         this.zIndex = z;
3611         if(this.el){
3612             this.el.setStyle("z-index", z);
3613         }
3614     }
3615 };
3616
3617 // Private utility class that manages the internal Shadow cache
3618 Roo.Shadow.Pool = function(){
3619     var p = [];
3620     var markup = Roo.isIE ?
3621                  '<div class="x-ie-shadow"></div>' :
3622                  '<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>';
3623     return {
3624         pull : function(){
3625             var sh = p.shift();
3626             if(!sh){
3627                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3628                 sh.autoBoxAdjust = false;
3629             }
3630             return sh;
3631         },
3632
3633         push : function(sh){
3634             p.push(sh);
3635         }
3636     };
3637 }();/*
3638  * Based on:
3639  * Ext JS Library 1.1.1
3640  * Copyright(c) 2006-2007, Ext JS, LLC.
3641  *
3642  * Originally Released Under LGPL - original licence link has changed is not relivant.
3643  *
3644  * Fork - LGPL
3645  * <script type="text/javascript">
3646  */
3647
3648
3649 /**
3650  * @class Roo.SplitBar
3651  * @extends Roo.util.Observable
3652  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3653  * <br><br>
3654  * Usage:
3655  * <pre><code>
3656 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3657                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3658 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3659 split.minSize = 100;
3660 split.maxSize = 600;
3661 split.animate = true;
3662 split.on('moved', splitterMoved);
3663 </code></pre>
3664  * @constructor
3665  * Create a new SplitBar
3666  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3667  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3668  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3669  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3670                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3671                         position of the SplitBar).
3672  */
3673 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3674     
3675     /** @private */
3676     this.el = Roo.get(dragElement, true);
3677     this.el.dom.unselectable = "on";
3678     /** @private */
3679     this.resizingEl = Roo.get(resizingElement, true);
3680
3681     /**
3682      * @private
3683      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3684      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3685      * @type Number
3686      */
3687     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3688     
3689     /**
3690      * The minimum size of the resizing element. (Defaults to 0)
3691      * @type Number
3692      */
3693     this.minSize = 0;
3694     
3695     /**
3696      * The maximum size of the resizing element. (Defaults to 2000)
3697      * @type Number
3698      */
3699     this.maxSize = 2000;
3700     
3701     /**
3702      * Whether to animate the transition to the new size
3703      * @type Boolean
3704      */
3705     this.animate = false;
3706     
3707     /**
3708      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3709      * @type Boolean
3710      */
3711     this.useShim = false;
3712     
3713     /** @private */
3714     this.shim = null;
3715     
3716     if(!existingProxy){
3717         /** @private */
3718         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3719     }else{
3720         this.proxy = Roo.get(existingProxy).dom;
3721     }
3722     /** @private */
3723     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3724     
3725     /** @private */
3726     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3727     
3728     /** @private */
3729     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3730     
3731     /** @private */
3732     this.dragSpecs = {};
3733     
3734     /**
3735      * @private The adapter to use to positon and resize elements
3736      */
3737     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3738     this.adapter.init(this);
3739     
3740     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3741         /** @private */
3742         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3743         this.el.addClass("x-splitbar-h");
3744     }else{
3745         /** @private */
3746         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3747         this.el.addClass("x-splitbar-v");
3748     }
3749     
3750     this.addEvents({
3751         /**
3752          * @event resize
3753          * Fires when the splitter is moved (alias for {@link #event-moved})
3754          * @param {Roo.SplitBar} this
3755          * @param {Number} newSize the new width or height
3756          */
3757         "resize" : true,
3758         /**
3759          * @event moved
3760          * Fires when the splitter is moved
3761          * @param {Roo.SplitBar} this
3762          * @param {Number} newSize the new width or height
3763          */
3764         "moved" : true,
3765         /**
3766          * @event beforeresize
3767          * Fires before the splitter is dragged
3768          * @param {Roo.SplitBar} this
3769          */
3770         "beforeresize" : true,
3771
3772         "beforeapply" : true
3773     });
3774
3775     Roo.util.Observable.call(this);
3776 };
3777
3778 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3779     onStartProxyDrag : function(x, y){
3780         this.fireEvent("beforeresize", this);
3781         if(!this.overlay){
3782             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3783             o.unselectable();
3784             o.enableDisplayMode("block");
3785             // all splitbars share the same overlay
3786             Roo.SplitBar.prototype.overlay = o;
3787         }
3788         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3789         this.overlay.show();
3790         Roo.get(this.proxy).setDisplayed("block");
3791         var size = this.adapter.getElementSize(this);
3792         this.activeMinSize = this.getMinimumSize();;
3793         this.activeMaxSize = this.getMaximumSize();;
3794         var c1 = size - this.activeMinSize;
3795         var c2 = Math.max(this.activeMaxSize - size, 0);
3796         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3797             this.dd.resetConstraints();
3798             this.dd.setXConstraint(
3799                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3800                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3801             );
3802             this.dd.setYConstraint(0, 0);
3803         }else{
3804             this.dd.resetConstraints();
3805             this.dd.setXConstraint(0, 0);
3806             this.dd.setYConstraint(
3807                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3808                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3809             );
3810          }
3811         this.dragSpecs.startSize = size;
3812         this.dragSpecs.startPoint = [x, y];
3813         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3814     },
3815     
3816     /** 
3817      * @private Called after the drag operation by the DDProxy
3818      */
3819     onEndProxyDrag : function(e){
3820         Roo.get(this.proxy).setDisplayed(false);
3821         var endPoint = Roo.lib.Event.getXY(e);
3822         if(this.overlay){
3823             this.overlay.hide();
3824         }
3825         var newSize;
3826         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3827             newSize = this.dragSpecs.startSize + 
3828                 (this.placement == Roo.SplitBar.LEFT ?
3829                     endPoint[0] - this.dragSpecs.startPoint[0] :
3830                     this.dragSpecs.startPoint[0] - endPoint[0]
3831                 );
3832         }else{
3833             newSize = this.dragSpecs.startSize + 
3834                 (this.placement == Roo.SplitBar.TOP ?
3835                     endPoint[1] - this.dragSpecs.startPoint[1] :
3836                     this.dragSpecs.startPoint[1] - endPoint[1]
3837                 );
3838         }
3839         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3840         if(newSize != this.dragSpecs.startSize){
3841             if(this.fireEvent('beforeapply', this, newSize) !== false){
3842                 this.adapter.setElementSize(this, newSize);
3843                 this.fireEvent("moved", this, newSize);
3844                 this.fireEvent("resize", this, newSize);
3845             }
3846         }
3847     },
3848     
3849     /**
3850      * Get the adapter this SplitBar uses
3851      * @return The adapter object
3852      */
3853     getAdapter : function(){
3854         return this.adapter;
3855     },
3856     
3857     /**
3858      * Set the adapter this SplitBar uses
3859      * @param {Object} adapter A SplitBar adapter object
3860      */
3861     setAdapter : function(adapter){
3862         this.adapter = adapter;
3863         this.adapter.init(this);
3864     },
3865     
3866     /**
3867      * Gets the minimum size for the resizing element
3868      * @return {Number} The minimum size
3869      */
3870     getMinimumSize : function(){
3871         return this.minSize;
3872     },
3873     
3874     /**
3875      * Sets the minimum size for the resizing element
3876      * @param {Number} minSize The minimum size
3877      */
3878     setMinimumSize : function(minSize){
3879         this.minSize = minSize;
3880     },
3881     
3882     /**
3883      * Gets the maximum size for the resizing element
3884      * @return {Number} The maximum size
3885      */
3886     getMaximumSize : function(){
3887         return this.maxSize;
3888     },
3889     
3890     /**
3891      * Sets the maximum size for the resizing element
3892      * @param {Number} maxSize The maximum size
3893      */
3894     setMaximumSize : function(maxSize){
3895         this.maxSize = maxSize;
3896     },
3897     
3898     /**
3899      * Sets the initialize size for the resizing element
3900      * @param {Number} size The initial size
3901      */
3902     setCurrentSize : function(size){
3903         var oldAnimate = this.animate;
3904         this.animate = false;
3905         this.adapter.setElementSize(this, size);
3906         this.animate = oldAnimate;
3907     },
3908     
3909     /**
3910      * Destroy this splitbar. 
3911      * @param {Boolean} removeEl True to remove the element
3912      */
3913     destroy : function(removeEl){
3914         if(this.shim){
3915             this.shim.remove();
3916         }
3917         this.dd.unreg();
3918         this.proxy.parentNode.removeChild(this.proxy);
3919         if(removeEl){
3920             this.el.remove();
3921         }
3922     }
3923 });
3924
3925 /**
3926  * @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.
3927  */
3928 Roo.SplitBar.createProxy = function(dir){
3929     var proxy = new Roo.Element(document.createElement("div"));
3930     proxy.unselectable();
3931     var cls = 'x-splitbar-proxy';
3932     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3933     document.body.appendChild(proxy.dom);
3934     return proxy.dom;
3935 };
3936
3937 /** 
3938  * @class Roo.SplitBar.BasicLayoutAdapter
3939  * Default Adapter. It assumes the splitter and resizing element are not positioned
3940  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3941  */
3942 Roo.SplitBar.BasicLayoutAdapter = function(){
3943 };
3944
3945 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3946     // do nothing for now
3947     init : function(s){
3948     
3949     },
3950     /**
3951      * Called before drag operations to get the current size of the resizing element. 
3952      * @param {Roo.SplitBar} s The SplitBar using this adapter
3953      */
3954      getElementSize : function(s){
3955         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3956             return s.resizingEl.getWidth();
3957         }else{
3958             return s.resizingEl.getHeight();
3959         }
3960     },
3961     
3962     /**
3963      * Called after drag operations to set the size of the resizing element.
3964      * @param {Roo.SplitBar} s The SplitBar using this adapter
3965      * @param {Number} newSize The new size to set
3966      * @param {Function} onComplete A function to be invoked when resizing is complete
3967      */
3968     setElementSize : function(s, newSize, onComplete){
3969         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3970             if(!s.animate){
3971                 s.resizingEl.setWidth(newSize);
3972                 if(onComplete){
3973                     onComplete(s, newSize);
3974                 }
3975             }else{
3976                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3977             }
3978         }else{
3979             
3980             if(!s.animate){
3981                 s.resizingEl.setHeight(newSize);
3982                 if(onComplete){
3983                     onComplete(s, newSize);
3984                 }
3985             }else{
3986                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3987             }
3988         }
3989     }
3990 };
3991
3992 /** 
3993  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3994  * @extends Roo.SplitBar.BasicLayoutAdapter
3995  * Adapter that  moves the splitter element to align with the resized sizing element. 
3996  * Used with an absolute positioned SplitBar.
3997  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3998  * document.body, make sure you assign an id to the body element.
3999  */
4000 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
4001     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
4002     this.container = Roo.get(container);
4003 };
4004
4005 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
4006     init : function(s){
4007         this.basic.init(s);
4008     },
4009     
4010     getElementSize : function(s){
4011         return this.basic.getElementSize(s);
4012     },
4013     
4014     setElementSize : function(s, newSize, onComplete){
4015         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
4016     },
4017     
4018     moveSplitter : function(s){
4019         var yes = Roo.SplitBar;
4020         switch(s.placement){
4021             case yes.LEFT:
4022                 s.el.setX(s.resizingEl.getRight());
4023                 break;
4024             case yes.RIGHT:
4025                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4026                 break;
4027             case yes.TOP:
4028                 s.el.setY(s.resizingEl.getBottom());
4029                 break;
4030             case yes.BOTTOM:
4031                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4032                 break;
4033         }
4034     }
4035 };
4036
4037 /**
4038  * Orientation constant - Create a vertical SplitBar
4039  * @static
4040  * @type Number
4041  */
4042 Roo.SplitBar.VERTICAL = 1;
4043
4044 /**
4045  * Orientation constant - Create a horizontal SplitBar
4046  * @static
4047  * @type Number
4048  */
4049 Roo.SplitBar.HORIZONTAL = 2;
4050
4051 /**
4052  * Placement constant - The resizing element is to the left of the splitter element
4053  * @static
4054  * @type Number
4055  */
4056 Roo.SplitBar.LEFT = 1;
4057
4058 /**
4059  * Placement constant - The resizing element is to the right of the splitter element
4060  * @static
4061  * @type Number
4062  */
4063 Roo.SplitBar.RIGHT = 2;
4064
4065 /**
4066  * Placement constant - The resizing element is positioned above the splitter element
4067  * @static
4068  * @type Number
4069  */
4070 Roo.SplitBar.TOP = 3;
4071
4072 /**
4073  * Placement constant - The resizing element is positioned under splitter element
4074  * @static
4075  * @type Number
4076  */
4077 Roo.SplitBar.BOTTOM = 4;
4078 /*
4079  * Based on:
4080  * Ext JS Library 1.1.1
4081  * Copyright(c) 2006-2007, Ext JS, LLC.
4082  *
4083  * Originally Released Under LGPL - original licence link has changed is not relivant.
4084  *
4085  * Fork - LGPL
4086  * <script type="text/javascript">
4087  */
4088
4089 /**
4090  * @class Roo.View
4091  * @extends Roo.util.Observable
4092  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4093  * This class also supports single and multi selection modes. <br>
4094  * Create a data model bound view:
4095  <pre><code>
4096  var store = new Roo.data.Store(...);
4097
4098  var view = new Roo.View({
4099     el : "my-element",
4100     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4101  
4102     singleSelect: true,
4103     selectedClass: "ydataview-selected",
4104     store: store
4105  });
4106
4107  // listen for node click?
4108  view.on("click", function(vw, index, node, e){
4109  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4110  });
4111
4112  // load XML data
4113  dataModel.load("foobar.xml");
4114  </code></pre>
4115  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4116  * <br><br>
4117  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4118  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4119  * 
4120  * Note: old style constructor is still suported (container, template, config)
4121  * 
4122  * @constructor
4123  * Create a new View
4124  * @param {Object} config The config object
4125  * 
4126  */
4127 Roo.View = function(config, depreciated_tpl, depreciated_config){
4128     
4129     this.parent = false;
4130     
4131     if (typeof(depreciated_tpl) == 'undefined') {
4132         // new way.. - universal constructor.
4133         Roo.apply(this, config);
4134         this.el  = Roo.get(this.el);
4135     } else {
4136         // old format..
4137         this.el  = Roo.get(config);
4138         this.tpl = depreciated_tpl;
4139         Roo.apply(this, depreciated_config);
4140     }
4141     this.wrapEl  = this.el.wrap().wrap();
4142     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4143     
4144     
4145     if(typeof(this.tpl) == "string"){
4146         this.tpl = new Roo.Template(this.tpl);
4147     } else {
4148         // support xtype ctors..
4149         this.tpl = new Roo.factory(this.tpl, Roo);
4150     }
4151     
4152     
4153     this.tpl.compile();
4154     
4155     /** @private */
4156     this.addEvents({
4157         /**
4158          * @event beforeclick
4159          * Fires before a click is processed. Returns false to cancel the default action.
4160          * @param {Roo.View} this
4161          * @param {Number} index The index of the target node
4162          * @param {HTMLElement} node The target node
4163          * @param {Roo.EventObject} e The raw event object
4164          */
4165             "beforeclick" : true,
4166         /**
4167          * @event click
4168          * Fires when a template node is clicked.
4169          * @param {Roo.View} this
4170          * @param {Number} index The index of the target node
4171          * @param {HTMLElement} node The target node
4172          * @param {Roo.EventObject} e The raw event object
4173          */
4174             "click" : true,
4175         /**
4176          * @event dblclick
4177          * Fires when a template node is double clicked.
4178          * @param {Roo.View} this
4179          * @param {Number} index The index of the target node
4180          * @param {HTMLElement} node The target node
4181          * @param {Roo.EventObject} e The raw event object
4182          */
4183             "dblclick" : true,
4184         /**
4185          * @event contextmenu
4186          * Fires when a template node is right clicked.
4187          * @param {Roo.View} this
4188          * @param {Number} index The index of the target node
4189          * @param {HTMLElement} node The target node
4190          * @param {Roo.EventObject} e The raw event object
4191          */
4192             "contextmenu" : true,
4193         /**
4194          * @event selectionchange
4195          * Fires when the selected nodes change.
4196          * @param {Roo.View} this
4197          * @param {Array} selections Array of the selected nodes
4198          */
4199             "selectionchange" : true,
4200     
4201         /**
4202          * @event beforeselect
4203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4204          * @param {Roo.View} this
4205          * @param {HTMLElement} node The node to be selected
4206          * @param {Array} selections Array of currently selected nodes
4207          */
4208             "beforeselect" : true,
4209         /**
4210          * @event preparedata
4211          * Fires on every row to render, to allow you to change the data.
4212          * @param {Roo.View} this
4213          * @param {Object} data to be rendered (change this)
4214          */
4215           "preparedata" : true
4216           
4217           
4218         });
4219
4220
4221
4222     this.el.on({
4223         "click": this.onClick,
4224         "dblclick": this.onDblClick,
4225         "contextmenu": this.onContextMenu,
4226         scope:this
4227     });
4228
4229     this.selections = [];
4230     this.nodes = [];
4231     this.cmp = new Roo.CompositeElementLite([]);
4232     if(this.store){
4233         this.store = Roo.factory(this.store, Roo.data);
4234         this.setStore(this.store, true);
4235     }
4236     
4237     if ( this.footer && this.footer.xtype) {
4238            
4239          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4240         
4241         this.footer.dataSource = this.store;
4242         this.footer.container = fctr;
4243         this.footer = Roo.factory(this.footer, Roo);
4244         fctr.insertFirst(this.el);
4245         
4246         // this is a bit insane - as the paging toolbar seems to detach the el..
4247 //        dom.parentNode.parentNode.parentNode
4248          // they get detached?
4249     }
4250     
4251     
4252     Roo.View.superclass.constructor.call(this);
4253     
4254     
4255 };
4256
4257 Roo.extend(Roo.View, Roo.util.Observable, {
4258     
4259      /**
4260      * @cfg {Roo.data.Store} store Data store to load data from.
4261      */
4262     store : false,
4263     
4264     /**
4265      * @cfg {String|Roo.Element} el The container element.
4266      */
4267     el : '',
4268     
4269     /**
4270      * @cfg {String|Roo.Template} tpl The template used by this View 
4271      */
4272     tpl : false,
4273     /**
4274      * @cfg {String} dataName the named area of the template to use as the data area
4275      *                          Works with domtemplates roo-name="name"
4276      */
4277     dataName: false,
4278     /**
4279      * @cfg {String} selectedClass The css class to add to selected nodes
4280      */
4281     selectedClass : "x-view-selected",
4282      /**
4283      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4284      */
4285     emptyText : "",
4286     
4287     /**
4288      * @cfg {String} text to display on mask (default Loading)
4289      */
4290     mask : false,
4291     /**
4292      * @cfg {Boolean} multiSelect Allow multiple selection
4293      */
4294     multiSelect : false,
4295     /**
4296      * @cfg {Boolean} singleSelect Allow single selection
4297      */
4298     singleSelect:  false,
4299     
4300     /**
4301      * @cfg {Boolean} toggleSelect - selecting 
4302      */
4303     toggleSelect : false,
4304     
4305     /**
4306      * @cfg {Boolean} tickable - selecting 
4307      */
4308     tickable : false,
4309     
4310     /**
4311      * Returns the element this view is bound to.
4312      * @return {Roo.Element}
4313      */
4314     getEl : function(){
4315         return this.wrapEl;
4316     },
4317     
4318     
4319
4320     /**
4321      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4322      */
4323     refresh : function(){
4324         //Roo.log('refresh');
4325         var t = this.tpl;
4326         
4327         // if we are using something like 'domtemplate', then
4328         // the what gets used is:
4329         // t.applySubtemplate(NAME, data, wrapping data..)
4330         // the outer template then get' applied with
4331         //     the store 'extra data'
4332         // and the body get's added to the
4333         //      roo-name="data" node?
4334         //      <span class='roo-tpl-{name}'></span> ?????
4335         
4336         
4337         
4338         this.clearSelections();
4339         this.el.update("");
4340         var html = [];
4341         var records = this.store.getRange();
4342         if(records.length < 1) {
4343             
4344             // is this valid??  = should it render a template??
4345             
4346             this.el.update(this.emptyText);
4347             return;
4348         }
4349         var el = this.el;
4350         if (this.dataName) {
4351             this.el.update(t.apply(this.store.meta)); //????
4352             el = this.el.child('.roo-tpl-' + this.dataName);
4353         }
4354         
4355         for(var i = 0, len = records.length; i < len; i++){
4356             var data = this.prepareData(records[i].data, i, records[i]);
4357             this.fireEvent("preparedata", this, data, i, records[i]);
4358             
4359             var d = Roo.apply({}, data);
4360             
4361             if(this.tickable){
4362                 Roo.apply(d, {'roo-id' : Roo.id()});
4363                 
4364                 var _this = this;
4365             
4366                 Roo.each(this.parent.item, function(item){
4367                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4368                         return;
4369                     }
4370                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4371                 });
4372             }
4373             
4374             html[html.length] = Roo.util.Format.trim(
4375                 this.dataName ?
4376                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4377                     t.apply(d)
4378             );
4379         }
4380         
4381         
4382         
4383         el.update(html.join(""));
4384         this.nodes = el.dom.childNodes;
4385         this.updateIndexes(0);
4386     },
4387     
4388
4389     /**
4390      * Function to override to reformat the data that is sent to
4391      * the template for each node.
4392      * DEPRICATED - use the preparedata event handler.
4393      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4394      * a JSON object for an UpdateManager bound view).
4395      */
4396     prepareData : function(data, index, record)
4397     {
4398         this.fireEvent("preparedata", this, data, index, record);
4399         return data;
4400     },
4401
4402     onUpdate : function(ds, record){
4403         // Roo.log('on update');   
4404         this.clearSelections();
4405         var index = this.store.indexOf(record);
4406         var n = this.nodes[index];
4407         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4408         n.parentNode.removeChild(n);
4409         this.updateIndexes(index, index);
4410     },
4411
4412     
4413     
4414 // --------- FIXME     
4415     onAdd : function(ds, records, index)
4416     {
4417         //Roo.log(['on Add', ds, records, index] );        
4418         this.clearSelections();
4419         if(this.nodes.length == 0){
4420             this.refresh();
4421             return;
4422         }
4423         var n = this.nodes[index];
4424         for(var i = 0, len = records.length; i < len; i++){
4425             var d = this.prepareData(records[i].data, i, records[i]);
4426             if(n){
4427                 this.tpl.insertBefore(n, d);
4428             }else{
4429                 
4430                 this.tpl.append(this.el, d);
4431             }
4432         }
4433         this.updateIndexes(index);
4434     },
4435
4436     onRemove : function(ds, record, index){
4437        // Roo.log('onRemove');
4438         this.clearSelections();
4439         var el = this.dataName  ?
4440             this.el.child('.roo-tpl-' + this.dataName) :
4441             this.el; 
4442         
4443         el.dom.removeChild(this.nodes[index]);
4444         this.updateIndexes(index);
4445     },
4446
4447     /**
4448      * Refresh an individual node.
4449      * @param {Number} index
4450      */
4451     refreshNode : function(index){
4452         this.onUpdate(this.store, this.store.getAt(index));
4453     },
4454
4455     updateIndexes : function(startIndex, endIndex){
4456         var ns = this.nodes;
4457         startIndex = startIndex || 0;
4458         endIndex = endIndex || ns.length - 1;
4459         for(var i = startIndex; i <= endIndex; i++){
4460             ns[i].nodeIndex = i;
4461         }
4462     },
4463
4464     /**
4465      * Changes the data store this view uses and refresh the view.
4466      * @param {Store} store
4467      */
4468     setStore : function(store, initial){
4469         if(!initial && this.store){
4470             this.store.un("datachanged", this.refresh);
4471             this.store.un("add", this.onAdd);
4472             this.store.un("remove", this.onRemove);
4473             this.store.un("update", this.onUpdate);
4474             this.store.un("clear", this.refresh);
4475             this.store.un("beforeload", this.onBeforeLoad);
4476             this.store.un("load", this.onLoad);
4477             this.store.un("loadexception", this.onLoad);
4478         }
4479         if(store){
4480           
4481             store.on("datachanged", this.refresh, this);
4482             store.on("add", this.onAdd, this);
4483             store.on("remove", this.onRemove, this);
4484             store.on("update", this.onUpdate, this);
4485             store.on("clear", this.refresh, this);
4486             store.on("beforeload", this.onBeforeLoad, this);
4487             store.on("load", this.onLoad, this);
4488             store.on("loadexception", this.onLoad, this);
4489         }
4490         
4491         if(store){
4492             this.refresh();
4493         }
4494     },
4495     /**
4496      * onbeforeLoad - masks the loading area.
4497      *
4498      */
4499     onBeforeLoad : function(store,opts)
4500     {
4501          //Roo.log('onBeforeLoad');   
4502         if (!opts.add) {
4503             this.el.update("");
4504         }
4505         this.el.mask(this.mask ? this.mask : "Loading" ); 
4506     },
4507     onLoad : function ()
4508     {
4509         this.el.unmask();
4510     },
4511     
4512
4513     /**
4514      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4515      * @param {HTMLElement} node
4516      * @return {HTMLElement} The template node
4517      */
4518     findItemFromChild : function(node){
4519         var el = this.dataName  ?
4520             this.el.child('.roo-tpl-' + this.dataName,true) :
4521             this.el.dom; 
4522         
4523         if(!node || node.parentNode == el){
4524                     return node;
4525             }
4526             var p = node.parentNode;
4527             while(p && p != el){
4528             if(p.parentNode == el){
4529                 return p;
4530             }
4531             p = p.parentNode;
4532         }
4533             return null;
4534     },
4535
4536     /** @ignore */
4537     onClick : function(e){
4538         var item = this.findItemFromChild(e.getTarget());
4539         if(item){
4540             var index = this.indexOf(item);
4541             if(this.onItemClick(item, index, e) !== false){
4542                 this.fireEvent("click", this, index, item, e);
4543             }
4544         }else{
4545             this.clearSelections();
4546         }
4547     },
4548
4549     /** @ignore */
4550     onContextMenu : function(e){
4551         var item = this.findItemFromChild(e.getTarget());
4552         if(item){
4553             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4554         }
4555     },
4556
4557     /** @ignore */
4558     onDblClick : function(e){
4559         var item = this.findItemFromChild(e.getTarget());
4560         if(item){
4561             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4562         }
4563     },
4564
4565     onItemClick : function(item, index, e)
4566     {
4567         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4568             return false;
4569         }
4570         if (this.toggleSelect) {
4571             var m = this.isSelected(item) ? 'unselect' : 'select';
4572             //Roo.log(m);
4573             var _t = this;
4574             _t[m](item, true, false);
4575             return true;
4576         }
4577         if(this.multiSelect || this.singleSelect){
4578             if(this.multiSelect && e.shiftKey && this.lastSelection){
4579                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4580             }else{
4581                 this.select(item, this.multiSelect && e.ctrlKey);
4582                 this.lastSelection = item;
4583             }
4584             
4585             if(!this.tickable){
4586                 e.preventDefault();
4587             }
4588             
4589         }
4590         return true;
4591     },
4592
4593     /**
4594      * Get the number of selected nodes.
4595      * @return {Number}
4596      */
4597     getSelectionCount : function(){
4598         return this.selections.length;
4599     },
4600
4601     /**
4602      * Get the currently selected nodes.
4603      * @return {Array} An array of HTMLElements
4604      */
4605     getSelectedNodes : function(){
4606         return this.selections;
4607     },
4608
4609     /**
4610      * Get the indexes of the selected nodes.
4611      * @return {Array}
4612      */
4613     getSelectedIndexes : function(){
4614         var indexes = [], s = this.selections;
4615         for(var i = 0, len = s.length; i < len; i++){
4616             indexes.push(s[i].nodeIndex);
4617         }
4618         return indexes;
4619     },
4620
4621     /**
4622      * Clear all selections
4623      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4624      */
4625     clearSelections : function(suppressEvent){
4626         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4627             this.cmp.elements = this.selections;
4628             this.cmp.removeClass(this.selectedClass);
4629             this.selections = [];
4630             if(!suppressEvent){
4631                 this.fireEvent("selectionchange", this, this.selections);
4632             }
4633         }
4634     },
4635
4636     /**
4637      * Returns true if the passed node is selected
4638      * @param {HTMLElement/Number} node The node or node index
4639      * @return {Boolean}
4640      */
4641     isSelected : function(node){
4642         var s = this.selections;
4643         if(s.length < 1){
4644             return false;
4645         }
4646         node = this.getNode(node);
4647         return s.indexOf(node) !== -1;
4648     },
4649
4650     /**
4651      * Selects nodes.
4652      * @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
4653      * @param {Boolean} keepExisting (optional) true to keep existing selections
4654      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4655      */
4656     select : function(nodeInfo, keepExisting, suppressEvent){
4657         if(nodeInfo instanceof Array){
4658             if(!keepExisting){
4659                 this.clearSelections(true);
4660             }
4661             for(var i = 0, len = nodeInfo.length; i < len; i++){
4662                 this.select(nodeInfo[i], true, true);
4663             }
4664             return;
4665         } 
4666         var node = this.getNode(nodeInfo);
4667         if(!node || this.isSelected(node)){
4668             return; // already selected.
4669         }
4670         if(!keepExisting){
4671             this.clearSelections(true);
4672         }
4673         
4674         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4675             Roo.fly(node).addClass(this.selectedClass);
4676             this.selections.push(node);
4677             if(!suppressEvent){
4678                 this.fireEvent("selectionchange", this, this.selections);
4679             }
4680         }
4681         
4682         
4683     },
4684       /**
4685      * Unselects nodes.
4686      * @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
4687      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4689      */
4690     unselect : function(nodeInfo, keepExisting, suppressEvent)
4691     {
4692         if(nodeInfo instanceof Array){
4693             Roo.each(this.selections, function(s) {
4694                 this.unselect(s, nodeInfo);
4695             }, this);
4696             return;
4697         }
4698         var node = this.getNode(nodeInfo);
4699         if(!node || !this.isSelected(node)){
4700             //Roo.log("not selected");
4701             return; // not selected.
4702         }
4703         // fireevent???
4704         var ns = [];
4705         Roo.each(this.selections, function(s) {
4706             if (s == node ) {
4707                 Roo.fly(node).removeClass(this.selectedClass);
4708
4709                 return;
4710             }
4711             ns.push(s);
4712         },this);
4713         
4714         this.selections= ns;
4715         this.fireEvent("selectionchange", this, this.selections);
4716     },
4717
4718     /**
4719      * Gets a template node.
4720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4721      * @return {HTMLElement} The node or null if it wasn't found
4722      */
4723     getNode : function(nodeInfo){
4724         if(typeof nodeInfo == "string"){
4725             return document.getElementById(nodeInfo);
4726         }else if(typeof nodeInfo == "number"){
4727             return this.nodes[nodeInfo];
4728         }
4729         return nodeInfo;
4730     },
4731
4732     /**
4733      * Gets a range template nodes.
4734      * @param {Number} startIndex
4735      * @param {Number} endIndex
4736      * @return {Array} An array of nodes
4737      */
4738     getNodes : function(start, end){
4739         var ns = this.nodes;
4740         start = start || 0;
4741         end = typeof end == "undefined" ? ns.length - 1 : end;
4742         var nodes = [];
4743         if(start <= end){
4744             for(var i = start; i <= end; i++){
4745                 nodes.push(ns[i]);
4746             }
4747         } else{
4748             for(var i = start; i >= end; i--){
4749                 nodes.push(ns[i]);
4750             }
4751         }
4752         return nodes;
4753     },
4754
4755     /**
4756      * Finds the index of the passed node
4757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4758      * @return {Number} The index of the node or -1
4759      */
4760     indexOf : function(node){
4761         node = this.getNode(node);
4762         if(typeof node.nodeIndex == "number"){
4763             return node.nodeIndex;
4764         }
4765         var ns = this.nodes;
4766         for(var i = 0, len = ns.length; i < len; i++){
4767             if(ns[i] == node){
4768                 return i;
4769             }
4770         }
4771         return -1;
4772     }
4773 });
4774 /*
4775  * Based on:
4776  * Ext JS Library 1.1.1
4777  * Copyright(c) 2006-2007, Ext JS, LLC.
4778  *
4779  * Originally Released Under LGPL - original licence link has changed is not relivant.
4780  *
4781  * Fork - LGPL
4782  * <script type="text/javascript">
4783  */
4784
4785 /**
4786  * @class Roo.JsonView
4787  * @extends Roo.View
4788  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4789 <pre><code>
4790 var view = new Roo.JsonView({
4791     container: "my-element",
4792     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4793     multiSelect: true, 
4794     jsonRoot: "data" 
4795 });
4796
4797 // listen for node click?
4798 view.on("click", function(vw, index, node, e){
4799     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4800 });
4801
4802 // direct load of JSON data
4803 view.load("foobar.php");
4804
4805 // Example from my blog list
4806 var tpl = new Roo.Template(
4807     '&lt;div class="entry"&gt;' +
4808     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4809     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4810     "&lt;/div&gt;&lt;hr /&gt;"
4811 );
4812
4813 var moreView = new Roo.JsonView({
4814     container :  "entry-list", 
4815     template : tpl,
4816     jsonRoot: "posts"
4817 });
4818 moreView.on("beforerender", this.sortEntries, this);
4819 moreView.load({
4820     url: "/blog/get-posts.php",
4821     params: "allposts=true",
4822     text: "Loading Blog Entries..."
4823 });
4824 </code></pre>
4825
4826 * Note: old code is supported with arguments : (container, template, config)
4827
4828
4829  * @constructor
4830  * Create a new JsonView
4831  * 
4832  * @param {Object} config The config object
4833  * 
4834  */
4835 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4836     
4837     
4838     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4839
4840     var um = this.el.getUpdateManager();
4841     um.setRenderer(this);
4842     um.on("update", this.onLoad, this);
4843     um.on("failure", this.onLoadException, this);
4844
4845     /**
4846      * @event beforerender
4847      * Fires before rendering of the downloaded JSON data.
4848      * @param {Roo.JsonView} this
4849      * @param {Object} data The JSON data loaded
4850      */
4851     /**
4852      * @event load
4853      * Fires when data is loaded.
4854      * @param {Roo.JsonView} this
4855      * @param {Object} data The JSON data loaded
4856      * @param {Object} response The raw Connect response object
4857      */
4858     /**
4859      * @event loadexception
4860      * Fires when loading fails.
4861      * @param {Roo.JsonView} this
4862      * @param {Object} response The raw Connect response object
4863      */
4864     this.addEvents({
4865         'beforerender' : true,
4866         'load' : true,
4867         'loadexception' : true
4868     });
4869 };
4870 Roo.extend(Roo.JsonView, Roo.View, {
4871     /**
4872      * @type {String} The root property in the loaded JSON object that contains the data
4873      */
4874     jsonRoot : "",
4875
4876     /**
4877      * Refreshes the view.
4878      */
4879     refresh : function(){
4880         this.clearSelections();
4881         this.el.update("");
4882         var html = [];
4883         var o = this.jsonData;
4884         if(o && o.length > 0){
4885             for(var i = 0, len = o.length; i < len; i++){
4886                 var data = this.prepareData(o[i], i, o);
4887                 html[html.length] = this.tpl.apply(data);
4888             }
4889         }else{
4890             html.push(this.emptyText);
4891         }
4892         this.el.update(html.join(""));
4893         this.nodes = this.el.dom.childNodes;
4894         this.updateIndexes(0);
4895     },
4896
4897     /**
4898      * 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.
4899      * @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:
4900      <pre><code>
4901      view.load({
4902          url: "your-url.php",
4903          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4904          callback: yourFunction,
4905          scope: yourObject, //(optional scope)
4906          discardUrl: false,
4907          nocache: false,
4908          text: "Loading...",
4909          timeout: 30,
4910          scripts: false
4911      });
4912      </code></pre>
4913      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4914      * 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.
4915      * @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}
4916      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4917      * @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.
4918      */
4919     load : function(){
4920         var um = this.el.getUpdateManager();
4921         um.update.apply(um, arguments);
4922     },
4923
4924     // note - render is a standard framework call...
4925     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4926     render : function(el, response){
4927         
4928         this.clearSelections();
4929         this.el.update("");
4930         var o;
4931         try{
4932             if (response != '') {
4933                 o = Roo.util.JSON.decode(response.responseText);
4934                 if(this.jsonRoot){
4935                     
4936                     o = o[this.jsonRoot];
4937                 }
4938             }
4939         } catch(e){
4940         }
4941         /**
4942          * The current JSON data or null
4943          */
4944         this.jsonData = o;
4945         this.beforeRender();
4946         this.refresh();
4947     },
4948
4949 /**
4950  * Get the number of records in the current JSON dataset
4951  * @return {Number}
4952  */
4953     getCount : function(){
4954         return this.jsonData ? this.jsonData.length : 0;
4955     },
4956
4957 /**
4958  * Returns the JSON object for the specified node(s)
4959  * @param {HTMLElement/Array} node The node or an array of nodes
4960  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4961  * you get the JSON object for the node
4962  */
4963     getNodeData : function(node){
4964         if(node instanceof Array){
4965             var data = [];
4966             for(var i = 0, len = node.length; i < len; i++){
4967                 data.push(this.getNodeData(node[i]));
4968             }
4969             return data;
4970         }
4971         return this.jsonData[this.indexOf(node)] || null;
4972     },
4973
4974     beforeRender : function(){
4975         this.snapshot = this.jsonData;
4976         if(this.sortInfo){
4977             this.sort.apply(this, this.sortInfo);
4978         }
4979         this.fireEvent("beforerender", this, this.jsonData);
4980     },
4981
4982     onLoad : function(el, o){
4983         this.fireEvent("load", this, this.jsonData, o);
4984     },
4985
4986     onLoadException : function(el, o){
4987         this.fireEvent("loadexception", this, o);
4988     },
4989
4990 /**
4991  * Filter the data by a specific property.
4992  * @param {String} property A property on your JSON objects
4993  * @param {String/RegExp} value Either string that the property values
4994  * should start with, or a RegExp to test against the property
4995  */
4996     filter : function(property, value){
4997         if(this.jsonData){
4998             var data = [];
4999             var ss = this.snapshot;
5000             if(typeof value == "string"){
5001                 var vlen = value.length;
5002                 if(vlen == 0){
5003                     this.clearFilter();
5004                     return;
5005                 }
5006                 value = value.toLowerCase();
5007                 for(var i = 0, len = ss.length; i < len; i++){
5008                     var o = ss[i];
5009                     if(o[property].substr(0, vlen).toLowerCase() == value){
5010                         data.push(o);
5011                     }
5012                 }
5013             } else if(value.exec){ // regex?
5014                 for(var i = 0, len = ss.length; i < len; i++){
5015                     var o = ss[i];
5016                     if(value.test(o[property])){
5017                         data.push(o);
5018                     }
5019                 }
5020             } else{
5021                 return;
5022             }
5023             this.jsonData = data;
5024             this.refresh();
5025         }
5026     },
5027
5028 /**
5029  * Filter by a function. The passed function will be called with each
5030  * object in the current dataset. If the function returns true the value is kept,
5031  * otherwise it is filtered.
5032  * @param {Function} fn
5033  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5034  */
5035     filterBy : function(fn, scope){
5036         if(this.jsonData){
5037             var data = [];
5038             var ss = this.snapshot;
5039             for(var i = 0, len = ss.length; i < len; i++){
5040                 var o = ss[i];
5041                 if(fn.call(scope || this, o)){
5042                     data.push(o);
5043                 }
5044             }
5045             this.jsonData = data;
5046             this.refresh();
5047         }
5048     },
5049
5050 /**
5051  * Clears the current filter.
5052  */
5053     clearFilter : function(){
5054         if(this.snapshot && this.jsonData != this.snapshot){
5055             this.jsonData = this.snapshot;
5056             this.refresh();
5057         }
5058     },
5059
5060
5061 /**
5062  * Sorts the data for this view and refreshes it.
5063  * @param {String} property A property on your JSON objects to sort on
5064  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5065  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5066  */
5067     sort : function(property, dir, sortType){
5068         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5069         if(this.jsonData){
5070             var p = property;
5071             var dsc = dir && dir.toLowerCase() == "desc";
5072             var f = function(o1, o2){
5073                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5074                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5075                 ;
5076                 if(v1 < v2){
5077                     return dsc ? +1 : -1;
5078                 } else if(v1 > v2){
5079                     return dsc ? -1 : +1;
5080                 } else{
5081                     return 0;
5082                 }
5083             };
5084             this.jsonData.sort(f);
5085             this.refresh();
5086             if(this.jsonData != this.snapshot){
5087                 this.snapshot.sort(f);
5088             }
5089         }
5090     }
5091 });/*
5092  * Based on:
5093  * Ext JS Library 1.1.1
5094  * Copyright(c) 2006-2007, Ext JS, LLC.
5095  *
5096  * Originally Released Under LGPL - original licence link has changed is not relivant.
5097  *
5098  * Fork - LGPL
5099  * <script type="text/javascript">
5100  */
5101  
5102
5103 /**
5104  * @class Roo.ColorPalette
5105  * @extends Roo.Component
5106  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5107  * Here's an example of typical usage:
5108  * <pre><code>
5109 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5110 cp.render('my-div');
5111
5112 cp.on('select', function(palette, selColor){
5113     // do something with selColor
5114 });
5115 </code></pre>
5116  * @constructor
5117  * Create a new ColorPalette
5118  * @param {Object} config The config object
5119  */
5120 Roo.ColorPalette = function(config){
5121     Roo.ColorPalette.superclass.constructor.call(this, config);
5122     this.addEvents({
5123         /**
5124              * @event select
5125              * Fires when a color is selected
5126              * @param {ColorPalette} this
5127              * @param {String} color The 6-digit color hex code (without the # symbol)
5128              */
5129         select: true
5130     });
5131
5132     if(this.handler){
5133         this.on("select", this.handler, this.scope, true);
5134     }
5135 };
5136 Roo.extend(Roo.ColorPalette, Roo.Component, {
5137     /**
5138      * @cfg {String} itemCls
5139      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5140      */
5141     itemCls : "x-color-palette",
5142     /**
5143      * @cfg {String} value
5144      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5145      * the hex codes are case-sensitive.
5146      */
5147     value : null,
5148     clickEvent:'click',
5149     // private
5150     ctype: "Roo.ColorPalette",
5151
5152     /**
5153      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5154      */
5155     allowReselect : false,
5156
5157     /**
5158      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5159      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5160      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5161      * of colors with the width setting until the box is symmetrical.</p>
5162      * <p>You can override individual colors if needed:</p>
5163      * <pre><code>
5164 var cp = new Roo.ColorPalette();
5165 cp.colors[0] = "FF0000";  // change the first box to red
5166 </code></pre>
5167
5168 Or you can provide a custom array of your own for complete control:
5169 <pre><code>
5170 var cp = new Roo.ColorPalette();
5171 cp.colors = ["000000", "993300", "333300"];
5172 </code></pre>
5173      * @type Array
5174      */
5175     colors : [
5176         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5177         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5178         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5179         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5180         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5181     ],
5182
5183     // private
5184     onRender : function(container, position){
5185         var t = new Roo.MasterTemplate(
5186             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5187         );
5188         var c = this.colors;
5189         for(var i = 0, len = c.length; i < len; i++){
5190             t.add([c[i]]);
5191         }
5192         var el = document.createElement("div");
5193         el.className = this.itemCls;
5194         t.overwrite(el);
5195         container.dom.insertBefore(el, position);
5196         this.el = Roo.get(el);
5197         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5198         if(this.clickEvent != 'click'){
5199             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5200         }
5201     },
5202
5203     // private
5204     afterRender : function(){
5205         Roo.ColorPalette.superclass.afterRender.call(this);
5206         if(this.value){
5207             var s = this.value;
5208             this.value = null;
5209             this.select(s);
5210         }
5211     },
5212
5213     // private
5214     handleClick : function(e, t){
5215         e.preventDefault();
5216         if(!this.disabled){
5217             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5218             this.select(c.toUpperCase());
5219         }
5220     },
5221
5222     /**
5223      * Selects the specified color in the palette (fires the select event)
5224      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5225      */
5226     select : function(color){
5227         color = color.replace("#", "");
5228         if(color != this.value || this.allowReselect){
5229             var el = this.el;
5230             if(this.value){
5231                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5232             }
5233             el.child("a.color-"+color).addClass("x-color-palette-sel");
5234             this.value = color;
5235             this.fireEvent("select", this, color);
5236         }
5237     }
5238 });/*
5239  * Based on:
5240  * Ext JS Library 1.1.1
5241  * Copyright(c) 2006-2007, Ext JS, LLC.
5242  *
5243  * Originally Released Under LGPL - original licence link has changed is not relivant.
5244  *
5245  * Fork - LGPL
5246  * <script type="text/javascript">
5247  */
5248  
5249 /**
5250  * @class Roo.DatePicker
5251  * @extends Roo.Component
5252  * Simple date picker class.
5253  * @constructor
5254  * Create a new DatePicker
5255  * @param {Object} config The config object
5256  */
5257 Roo.DatePicker = function(config){
5258     Roo.DatePicker.superclass.constructor.call(this, config);
5259
5260     this.value = config && config.value ?
5261                  config.value.clearTime() : new Date().clearTime();
5262
5263     this.addEvents({
5264         /**
5265              * @event select
5266              * Fires when a date is selected
5267              * @param {DatePicker} this
5268              * @param {Date} date The selected date
5269              */
5270         'select': true,
5271         /**
5272              * @event monthchange
5273              * Fires when the displayed month changes 
5274              * @param {DatePicker} this
5275              * @param {Date} date The selected month
5276              */
5277         'monthchange': true
5278     });
5279
5280     if(this.handler){
5281         this.on("select", this.handler,  this.scope || this);
5282     }
5283     // build the disabledDatesRE
5284     if(!this.disabledDatesRE && this.disabledDates){
5285         var dd = this.disabledDates;
5286         var re = "(?:";
5287         for(var i = 0; i < dd.length; i++){
5288             re += dd[i];
5289             if(i != dd.length-1) {
5290                 re += "|";
5291             }
5292         }
5293         this.disabledDatesRE = new RegExp(re + ")");
5294     }
5295 };
5296
5297 Roo.extend(Roo.DatePicker, Roo.Component, {
5298     /**
5299      * @cfg {String} todayText
5300      * The text to display on the button that selects the current date (defaults to "Today")
5301      */
5302     todayText : "Today",
5303     /**
5304      * @cfg {String} okText
5305      * The text to display on the ok button
5306      */
5307     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5308     /**
5309      * @cfg {String} cancelText
5310      * The text to display on the cancel button
5311      */
5312     cancelText : "Cancel",
5313     /**
5314      * @cfg {String} todayTip
5315      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5316      */
5317     todayTip : "{0} (Spacebar)",
5318     /**
5319      * @cfg {Date} minDate
5320      * Minimum allowable date (JavaScript date object, defaults to null)
5321      */
5322     minDate : null,
5323     /**
5324      * @cfg {Date} maxDate
5325      * Maximum allowable date (JavaScript date object, defaults to null)
5326      */
5327     maxDate : null,
5328     /**
5329      * @cfg {String} minText
5330      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5331      */
5332     minText : "This date is before the minimum date",
5333     /**
5334      * @cfg {String} maxText
5335      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5336      */
5337     maxText : "This date is after the maximum date",
5338     /**
5339      * @cfg {String} format
5340      * The default date format string which can be overriden for localization support.  The format must be
5341      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5342      */
5343     format : "m/d/y",
5344     /**
5345      * @cfg {Array} disabledDays
5346      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5347      */
5348     disabledDays : null,
5349     /**
5350      * @cfg {String} disabledDaysText
5351      * The tooltip to display when the date falls on a disabled day (defaults to "")
5352      */
5353     disabledDaysText : "",
5354     /**
5355      * @cfg {RegExp} disabledDatesRE
5356      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5357      */
5358     disabledDatesRE : null,
5359     /**
5360      * @cfg {String} disabledDatesText
5361      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5362      */
5363     disabledDatesText : "",
5364     /**
5365      * @cfg {Boolean} constrainToViewport
5366      * True to constrain the date picker to the viewport (defaults to true)
5367      */
5368     constrainToViewport : true,
5369     /**
5370      * @cfg {Array} monthNames
5371      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5372      */
5373     monthNames : Date.monthNames,
5374     /**
5375      * @cfg {Array} dayNames
5376      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5377      */
5378     dayNames : Date.dayNames,
5379     /**
5380      * @cfg {String} nextText
5381      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5382      */
5383     nextText: 'Next Month (Control+Right)',
5384     /**
5385      * @cfg {String} prevText
5386      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5387      */
5388     prevText: 'Previous Month (Control+Left)',
5389     /**
5390      * @cfg {String} monthYearText
5391      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5392      */
5393     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5394     /**
5395      * @cfg {Number} startDay
5396      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5397      */
5398     startDay : 0,
5399     /**
5400      * @cfg {Bool} showClear
5401      * Show a clear button (usefull for date form elements that can be blank.)
5402      */
5403     
5404     showClear: false,
5405     
5406     /**
5407      * Sets the value of the date field
5408      * @param {Date} value The date to set
5409      */
5410     setValue : function(value){
5411         var old = this.value;
5412         
5413         if (typeof(value) == 'string') {
5414          
5415             value = Date.parseDate(value, this.format);
5416         }
5417         if (!value) {
5418             value = new Date();
5419         }
5420         
5421         this.value = value.clearTime(true);
5422         if(this.el){
5423             this.update(this.value);
5424         }
5425     },
5426
5427     /**
5428      * Gets the current selected value of the date field
5429      * @return {Date} The selected date
5430      */
5431     getValue : function(){
5432         return this.value;
5433     },
5434
5435     // private
5436     focus : function(){
5437         if(this.el){
5438             this.update(this.activeDate);
5439         }
5440     },
5441
5442     // privateval
5443     onRender : function(container, position){
5444         
5445         var m = [
5446              '<table cellspacing="0">',
5447                 '<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>',
5448                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5449         var dn = this.dayNames;
5450         for(var i = 0; i < 7; i++){
5451             var d = this.startDay+i;
5452             if(d > 6){
5453                 d = d-7;
5454             }
5455             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5456         }
5457         m[m.length] = "</tr></thead><tbody><tr>";
5458         for(var i = 0; i < 42; i++) {
5459             if(i % 7 == 0 && i != 0){
5460                 m[m.length] = "</tr><tr>";
5461             }
5462             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5463         }
5464         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5465             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5466
5467         var el = document.createElement("div");
5468         el.className = "x-date-picker";
5469         el.innerHTML = m.join("");
5470
5471         container.dom.insertBefore(el, position);
5472
5473         this.el = Roo.get(el);
5474         this.eventEl = Roo.get(el.firstChild);
5475
5476         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5477             handler: this.showPrevMonth,
5478             scope: this,
5479             preventDefault:true,
5480             stopDefault:true
5481         });
5482
5483         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5484             handler: this.showNextMonth,
5485             scope: this,
5486             preventDefault:true,
5487             stopDefault:true
5488         });
5489
5490         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5491
5492         this.monthPicker = this.el.down('div.x-date-mp');
5493         this.monthPicker.enableDisplayMode('block');
5494         
5495         var kn = new Roo.KeyNav(this.eventEl, {
5496             "left" : function(e){
5497                 e.ctrlKey ?
5498                     this.showPrevMonth() :
5499                     this.update(this.activeDate.add("d", -1));
5500             },
5501
5502             "right" : function(e){
5503                 e.ctrlKey ?
5504                     this.showNextMonth() :
5505                     this.update(this.activeDate.add("d", 1));
5506             },
5507
5508             "up" : function(e){
5509                 e.ctrlKey ?
5510                     this.showNextYear() :
5511                     this.update(this.activeDate.add("d", -7));
5512             },
5513
5514             "down" : function(e){
5515                 e.ctrlKey ?
5516                     this.showPrevYear() :
5517                     this.update(this.activeDate.add("d", 7));
5518             },
5519
5520             "pageUp" : function(e){
5521                 this.showNextMonth();
5522             },
5523
5524             "pageDown" : function(e){
5525                 this.showPrevMonth();
5526             },
5527
5528             "enter" : function(e){
5529                 e.stopPropagation();
5530                 return true;
5531             },
5532
5533             scope : this
5534         });
5535
5536         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5537
5538         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5539
5540         this.el.unselectable();
5541         
5542         this.cells = this.el.select("table.x-date-inner tbody td");
5543         this.textNodes = this.el.query("table.x-date-inner tbody span");
5544
5545         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5546             text: "&#160;",
5547             tooltip: this.monthYearText
5548         });
5549
5550         this.mbtn.on('click', this.showMonthPicker, this);
5551         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5552
5553
5554         var today = (new Date()).dateFormat(this.format);
5555         
5556         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5557         if (this.showClear) {
5558             baseTb.add( new Roo.Toolbar.Fill());
5559         }
5560         baseTb.add({
5561             text: String.format(this.todayText, today),
5562             tooltip: String.format(this.todayTip, today),
5563             handler: this.selectToday,
5564             scope: this
5565         });
5566         
5567         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5568             
5569         //});
5570         if (this.showClear) {
5571             
5572             baseTb.add( new Roo.Toolbar.Fill());
5573             baseTb.add({
5574                 text: '&#160;',
5575                 cls: 'x-btn-icon x-btn-clear',
5576                 handler: function() {
5577                     //this.value = '';
5578                     this.fireEvent("select", this, '');
5579                 },
5580                 scope: this
5581             });
5582         }
5583         
5584         
5585         if(Roo.isIE){
5586             this.el.repaint();
5587         }
5588         this.update(this.value);
5589     },
5590
5591     createMonthPicker : function(){
5592         if(!this.monthPicker.dom.firstChild){
5593             var buf = ['<table border="0" cellspacing="0">'];
5594             for(var i = 0; i < 6; i++){
5595                 buf.push(
5596                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5597                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5598                     i == 0 ?
5599                     '<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>' :
5600                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5601                 );
5602             }
5603             buf.push(
5604                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5605                     this.okText,
5606                     '</button><button type="button" class="x-date-mp-cancel">',
5607                     this.cancelText,
5608                     '</button></td></tr>',
5609                 '</table>'
5610             );
5611             this.monthPicker.update(buf.join(''));
5612             this.monthPicker.on('click', this.onMonthClick, this);
5613             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5614
5615             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5616             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5617
5618             this.mpMonths.each(function(m, a, i){
5619                 i += 1;
5620                 if((i%2) == 0){
5621                     m.dom.xmonth = 5 + Math.round(i * .5);
5622                 }else{
5623                     m.dom.xmonth = Math.round((i-1) * .5);
5624                 }
5625             });
5626         }
5627     },
5628
5629     showMonthPicker : function(){
5630         this.createMonthPicker();
5631         var size = this.el.getSize();
5632         this.monthPicker.setSize(size);
5633         this.monthPicker.child('table').setSize(size);
5634
5635         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5636         this.updateMPMonth(this.mpSelMonth);
5637         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5638         this.updateMPYear(this.mpSelYear);
5639
5640         this.monthPicker.slideIn('t', {duration:.2});
5641     },
5642
5643     updateMPYear : function(y){
5644         this.mpyear = y;
5645         var ys = this.mpYears.elements;
5646         for(var i = 1; i <= 10; i++){
5647             var td = ys[i-1], y2;
5648             if((i%2) == 0){
5649                 y2 = y + Math.round(i * .5);
5650                 td.firstChild.innerHTML = y2;
5651                 td.xyear = y2;
5652             }else{
5653                 y2 = y - (5-Math.round(i * .5));
5654                 td.firstChild.innerHTML = y2;
5655                 td.xyear = y2;
5656             }
5657             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5658         }
5659     },
5660
5661     updateMPMonth : function(sm){
5662         this.mpMonths.each(function(m, a, i){
5663             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5664         });
5665     },
5666
5667     selectMPMonth: function(m){
5668         
5669     },
5670
5671     onMonthClick : function(e, t){
5672         e.stopEvent();
5673         var el = new Roo.Element(t), pn;
5674         if(el.is('button.x-date-mp-cancel')){
5675             this.hideMonthPicker();
5676         }
5677         else if(el.is('button.x-date-mp-ok')){
5678             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5679             this.hideMonthPicker();
5680         }
5681         else if(pn = el.up('td.x-date-mp-month', 2)){
5682             this.mpMonths.removeClass('x-date-mp-sel');
5683             pn.addClass('x-date-mp-sel');
5684             this.mpSelMonth = pn.dom.xmonth;
5685         }
5686         else if(pn = el.up('td.x-date-mp-year', 2)){
5687             this.mpYears.removeClass('x-date-mp-sel');
5688             pn.addClass('x-date-mp-sel');
5689             this.mpSelYear = pn.dom.xyear;
5690         }
5691         else if(el.is('a.x-date-mp-prev')){
5692             this.updateMPYear(this.mpyear-10);
5693         }
5694         else if(el.is('a.x-date-mp-next')){
5695             this.updateMPYear(this.mpyear+10);
5696         }
5697     },
5698
5699     onMonthDblClick : function(e, t){
5700         e.stopEvent();
5701         var el = new Roo.Element(t), pn;
5702         if(pn = el.up('td.x-date-mp-month', 2)){
5703             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5704             this.hideMonthPicker();
5705         }
5706         else if(pn = el.up('td.x-date-mp-year', 2)){
5707             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5708             this.hideMonthPicker();
5709         }
5710     },
5711
5712     hideMonthPicker : function(disableAnim){
5713         if(this.monthPicker){
5714             if(disableAnim === true){
5715                 this.monthPicker.hide();
5716             }else{
5717                 this.monthPicker.slideOut('t', {duration:.2});
5718             }
5719         }
5720     },
5721
5722     // private
5723     showPrevMonth : function(e){
5724         this.update(this.activeDate.add("mo", -1));
5725     },
5726
5727     // private
5728     showNextMonth : function(e){
5729         this.update(this.activeDate.add("mo", 1));
5730     },
5731
5732     // private
5733     showPrevYear : function(){
5734         this.update(this.activeDate.add("y", -1));
5735     },
5736
5737     // private
5738     showNextYear : function(){
5739         this.update(this.activeDate.add("y", 1));
5740     },
5741
5742     // private
5743     handleMouseWheel : function(e){
5744         var delta = e.getWheelDelta();
5745         if(delta > 0){
5746             this.showPrevMonth();
5747             e.stopEvent();
5748         } else if(delta < 0){
5749             this.showNextMonth();
5750             e.stopEvent();
5751         }
5752     },
5753
5754     // private
5755     handleDateClick : function(e, t){
5756         e.stopEvent();
5757         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5758             this.setValue(new Date(t.dateValue));
5759             this.fireEvent("select", this, this.value);
5760         }
5761     },
5762
5763     // private
5764     selectToday : function(){
5765         this.setValue(new Date().clearTime());
5766         this.fireEvent("select", this, this.value);
5767     },
5768
5769     // private
5770     update : function(date)
5771     {
5772         var vd = this.activeDate;
5773         this.activeDate = date;
5774         if(vd && this.el){
5775             var t = date.getTime();
5776             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5777                 this.cells.removeClass("x-date-selected");
5778                 this.cells.each(function(c){
5779                    if(c.dom.firstChild.dateValue == t){
5780                        c.addClass("x-date-selected");
5781                        setTimeout(function(){
5782                             try{c.dom.firstChild.focus();}catch(e){}
5783                        }, 50);
5784                        return false;
5785                    }
5786                 });
5787                 return;
5788             }
5789         }
5790         
5791         var days = date.getDaysInMonth();
5792         var firstOfMonth = date.getFirstDateOfMonth();
5793         var startingPos = firstOfMonth.getDay()-this.startDay;
5794
5795         if(startingPos <= this.startDay){
5796             startingPos += 7;
5797         }
5798
5799         var pm = date.add("mo", -1);
5800         var prevStart = pm.getDaysInMonth()-startingPos;
5801
5802         var cells = this.cells.elements;
5803         var textEls = this.textNodes;
5804         days += startingPos;
5805
5806         // convert everything to numbers so it's fast
5807         var day = 86400000;
5808         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5809         var today = new Date().clearTime().getTime();
5810         var sel = date.clearTime().getTime();
5811         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5812         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5813         var ddMatch = this.disabledDatesRE;
5814         var ddText = this.disabledDatesText;
5815         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5816         var ddaysText = this.disabledDaysText;
5817         var format = this.format;
5818
5819         var setCellClass = function(cal, cell){
5820             cell.title = "";
5821             var t = d.getTime();
5822             cell.firstChild.dateValue = t;
5823             if(t == today){
5824                 cell.className += " x-date-today";
5825                 cell.title = cal.todayText;
5826             }
5827             if(t == sel){
5828                 cell.className += " x-date-selected";
5829                 setTimeout(function(){
5830                     try{cell.firstChild.focus();}catch(e){}
5831                 }, 50);
5832             }
5833             // disabling
5834             if(t < min) {
5835                 cell.className = " x-date-disabled";
5836                 cell.title = cal.minText;
5837                 return;
5838             }
5839             if(t > max) {
5840                 cell.className = " x-date-disabled";
5841                 cell.title = cal.maxText;
5842                 return;
5843             }
5844             if(ddays){
5845                 if(ddays.indexOf(d.getDay()) != -1){
5846                     cell.title = ddaysText;
5847                     cell.className = " x-date-disabled";
5848                 }
5849             }
5850             if(ddMatch && format){
5851                 var fvalue = d.dateFormat(format);
5852                 if(ddMatch.test(fvalue)){
5853                     cell.title = ddText.replace("%0", fvalue);
5854                     cell.className = " x-date-disabled";
5855                 }
5856             }
5857         };
5858
5859         var i = 0;
5860         for(; i < startingPos; i++) {
5861             textEls[i].innerHTML = (++prevStart);
5862             d.setDate(d.getDate()+1);
5863             cells[i].className = "x-date-prevday";
5864             setCellClass(this, cells[i]);
5865         }
5866         for(; i < days; i++){
5867             intDay = i - startingPos + 1;
5868             textEls[i].innerHTML = (intDay);
5869             d.setDate(d.getDate()+1);
5870             cells[i].className = "x-date-active";
5871             setCellClass(this, cells[i]);
5872         }
5873         var extraDays = 0;
5874         for(; i < 42; i++) {
5875              textEls[i].innerHTML = (++extraDays);
5876              d.setDate(d.getDate()+1);
5877              cells[i].className = "x-date-nextday";
5878              setCellClass(this, cells[i]);
5879         }
5880
5881         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5882         this.fireEvent('monthchange', this, date);
5883         
5884         if(!this.internalRender){
5885             var main = this.el.dom.firstChild;
5886             var w = main.offsetWidth;
5887             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5888             Roo.fly(main).setWidth(w);
5889             this.internalRender = true;
5890             // opera does not respect the auto grow header center column
5891             // then, after it gets a width opera refuses to recalculate
5892             // without a second pass
5893             if(Roo.isOpera && !this.secondPass){
5894                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5895                 this.secondPass = true;
5896                 this.update.defer(10, this, [date]);
5897             }
5898         }
5899         
5900         
5901     }
5902 });        /*
5903  * Based on:
5904  * Ext JS Library 1.1.1
5905  * Copyright(c) 2006-2007, Ext JS, LLC.
5906  *
5907  * Originally Released Under LGPL - original licence link has changed is not relivant.
5908  *
5909  * Fork - LGPL
5910  * <script type="text/javascript">
5911  */
5912 /**
5913  * @class Roo.TabPanel
5914  * @extends Roo.util.Observable
5915  * A lightweight tab container.
5916  * <br><br>
5917  * Usage:
5918  * <pre><code>
5919 // basic tabs 1, built from existing content
5920 var tabs = new Roo.TabPanel("tabs1");
5921 tabs.addTab("script", "View Script");
5922 tabs.addTab("markup", "View Markup");
5923 tabs.activate("script");
5924
5925 // more advanced tabs, built from javascript
5926 var jtabs = new Roo.TabPanel("jtabs");
5927 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5928
5929 // set up the UpdateManager
5930 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5931 var updater = tab2.getUpdateManager();
5932 updater.setDefaultUrl("ajax1.htm");
5933 tab2.on('activate', updater.refresh, updater, true);
5934
5935 // Use setUrl for Ajax loading
5936 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5937 tab3.setUrl("ajax2.htm", null, true);
5938
5939 // Disabled tab
5940 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5941 tab4.disable();
5942
5943 jtabs.activate("jtabs-1");
5944  * </code></pre>
5945  * @constructor
5946  * Create a new TabPanel.
5947  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5948  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5949  */
5950 Roo.TabPanel = function(container, config){
5951     /**
5952     * The container element for this TabPanel.
5953     * @type Roo.Element
5954     */
5955     this.el = Roo.get(container, true);
5956     if(config){
5957         if(typeof config == "boolean"){
5958             this.tabPosition = config ? "bottom" : "top";
5959         }else{
5960             Roo.apply(this, config);
5961         }
5962     }
5963     if(this.tabPosition == "bottom"){
5964         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5965         this.el.addClass("x-tabs-bottom");
5966     }
5967     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5968     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5969     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5970     if(Roo.isIE){
5971         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5972     }
5973     if(this.tabPosition != "bottom"){
5974         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5975          * @type Roo.Element
5976          */
5977         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5978         this.el.addClass("x-tabs-top");
5979     }
5980     this.items = [];
5981
5982     this.bodyEl.setStyle("position", "relative");
5983
5984     this.active = null;
5985     this.activateDelegate = this.activate.createDelegate(this);
5986
5987     this.addEvents({
5988         /**
5989          * @event tabchange
5990          * Fires when the active tab changes
5991          * @param {Roo.TabPanel} this
5992          * @param {Roo.TabPanelItem} activePanel The new active tab
5993          */
5994         "tabchange": true,
5995         /**
5996          * @event beforetabchange
5997          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5998          * @param {Roo.TabPanel} this
5999          * @param {Object} e Set cancel to true on this object to cancel the tab change
6000          * @param {Roo.TabPanelItem} tab The tab being changed to
6001          */
6002         "beforetabchange" : true
6003     });
6004
6005     Roo.EventManager.onWindowResize(this.onResize, this);
6006     this.cpad = this.el.getPadding("lr");
6007     this.hiddenCount = 0;
6008
6009
6010     // toolbar on the tabbar support...
6011     if (this.toolbar) {
6012         var tcfg = this.toolbar;
6013         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
6014         this.toolbar = new Roo.Toolbar(tcfg);
6015         if (Roo.isSafari) {
6016             var tbl = tcfg.container.child('table', true);
6017             tbl.setAttribute('width', '100%');
6018         }
6019         
6020     }
6021    
6022
6023
6024     Roo.TabPanel.superclass.constructor.call(this);
6025 };
6026
6027 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6028     /*
6029      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6030      */
6031     tabPosition : "top",
6032     /*
6033      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6034      */
6035     currentTabWidth : 0,
6036     /*
6037      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6038      */
6039     minTabWidth : 40,
6040     /*
6041      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6042      */
6043     maxTabWidth : 250,
6044     /*
6045      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6046      */
6047     preferredTabWidth : 175,
6048     /*
6049      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6050      */
6051     resizeTabs : false,
6052     /*
6053      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6054      */
6055     monitorResize : true,
6056     /*
6057      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6058      */
6059     toolbar : false,
6060
6061     /**
6062      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6063      * @param {String} id The id of the div to use <b>or create</b>
6064      * @param {String} text The text for the tab
6065      * @param {String} content (optional) Content to put in the TabPanelItem body
6066      * @param {Boolean} closable (optional) True to create a close icon on the tab
6067      * @return {Roo.TabPanelItem} The created TabPanelItem
6068      */
6069     addTab : function(id, text, content, closable){
6070         var item = new Roo.TabPanelItem(this, id, text, closable);
6071         this.addTabItem(item);
6072         if(content){
6073             item.setContent(content);
6074         }
6075         return item;
6076     },
6077
6078     /**
6079      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6080      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6081      * @return {Roo.TabPanelItem}
6082      */
6083     getTab : function(id){
6084         return this.items[id];
6085     },
6086
6087     /**
6088      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6089      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6090      */
6091     hideTab : function(id){
6092         var t = this.items[id];
6093         if(!t.isHidden()){
6094            t.setHidden(true);
6095            this.hiddenCount++;
6096            this.autoSizeTabs();
6097         }
6098     },
6099
6100     /**
6101      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6102      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6103      */
6104     unhideTab : function(id){
6105         var t = this.items[id];
6106         if(t.isHidden()){
6107            t.setHidden(false);
6108            this.hiddenCount--;
6109            this.autoSizeTabs();
6110         }
6111     },
6112
6113     /**
6114      * Adds an existing {@link Roo.TabPanelItem}.
6115      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6116      */
6117     addTabItem : function(item){
6118         this.items[item.id] = item;
6119         this.items.push(item);
6120         if(this.resizeTabs){
6121            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6122            this.autoSizeTabs();
6123         }else{
6124             item.autoSize();
6125         }
6126     },
6127
6128     /**
6129      * Removes a {@link Roo.TabPanelItem}.
6130      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6131      */
6132     removeTab : function(id){
6133         var items = this.items;
6134         var tab = items[id];
6135         if(!tab) { return; }
6136         var index = items.indexOf(tab);
6137         if(this.active == tab && items.length > 1){
6138             var newTab = this.getNextAvailable(index);
6139             if(newTab) {
6140                 newTab.activate();
6141             }
6142         }
6143         this.stripEl.dom.removeChild(tab.pnode.dom);
6144         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6145             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6146         }
6147         items.splice(index, 1);
6148         delete this.items[tab.id];
6149         tab.fireEvent("close", tab);
6150         tab.purgeListeners();
6151         this.autoSizeTabs();
6152     },
6153
6154     getNextAvailable : function(start){
6155         var items = this.items;
6156         var index = start;
6157         // look for a next tab that will slide over to
6158         // replace the one being removed
6159         while(index < items.length){
6160             var item = items[++index];
6161             if(item && !item.isHidden()){
6162                 return item;
6163             }
6164         }
6165         // if one isn't found select the previous tab (on the left)
6166         index = start;
6167         while(index >= 0){
6168             var item = items[--index];
6169             if(item && !item.isHidden()){
6170                 return item;
6171             }
6172         }
6173         return null;
6174     },
6175
6176     /**
6177      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6178      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6179      */
6180     disableTab : function(id){
6181         var tab = this.items[id];
6182         if(tab && this.active != tab){
6183             tab.disable();
6184         }
6185     },
6186
6187     /**
6188      * Enables a {@link Roo.TabPanelItem} that is disabled.
6189      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6190      */
6191     enableTab : function(id){
6192         var tab = this.items[id];
6193         tab.enable();
6194     },
6195
6196     /**
6197      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6198      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6199      * @return {Roo.TabPanelItem} The TabPanelItem.
6200      */
6201     activate : function(id){
6202         var tab = this.items[id];
6203         if(!tab){
6204             return null;
6205         }
6206         if(tab == this.active || tab.disabled){
6207             return tab;
6208         }
6209         var e = {};
6210         this.fireEvent("beforetabchange", this, e, tab);
6211         if(e.cancel !== true && !tab.disabled){
6212             if(this.active){
6213                 this.active.hide();
6214             }
6215             this.active = this.items[id];
6216             this.active.show();
6217             this.fireEvent("tabchange", this, this.active);
6218         }
6219         return tab;
6220     },
6221
6222     /**
6223      * Gets the active {@link Roo.TabPanelItem}.
6224      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6225      */
6226     getActiveTab : function(){
6227         return this.active;
6228     },
6229
6230     /**
6231      * Updates the tab body element to fit the height of the container element
6232      * for overflow scrolling
6233      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6234      */
6235     syncHeight : function(targetHeight){
6236         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6237         var bm = this.bodyEl.getMargins();
6238         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6239         this.bodyEl.setHeight(newHeight);
6240         return newHeight;
6241     },
6242
6243     onResize : function(){
6244         if(this.monitorResize){
6245             this.autoSizeTabs();
6246         }
6247     },
6248
6249     /**
6250      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6251      */
6252     beginUpdate : function(){
6253         this.updating = true;
6254     },
6255
6256     /**
6257      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6258      */
6259     endUpdate : function(){
6260         this.updating = false;
6261         this.autoSizeTabs();
6262     },
6263
6264     /**
6265      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6266      */
6267     autoSizeTabs : function(){
6268         var count = this.items.length;
6269         var vcount = count - this.hiddenCount;
6270         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6271             return;
6272         }
6273         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6274         var availWidth = Math.floor(w / vcount);
6275         var b = this.stripBody;
6276         if(b.getWidth() > w){
6277             var tabs = this.items;
6278             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6279             if(availWidth < this.minTabWidth){
6280                 /*if(!this.sleft){    // incomplete scrolling code
6281                     this.createScrollButtons();
6282                 }
6283                 this.showScroll();
6284                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6285             }
6286         }else{
6287             if(this.currentTabWidth < this.preferredTabWidth){
6288                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6289             }
6290         }
6291     },
6292
6293     /**
6294      * Returns the number of tabs in this TabPanel.
6295      * @return {Number}
6296      */
6297      getCount : function(){
6298          return this.items.length;
6299      },
6300
6301     /**
6302      * Resizes all the tabs to the passed width
6303      * @param {Number} The new width
6304      */
6305     setTabWidth : function(width){
6306         this.currentTabWidth = width;
6307         for(var i = 0, len = this.items.length; i < len; i++) {
6308                 if(!this.items[i].isHidden()) {
6309                 this.items[i].setWidth(width);
6310             }
6311         }
6312     },
6313
6314     /**
6315      * Destroys this TabPanel
6316      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6317      */
6318     destroy : function(removeEl){
6319         Roo.EventManager.removeResizeListener(this.onResize, this);
6320         for(var i = 0, len = this.items.length; i < len; i++){
6321             this.items[i].purgeListeners();
6322         }
6323         if(removeEl === true){
6324             this.el.update("");
6325             this.el.remove();
6326         }
6327     }
6328 });
6329
6330 /**
6331  * @class Roo.TabPanelItem
6332  * @extends Roo.util.Observable
6333  * Represents an individual item (tab plus body) in a TabPanel.
6334  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6335  * @param {String} id The id of this TabPanelItem
6336  * @param {String} text The text for the tab of this TabPanelItem
6337  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6338  */
6339 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6340     /**
6341      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6342      * @type Roo.TabPanel
6343      */
6344     this.tabPanel = tabPanel;
6345     /**
6346      * The id for this TabPanelItem
6347      * @type String
6348      */
6349     this.id = id;
6350     /** @private */
6351     this.disabled = false;
6352     /** @private */
6353     this.text = text;
6354     /** @private */
6355     this.loaded = false;
6356     this.closable = closable;
6357
6358     /**
6359      * The body element for this TabPanelItem.
6360      * @type Roo.Element
6361      */
6362     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6363     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6364     this.bodyEl.setStyle("display", "block");
6365     this.bodyEl.setStyle("zoom", "1");
6366     this.hideAction();
6367
6368     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6369     /** @private */
6370     this.el = Roo.get(els.el, true);
6371     this.inner = Roo.get(els.inner, true);
6372     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6373     this.pnode = Roo.get(els.el.parentNode, true);
6374     this.el.on("mousedown", this.onTabMouseDown, this);
6375     this.el.on("click", this.onTabClick, this);
6376     /** @private */
6377     if(closable){
6378         var c = Roo.get(els.close, true);
6379         c.dom.title = this.closeText;
6380         c.addClassOnOver("close-over");
6381         c.on("click", this.closeClick, this);
6382      }
6383
6384     this.addEvents({
6385          /**
6386          * @event activate
6387          * Fires when this tab becomes the active tab.
6388          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6389          * @param {Roo.TabPanelItem} this
6390          */
6391         "activate": true,
6392         /**
6393          * @event beforeclose
6394          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6395          * @param {Roo.TabPanelItem} this
6396          * @param {Object} e Set cancel to true on this object to cancel the close.
6397          */
6398         "beforeclose": true,
6399         /**
6400          * @event close
6401          * Fires when this tab is closed.
6402          * @param {Roo.TabPanelItem} this
6403          */
6404          "close": true,
6405         /**
6406          * @event deactivate
6407          * Fires when this tab is no longer the active tab.
6408          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6409          * @param {Roo.TabPanelItem} this
6410          */
6411          "deactivate" : true
6412     });
6413     this.hidden = false;
6414
6415     Roo.TabPanelItem.superclass.constructor.call(this);
6416 };
6417
6418 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6419     purgeListeners : function(){
6420        Roo.util.Observable.prototype.purgeListeners.call(this);
6421        this.el.removeAllListeners();
6422     },
6423     /**
6424      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6425      */
6426     show : function(){
6427         this.pnode.addClass("on");
6428         this.showAction();
6429         if(Roo.isOpera){
6430             this.tabPanel.stripWrap.repaint();
6431         }
6432         this.fireEvent("activate", this.tabPanel, this);
6433     },
6434
6435     /**
6436      * Returns true if this tab is the active tab.
6437      * @return {Boolean}
6438      */
6439     isActive : function(){
6440         return this.tabPanel.getActiveTab() == this;
6441     },
6442
6443     /**
6444      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6445      */
6446     hide : function(){
6447         this.pnode.removeClass("on");
6448         this.hideAction();
6449         this.fireEvent("deactivate", this.tabPanel, this);
6450     },
6451
6452     hideAction : function(){
6453         this.bodyEl.hide();
6454         this.bodyEl.setStyle("position", "absolute");
6455         this.bodyEl.setLeft("-20000px");
6456         this.bodyEl.setTop("-20000px");
6457     },
6458
6459     showAction : function(){
6460         this.bodyEl.setStyle("position", "relative");
6461         this.bodyEl.setTop("");
6462         this.bodyEl.setLeft("");
6463         this.bodyEl.show();
6464     },
6465
6466     /**
6467      * Set the tooltip for the tab.
6468      * @param {String} tooltip The tab's tooltip
6469      */
6470     setTooltip : function(text){
6471         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6472             this.textEl.dom.qtip = text;
6473             this.textEl.dom.removeAttribute('title');
6474         }else{
6475             this.textEl.dom.title = text;
6476         }
6477     },
6478
6479     onTabClick : function(e){
6480         e.preventDefault();
6481         this.tabPanel.activate(this.id);
6482     },
6483
6484     onTabMouseDown : function(e){
6485         e.preventDefault();
6486         this.tabPanel.activate(this.id);
6487     },
6488
6489     getWidth : function(){
6490         return this.inner.getWidth();
6491     },
6492
6493     setWidth : function(width){
6494         var iwidth = width - this.pnode.getPadding("lr");
6495         this.inner.setWidth(iwidth);
6496         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6497         this.pnode.setWidth(width);
6498     },
6499
6500     /**
6501      * Show or hide the tab
6502      * @param {Boolean} hidden True to hide or false to show.
6503      */
6504     setHidden : function(hidden){
6505         this.hidden = hidden;
6506         this.pnode.setStyle("display", hidden ? "none" : "");
6507     },
6508
6509     /**
6510      * Returns true if this tab is "hidden"
6511      * @return {Boolean}
6512      */
6513     isHidden : function(){
6514         return this.hidden;
6515     },
6516
6517     /**
6518      * Returns the text for this tab
6519      * @return {String}
6520      */
6521     getText : function(){
6522         return this.text;
6523     },
6524
6525     autoSize : function(){
6526         //this.el.beginMeasure();
6527         this.textEl.setWidth(1);
6528         /*
6529          *  #2804 [new] Tabs in Roojs
6530          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6531          */
6532         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6533         //this.el.endMeasure();
6534     },
6535
6536     /**
6537      * Sets the text for the tab (Note: this also sets the tooltip text)
6538      * @param {String} text The tab's text and tooltip
6539      */
6540     setText : function(text){
6541         this.text = text;
6542         this.textEl.update(text);
6543         this.setTooltip(text);
6544         if(!this.tabPanel.resizeTabs){
6545             this.autoSize();
6546         }
6547     },
6548     /**
6549      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6550      */
6551     activate : function(){
6552         this.tabPanel.activate(this.id);
6553     },
6554
6555     /**
6556      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6557      */
6558     disable : function(){
6559         if(this.tabPanel.active != this){
6560             this.disabled = true;
6561             this.pnode.addClass("disabled");
6562         }
6563     },
6564
6565     /**
6566      * Enables this TabPanelItem if it was previously disabled.
6567      */
6568     enable : function(){
6569         this.disabled = false;
6570         this.pnode.removeClass("disabled");
6571     },
6572
6573     /**
6574      * Sets the content for this TabPanelItem.
6575      * @param {String} content The content
6576      * @param {Boolean} loadScripts true to look for and load scripts
6577      */
6578     setContent : function(content, loadScripts){
6579         this.bodyEl.update(content, loadScripts);
6580     },
6581
6582     /**
6583      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6584      * @return {Roo.UpdateManager} The UpdateManager
6585      */
6586     getUpdateManager : function(){
6587         return this.bodyEl.getUpdateManager();
6588     },
6589
6590     /**
6591      * Set a URL to be used to load the content for this TabPanelItem.
6592      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6593      * @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)
6594      * @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)
6595      * @return {Roo.UpdateManager} The UpdateManager
6596      */
6597     setUrl : function(url, params, loadOnce){
6598         if(this.refreshDelegate){
6599             this.un('activate', this.refreshDelegate);
6600         }
6601         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6602         this.on("activate", this.refreshDelegate);
6603         return this.bodyEl.getUpdateManager();
6604     },
6605
6606     /** @private */
6607     _handleRefresh : function(url, params, loadOnce){
6608         if(!loadOnce || !this.loaded){
6609             var updater = this.bodyEl.getUpdateManager();
6610             updater.update(url, params, this._setLoaded.createDelegate(this));
6611         }
6612     },
6613
6614     /**
6615      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6616      *   Will fail silently if the setUrl method has not been called.
6617      *   This does not activate the panel, just updates its content.
6618      */
6619     refresh : function(){
6620         if(this.refreshDelegate){
6621            this.loaded = false;
6622            this.refreshDelegate();
6623         }
6624     },
6625
6626     /** @private */
6627     _setLoaded : function(){
6628         this.loaded = true;
6629     },
6630
6631     /** @private */
6632     closeClick : function(e){
6633         var o = {};
6634         e.stopEvent();
6635         this.fireEvent("beforeclose", this, o);
6636         if(o.cancel !== true){
6637             this.tabPanel.removeTab(this.id);
6638         }
6639     },
6640     /**
6641      * The text displayed in the tooltip for the close icon.
6642      * @type String
6643      */
6644     closeText : "Close this tab"
6645 });
6646
6647 /** @private */
6648 Roo.TabPanel.prototype.createStrip = function(container){
6649     var strip = document.createElement("div");
6650     strip.className = "x-tabs-wrap";
6651     container.appendChild(strip);
6652     return strip;
6653 };
6654 /** @private */
6655 Roo.TabPanel.prototype.createStripList = function(strip){
6656     // div wrapper for retard IE
6657     // returns the "tr" element.
6658     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6659         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6660         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6661     return strip.firstChild.firstChild.firstChild.firstChild;
6662 };
6663 /** @private */
6664 Roo.TabPanel.prototype.createBody = function(container){
6665     var body = document.createElement("div");
6666     Roo.id(body, "tab-body");
6667     Roo.fly(body).addClass("x-tabs-body");
6668     container.appendChild(body);
6669     return body;
6670 };
6671 /** @private */
6672 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6673     var body = Roo.getDom(id);
6674     if(!body){
6675         body = document.createElement("div");
6676         body.id = id;
6677     }
6678     Roo.fly(body).addClass("x-tabs-item-body");
6679     bodyEl.insertBefore(body, bodyEl.firstChild);
6680     return body;
6681 };
6682 /** @private */
6683 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6684     var td = document.createElement("td");
6685     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6686     //stripEl.appendChild(td);
6687     if(closable){
6688         td.className = "x-tabs-closable";
6689         if(!this.closeTpl){
6690             this.closeTpl = new Roo.Template(
6691                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6692                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6693                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6694             );
6695         }
6696         var el = this.closeTpl.overwrite(td, {"text": text});
6697         var close = el.getElementsByTagName("div")[0];
6698         var inner = el.getElementsByTagName("em")[0];
6699         return {"el": el, "close": close, "inner": inner};
6700     } else {
6701         if(!this.tabTpl){
6702             this.tabTpl = new Roo.Template(
6703                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6704                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6705             );
6706         }
6707         var el = this.tabTpl.overwrite(td, {"text": text});
6708         var inner = el.getElementsByTagName("em")[0];
6709         return {"el": el, "inner": inner};
6710     }
6711 };/*
6712  * Based on:
6713  * Ext JS Library 1.1.1
6714  * Copyright(c) 2006-2007, Ext JS, LLC.
6715  *
6716  * Originally Released Under LGPL - original licence link has changed is not relivant.
6717  *
6718  * Fork - LGPL
6719  * <script type="text/javascript">
6720  */
6721
6722 /**
6723  * @class Roo.Button
6724  * @extends Roo.util.Observable
6725  * Simple Button class
6726  * @cfg {String} text The button text
6727  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6728  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6729  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6730  * @cfg {Object} scope The scope of the handler
6731  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6732  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6733  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6734  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6735  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6736  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6737    applies if enableToggle = true)
6738  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6739  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6740   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6741  * @constructor
6742  * Create a new button
6743  * @param {Object} config The config object
6744  */
6745 Roo.Button = function(renderTo, config)
6746 {
6747     if (!config) {
6748         config = renderTo;
6749         renderTo = config.renderTo || false;
6750     }
6751     
6752     Roo.apply(this, config);
6753     this.addEvents({
6754         /**
6755              * @event click
6756              * Fires when this button is clicked
6757              * @param {Button} this
6758              * @param {EventObject} e The click event
6759              */
6760             "click" : true,
6761         /**
6762              * @event toggle
6763              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6764              * @param {Button} this
6765              * @param {Boolean} pressed
6766              */
6767             "toggle" : true,
6768         /**
6769              * @event mouseover
6770              * Fires when the mouse hovers over the button
6771              * @param {Button} this
6772              * @param {Event} e The event object
6773              */
6774         'mouseover' : true,
6775         /**
6776              * @event mouseout
6777              * Fires when the mouse exits the button
6778              * @param {Button} this
6779              * @param {Event} e The event object
6780              */
6781         'mouseout': true,
6782          /**
6783              * @event render
6784              * Fires when the button is rendered
6785              * @param {Button} this
6786              */
6787         'render': true
6788     });
6789     if(this.menu){
6790         this.menu = Roo.menu.MenuMgr.get(this.menu);
6791     }
6792     // register listeners first!!  - so render can be captured..
6793     Roo.util.Observable.call(this);
6794     if(renderTo){
6795         this.render(renderTo);
6796     }
6797     
6798   
6799 };
6800
6801 Roo.extend(Roo.Button, Roo.util.Observable, {
6802     /**
6803      * 
6804      */
6805     
6806     /**
6807      * Read-only. True if this button is hidden
6808      * @type Boolean
6809      */
6810     hidden : false,
6811     /**
6812      * Read-only. True if this button is disabled
6813      * @type Boolean
6814      */
6815     disabled : false,
6816     /**
6817      * Read-only. True if this button is pressed (only if enableToggle = true)
6818      * @type Boolean
6819      */
6820     pressed : false,
6821
6822     /**
6823      * @cfg {Number} tabIndex 
6824      * The DOM tabIndex for this button (defaults to undefined)
6825      */
6826     tabIndex : undefined,
6827
6828     /**
6829      * @cfg {Boolean} enableToggle
6830      * True to enable pressed/not pressed toggling (defaults to false)
6831      */
6832     enableToggle: false,
6833     /**
6834      * @cfg {Mixed} menu
6835      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6836      */
6837     menu : undefined,
6838     /**
6839      * @cfg {String} menuAlign
6840      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6841      */
6842     menuAlign : "tl-bl?",
6843
6844     /**
6845      * @cfg {String} iconCls
6846      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6847      */
6848     iconCls : undefined,
6849     /**
6850      * @cfg {String} type
6851      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6852      */
6853     type : 'button',
6854
6855     // private
6856     menuClassTarget: 'tr',
6857
6858     /**
6859      * @cfg {String} clickEvent
6860      * The type of event to map to the button's event handler (defaults to 'click')
6861      */
6862     clickEvent : 'click',
6863
6864     /**
6865      * @cfg {Boolean} handleMouseEvents
6866      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6867      */
6868     handleMouseEvents : true,
6869
6870     /**
6871      * @cfg {String} tooltipType
6872      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6873      */
6874     tooltipType : 'qtip',
6875
6876     /**
6877      * @cfg {String} cls
6878      * A CSS class to apply to the button's main element.
6879      */
6880     
6881     /**
6882      * @cfg {Roo.Template} template (Optional)
6883      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6884      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6885      * require code modifications if required elements (e.g. a button) aren't present.
6886      */
6887
6888     // private
6889     render : function(renderTo){
6890         var btn;
6891         if(this.hideParent){
6892             this.parentEl = Roo.get(renderTo);
6893         }
6894         if(!this.dhconfig){
6895             if(!this.template){
6896                 if(!Roo.Button.buttonTemplate){
6897                     // hideous table template
6898                     Roo.Button.buttonTemplate = new Roo.Template(
6899                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6900                         '<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>',
6901                         "</tr></tbody></table>");
6902                 }
6903                 this.template = Roo.Button.buttonTemplate;
6904             }
6905             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6906             var btnEl = btn.child("button:first");
6907             btnEl.on('focus', this.onFocus, this);
6908             btnEl.on('blur', this.onBlur, this);
6909             if(this.cls){
6910                 btn.addClass(this.cls);
6911             }
6912             if(this.icon){
6913                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6914             }
6915             if(this.iconCls){
6916                 btnEl.addClass(this.iconCls);
6917                 if(!this.cls){
6918                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6919                 }
6920             }
6921             if(this.tabIndex !== undefined){
6922                 btnEl.dom.tabIndex = this.tabIndex;
6923             }
6924             if(this.tooltip){
6925                 if(typeof this.tooltip == 'object'){
6926                     Roo.QuickTips.tips(Roo.apply({
6927                           target: btnEl.id
6928                     }, this.tooltip));
6929                 } else {
6930                     btnEl.dom[this.tooltipType] = this.tooltip;
6931                 }
6932             }
6933         }else{
6934             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6935         }
6936         this.el = btn;
6937         if(this.id){
6938             this.el.dom.id = this.el.id = this.id;
6939         }
6940         if(this.menu){
6941             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6942             this.menu.on("show", this.onMenuShow, this);
6943             this.menu.on("hide", this.onMenuHide, this);
6944         }
6945         btn.addClass("x-btn");
6946         if(Roo.isIE && !Roo.isIE7){
6947             this.autoWidth.defer(1, this);
6948         }else{
6949             this.autoWidth();
6950         }
6951         if(this.handleMouseEvents){
6952             btn.on("mouseover", this.onMouseOver, this);
6953             btn.on("mouseout", this.onMouseOut, this);
6954             btn.on("mousedown", this.onMouseDown, this);
6955         }
6956         btn.on(this.clickEvent, this.onClick, this);
6957         //btn.on("mouseup", this.onMouseUp, this);
6958         if(this.hidden){
6959             this.hide();
6960         }
6961         if(this.disabled){
6962             this.disable();
6963         }
6964         Roo.ButtonToggleMgr.register(this);
6965         if(this.pressed){
6966             this.el.addClass("x-btn-pressed");
6967         }
6968         if(this.repeat){
6969             var repeater = new Roo.util.ClickRepeater(btn,
6970                 typeof this.repeat == "object" ? this.repeat : {}
6971             );
6972             repeater.on("click", this.onClick,  this);
6973         }
6974         
6975         this.fireEvent('render', this);
6976         
6977     },
6978     /**
6979      * Returns the button's underlying element
6980      * @return {Roo.Element} The element
6981      */
6982     getEl : function(){
6983         return this.el;  
6984     },
6985     
6986     /**
6987      * Destroys this Button and removes any listeners.
6988      */
6989     destroy : function(){
6990         Roo.ButtonToggleMgr.unregister(this);
6991         this.el.removeAllListeners();
6992         this.purgeListeners();
6993         this.el.remove();
6994     },
6995
6996     // private
6997     autoWidth : function(){
6998         if(this.el){
6999             this.el.setWidth("auto");
7000             if(Roo.isIE7 && Roo.isStrict){
7001                 var ib = this.el.child('button');
7002                 if(ib && ib.getWidth() > 20){
7003                     ib.clip();
7004                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7005                 }
7006             }
7007             if(this.minWidth){
7008                 if(this.hidden){
7009                     this.el.beginMeasure();
7010                 }
7011                 if(this.el.getWidth() < this.minWidth){
7012                     this.el.setWidth(this.minWidth);
7013                 }
7014                 if(this.hidden){
7015                     this.el.endMeasure();
7016                 }
7017             }
7018         }
7019     },
7020
7021     /**
7022      * Assigns this button's click handler
7023      * @param {Function} handler The function to call when the button is clicked
7024      * @param {Object} scope (optional) Scope for the function passed in
7025      */
7026     setHandler : function(handler, scope){
7027         this.handler = handler;
7028         this.scope = scope;  
7029     },
7030     
7031     /**
7032      * Sets this button's text
7033      * @param {String} text The button text
7034      */
7035     setText : function(text){
7036         this.text = text;
7037         if(this.el){
7038             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7039         }
7040         this.autoWidth();
7041     },
7042     
7043     /**
7044      * Gets the text for this button
7045      * @return {String} The button text
7046      */
7047     getText : function(){
7048         return this.text;  
7049     },
7050     
7051     /**
7052      * Show this button
7053      */
7054     show: function(){
7055         this.hidden = false;
7056         if(this.el){
7057             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7058         }
7059     },
7060     
7061     /**
7062      * Hide this button
7063      */
7064     hide: function(){
7065         this.hidden = true;
7066         if(this.el){
7067             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7068         }
7069     },
7070     
7071     /**
7072      * Convenience function for boolean show/hide
7073      * @param {Boolean} visible True to show, false to hide
7074      */
7075     setVisible: function(visible){
7076         if(visible) {
7077             this.show();
7078         }else{
7079             this.hide();
7080         }
7081     },
7082     
7083     /**
7084      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7085      * @param {Boolean} state (optional) Force a particular state
7086      */
7087     toggle : function(state){
7088         state = state === undefined ? !this.pressed : state;
7089         if(state != this.pressed){
7090             if(state){
7091                 this.el.addClass("x-btn-pressed");
7092                 this.pressed = true;
7093                 this.fireEvent("toggle", this, true);
7094             }else{
7095                 this.el.removeClass("x-btn-pressed");
7096                 this.pressed = false;
7097                 this.fireEvent("toggle", this, false);
7098             }
7099             if(this.toggleHandler){
7100                 this.toggleHandler.call(this.scope || this, this, state);
7101             }
7102         }
7103     },
7104     
7105     /**
7106      * Focus the button
7107      */
7108     focus : function(){
7109         this.el.child('button:first').focus();
7110     },
7111     
7112     /**
7113      * Disable this button
7114      */
7115     disable : function(){
7116         if(this.el){
7117             this.el.addClass("x-btn-disabled");
7118         }
7119         this.disabled = true;
7120     },
7121     
7122     /**
7123      * Enable this button
7124      */
7125     enable : function(){
7126         if(this.el){
7127             this.el.removeClass("x-btn-disabled");
7128         }
7129         this.disabled = false;
7130     },
7131
7132     /**
7133      * Convenience function for boolean enable/disable
7134      * @param {Boolean} enabled True to enable, false to disable
7135      */
7136     setDisabled : function(v){
7137         this[v !== true ? "enable" : "disable"]();
7138     },
7139
7140     // private
7141     onClick : function(e)
7142     {
7143         if(e){
7144             e.preventDefault();
7145         }
7146         if(e.button != 0){
7147             return;
7148         }
7149         if(!this.disabled){
7150             if(this.enableToggle){
7151                 this.toggle();
7152             }
7153             if(this.menu && !this.menu.isVisible()){
7154                 this.menu.show(this.el, this.menuAlign);
7155             }
7156             this.fireEvent("click", this, e);
7157             if(this.handler){
7158                 this.el.removeClass("x-btn-over");
7159                 this.handler.call(this.scope || this, this, e);
7160             }
7161         }
7162     },
7163     // private
7164     onMouseOver : function(e){
7165         if(!this.disabled){
7166             this.el.addClass("x-btn-over");
7167             this.fireEvent('mouseover', this, e);
7168         }
7169     },
7170     // private
7171     onMouseOut : function(e){
7172         if(!e.within(this.el,  true)){
7173             this.el.removeClass("x-btn-over");
7174             this.fireEvent('mouseout', this, e);
7175         }
7176     },
7177     // private
7178     onFocus : function(e){
7179         if(!this.disabled){
7180             this.el.addClass("x-btn-focus");
7181         }
7182     },
7183     // private
7184     onBlur : function(e){
7185         this.el.removeClass("x-btn-focus");
7186     },
7187     // private
7188     onMouseDown : function(e){
7189         if(!this.disabled && e.button == 0){
7190             this.el.addClass("x-btn-click");
7191             Roo.get(document).on('mouseup', this.onMouseUp, this);
7192         }
7193     },
7194     // private
7195     onMouseUp : function(e){
7196         if(e.button == 0){
7197             this.el.removeClass("x-btn-click");
7198             Roo.get(document).un('mouseup', this.onMouseUp, this);
7199         }
7200     },
7201     // private
7202     onMenuShow : function(e){
7203         this.el.addClass("x-btn-menu-active");
7204     },
7205     // private
7206     onMenuHide : function(e){
7207         this.el.removeClass("x-btn-menu-active");
7208     }   
7209 });
7210
7211 // Private utility class used by Button
7212 Roo.ButtonToggleMgr = function(){
7213    var groups = {};
7214    
7215    function toggleGroup(btn, state){
7216        if(state){
7217            var g = groups[btn.toggleGroup];
7218            for(var i = 0, l = g.length; i < l; i++){
7219                if(g[i] != btn){
7220                    g[i].toggle(false);
7221                }
7222            }
7223        }
7224    }
7225    
7226    return {
7227        register : function(btn){
7228            if(!btn.toggleGroup){
7229                return;
7230            }
7231            var g = groups[btn.toggleGroup];
7232            if(!g){
7233                g = groups[btn.toggleGroup] = [];
7234            }
7235            g.push(btn);
7236            btn.on("toggle", toggleGroup);
7237        },
7238        
7239        unregister : function(btn){
7240            if(!btn.toggleGroup){
7241                return;
7242            }
7243            var g = groups[btn.toggleGroup];
7244            if(g){
7245                g.remove(btn);
7246                btn.un("toggle", toggleGroup);
7247            }
7248        }
7249    };
7250 }();/*
7251  * Based on:
7252  * Ext JS Library 1.1.1
7253  * Copyright(c) 2006-2007, Ext JS, LLC.
7254  *
7255  * Originally Released Under LGPL - original licence link has changed is not relivant.
7256  *
7257  * Fork - LGPL
7258  * <script type="text/javascript">
7259  */
7260  
7261 /**
7262  * @class Roo.SplitButton
7263  * @extends Roo.Button
7264  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7265  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7266  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7267  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7268  * @cfg {String} arrowTooltip The title attribute of the arrow
7269  * @constructor
7270  * Create a new menu button
7271  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7272  * @param {Object} config The config object
7273  */
7274 Roo.SplitButton = function(renderTo, config){
7275     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7276     /**
7277      * @event arrowclick
7278      * Fires when this button's arrow is clicked
7279      * @param {SplitButton} this
7280      * @param {EventObject} e The click event
7281      */
7282     this.addEvents({"arrowclick":true});
7283 };
7284
7285 Roo.extend(Roo.SplitButton, Roo.Button, {
7286     render : function(renderTo){
7287         // this is one sweet looking template!
7288         var tpl = new Roo.Template(
7289             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7290             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7291             '<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>',
7292             "</tbody></table></td><td>",
7293             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7294             '<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>',
7295             "</tbody></table></td></tr></table>"
7296         );
7297         var btn = tpl.append(renderTo, [this.text, this.type], true);
7298         var btnEl = btn.child("button");
7299         if(this.cls){
7300             btn.addClass(this.cls);
7301         }
7302         if(this.icon){
7303             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7304         }
7305         if(this.iconCls){
7306             btnEl.addClass(this.iconCls);
7307             if(!this.cls){
7308                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7309             }
7310         }
7311         this.el = btn;
7312         if(this.handleMouseEvents){
7313             btn.on("mouseover", this.onMouseOver, this);
7314             btn.on("mouseout", this.onMouseOut, this);
7315             btn.on("mousedown", this.onMouseDown, this);
7316             btn.on("mouseup", this.onMouseUp, this);
7317         }
7318         btn.on(this.clickEvent, this.onClick, this);
7319         if(this.tooltip){
7320             if(typeof this.tooltip == 'object'){
7321                 Roo.QuickTips.tips(Roo.apply({
7322                       target: btnEl.id
7323                 }, this.tooltip));
7324             } else {
7325                 btnEl.dom[this.tooltipType] = this.tooltip;
7326             }
7327         }
7328         if(this.arrowTooltip){
7329             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7330         }
7331         if(this.hidden){
7332             this.hide();
7333         }
7334         if(this.disabled){
7335             this.disable();
7336         }
7337         if(this.pressed){
7338             this.el.addClass("x-btn-pressed");
7339         }
7340         if(Roo.isIE && !Roo.isIE7){
7341             this.autoWidth.defer(1, this);
7342         }else{
7343             this.autoWidth();
7344         }
7345         if(this.menu){
7346             this.menu.on("show", this.onMenuShow, this);
7347             this.menu.on("hide", this.onMenuHide, this);
7348         }
7349         this.fireEvent('render', this);
7350     },
7351
7352     // private
7353     autoWidth : function(){
7354         if(this.el){
7355             var tbl = this.el.child("table:first");
7356             var tbl2 = this.el.child("table:last");
7357             this.el.setWidth("auto");
7358             tbl.setWidth("auto");
7359             if(Roo.isIE7 && Roo.isStrict){
7360                 var ib = this.el.child('button:first');
7361                 if(ib && ib.getWidth() > 20){
7362                     ib.clip();
7363                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7364                 }
7365             }
7366             if(this.minWidth){
7367                 if(this.hidden){
7368                     this.el.beginMeasure();
7369                 }
7370                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7371                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7372                 }
7373                 if(this.hidden){
7374                     this.el.endMeasure();
7375                 }
7376             }
7377             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7378         } 
7379     },
7380     /**
7381      * Sets this button's click handler
7382      * @param {Function} handler The function to call when the button is clicked
7383      * @param {Object} scope (optional) Scope for the function passed above
7384      */
7385     setHandler : function(handler, scope){
7386         this.handler = handler;
7387         this.scope = scope;  
7388     },
7389     
7390     /**
7391      * Sets this button's arrow click handler
7392      * @param {Function} handler The function to call when the arrow is clicked
7393      * @param {Object} scope (optional) Scope for the function passed above
7394      */
7395     setArrowHandler : function(handler, scope){
7396         this.arrowHandler = handler;
7397         this.scope = scope;  
7398     },
7399     
7400     /**
7401      * Focus the button
7402      */
7403     focus : function(){
7404         if(this.el){
7405             this.el.child("button:first").focus();
7406         }
7407     },
7408
7409     // private
7410     onClick : function(e){
7411         e.preventDefault();
7412         if(!this.disabled){
7413             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7414                 if(this.menu && !this.menu.isVisible()){
7415                     this.menu.show(this.el, this.menuAlign);
7416                 }
7417                 this.fireEvent("arrowclick", this, e);
7418                 if(this.arrowHandler){
7419                     this.arrowHandler.call(this.scope || this, this, e);
7420                 }
7421             }else{
7422                 this.fireEvent("click", this, e);
7423                 if(this.handler){
7424                     this.handler.call(this.scope || this, this, e);
7425                 }
7426             }
7427         }
7428     },
7429     // private
7430     onMouseDown : function(e){
7431         if(!this.disabled){
7432             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7433         }
7434     },
7435     // private
7436     onMouseUp : function(e){
7437         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7438     }   
7439 });
7440
7441
7442 // backwards compat
7443 Roo.MenuButton = Roo.SplitButton;/*
7444  * Based on:
7445  * Ext JS Library 1.1.1
7446  * Copyright(c) 2006-2007, Ext JS, LLC.
7447  *
7448  * Originally Released Under LGPL - original licence link has changed is not relivant.
7449  *
7450  * Fork - LGPL
7451  * <script type="text/javascript">
7452  */
7453
7454 /**
7455  * @class Roo.Toolbar
7456  * Basic Toolbar class.
7457  * @constructor
7458  * Creates a new Toolbar
7459  * @param {Object} container The config object
7460  */ 
7461 Roo.Toolbar = function(container, buttons, config)
7462 {
7463     /// old consturctor format still supported..
7464     if(container instanceof Array){ // omit the container for later rendering
7465         buttons = container;
7466         config = buttons;
7467         container = null;
7468     }
7469     if (typeof(container) == 'object' && container.xtype) {
7470         config = container;
7471         container = config.container;
7472         buttons = config.buttons || []; // not really - use items!!
7473     }
7474     var xitems = [];
7475     if (config && config.items) {
7476         xitems = config.items;
7477         delete config.items;
7478     }
7479     Roo.apply(this, config);
7480     this.buttons = buttons;
7481     
7482     if(container){
7483         this.render(container);
7484     }
7485     this.xitems = xitems;
7486     Roo.each(xitems, function(b) {
7487         this.add(b);
7488     }, this);
7489     
7490 };
7491
7492 Roo.Toolbar.prototype = {
7493     /**
7494      * @cfg {Array} items
7495      * array of button configs or elements to add (will be converted to a MixedCollection)
7496      */
7497     
7498     /**
7499      * @cfg {String/HTMLElement/Element} container
7500      * The id or element that will contain the toolbar
7501      */
7502     // private
7503     render : function(ct){
7504         this.el = Roo.get(ct);
7505         if(this.cls){
7506             this.el.addClass(this.cls);
7507         }
7508         // using a table allows for vertical alignment
7509         // 100% width is needed by Safari...
7510         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7511         this.tr = this.el.child("tr", true);
7512         var autoId = 0;
7513         this.items = new Roo.util.MixedCollection(false, function(o){
7514             return o.id || ("item" + (++autoId));
7515         });
7516         if(this.buttons){
7517             this.add.apply(this, this.buttons);
7518             delete this.buttons;
7519         }
7520     },
7521
7522     /**
7523      * Adds element(s) to the toolbar -- this function takes a variable number of 
7524      * arguments of mixed type and adds them to the toolbar.
7525      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7526      * <ul>
7527      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7528      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7529      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7530      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7531      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7532      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7533      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7534      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7535      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7536      * </ul>
7537      * @param {Mixed} arg2
7538      * @param {Mixed} etc.
7539      */
7540     add : function(){
7541         var a = arguments, l = a.length;
7542         for(var i = 0; i < l; i++){
7543             this._add(a[i]);
7544         }
7545     },
7546     // private..
7547     _add : function(el) {
7548         
7549         if (el.xtype) {
7550             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7551         }
7552         
7553         if (el.applyTo){ // some kind of form field
7554             return this.addField(el);
7555         } 
7556         if (el.render){ // some kind of Toolbar.Item
7557             return this.addItem(el);
7558         }
7559         if (typeof el == "string"){ // string
7560             if(el == "separator" || el == "-"){
7561                 return this.addSeparator();
7562             }
7563             if (el == " "){
7564                 return this.addSpacer();
7565             }
7566             if(el == "->"){
7567                 return this.addFill();
7568             }
7569             return this.addText(el);
7570             
7571         }
7572         if(el.tagName){ // element
7573             return this.addElement(el);
7574         }
7575         if(typeof el == "object"){ // must be button config?
7576             return this.addButton(el);
7577         }
7578         // and now what?!?!
7579         return false;
7580         
7581     },
7582     
7583     /**
7584      * Add an Xtype element
7585      * @param {Object} xtype Xtype Object
7586      * @return {Object} created Object
7587      */
7588     addxtype : function(e){
7589         return this.add(e);  
7590     },
7591     
7592     /**
7593      * Returns the Element for this toolbar.
7594      * @return {Roo.Element}
7595      */
7596     getEl : function(){
7597         return this.el;  
7598     },
7599     
7600     /**
7601      * Adds a separator
7602      * @return {Roo.Toolbar.Item} The separator item
7603      */
7604     addSeparator : function(){
7605         return this.addItem(new Roo.Toolbar.Separator());
7606     },
7607
7608     /**
7609      * Adds a spacer element
7610      * @return {Roo.Toolbar.Spacer} The spacer item
7611      */
7612     addSpacer : function(){
7613         return this.addItem(new Roo.Toolbar.Spacer());
7614     },
7615
7616     /**
7617      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7618      * @return {Roo.Toolbar.Fill} The fill item
7619      */
7620     addFill : function(){
7621         return this.addItem(new Roo.Toolbar.Fill());
7622     },
7623
7624     /**
7625      * Adds any standard HTML element to the toolbar
7626      * @param {String/HTMLElement/Element} el The element or id of the element to add
7627      * @return {Roo.Toolbar.Item} The element's item
7628      */
7629     addElement : function(el){
7630         return this.addItem(new Roo.Toolbar.Item(el));
7631     },
7632     /**
7633      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7634      * @type Roo.util.MixedCollection  
7635      */
7636     items : false,
7637      
7638     /**
7639      * Adds any Toolbar.Item or subclass
7640      * @param {Roo.Toolbar.Item} item
7641      * @return {Roo.Toolbar.Item} The item
7642      */
7643     addItem : function(item){
7644         var td = this.nextBlock();
7645         item.render(td);
7646         this.items.add(item);
7647         return item;
7648     },
7649     
7650     /**
7651      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7652      * @param {Object/Array} config A button config or array of configs
7653      * @return {Roo.Toolbar.Button/Array}
7654      */
7655     addButton : function(config){
7656         if(config instanceof Array){
7657             var buttons = [];
7658             for(var i = 0, len = config.length; i < len; i++) {
7659                 buttons.push(this.addButton(config[i]));
7660             }
7661             return buttons;
7662         }
7663         var b = config;
7664         if(!(config instanceof Roo.Toolbar.Button)){
7665             b = config.split ?
7666                 new Roo.Toolbar.SplitButton(config) :
7667                 new Roo.Toolbar.Button(config);
7668         }
7669         var td = this.nextBlock();
7670         b.render(td);
7671         this.items.add(b);
7672         return b;
7673     },
7674     
7675     /**
7676      * Adds text to the toolbar
7677      * @param {String} text The text to add
7678      * @return {Roo.Toolbar.Item} The element's item
7679      */
7680     addText : function(text){
7681         return this.addItem(new Roo.Toolbar.TextItem(text));
7682     },
7683     
7684     /**
7685      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7686      * @param {Number} index The index where the item is to be inserted
7687      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7688      * @return {Roo.Toolbar.Button/Item}
7689      */
7690     insertButton : function(index, item){
7691         if(item instanceof Array){
7692             var buttons = [];
7693             for(var i = 0, len = item.length; i < len; i++) {
7694                buttons.push(this.insertButton(index + i, item[i]));
7695             }
7696             return buttons;
7697         }
7698         if (!(item instanceof Roo.Toolbar.Button)){
7699            item = new Roo.Toolbar.Button(item);
7700         }
7701         var td = document.createElement("td");
7702         this.tr.insertBefore(td, this.tr.childNodes[index]);
7703         item.render(td);
7704         this.items.insert(index, item);
7705         return item;
7706     },
7707     
7708     /**
7709      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7710      * @param {Object} config
7711      * @return {Roo.Toolbar.Item} The element's item
7712      */
7713     addDom : function(config, returnEl){
7714         var td = this.nextBlock();
7715         Roo.DomHelper.overwrite(td, config);
7716         var ti = new Roo.Toolbar.Item(td.firstChild);
7717         ti.render(td);
7718         this.items.add(ti);
7719         return ti;
7720     },
7721
7722     /**
7723      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7724      * @type Roo.util.MixedCollection  
7725      */
7726     fields : false,
7727     
7728     /**
7729      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7730      * Note: the field should not have been rendered yet. For a field that has already been
7731      * rendered, use {@link #addElement}.
7732      * @param {Roo.form.Field} field
7733      * @return {Roo.ToolbarItem}
7734      */
7735      
7736       
7737     addField : function(field) {
7738         if (!this.fields) {
7739             var autoId = 0;
7740             this.fields = new Roo.util.MixedCollection(false, function(o){
7741                 return o.id || ("item" + (++autoId));
7742             });
7743
7744         }
7745         
7746         var td = this.nextBlock();
7747         field.render(td);
7748         var ti = new Roo.Toolbar.Item(td.firstChild);
7749         ti.render(td);
7750         this.items.add(ti);
7751         this.fields.add(field);
7752         return ti;
7753     },
7754     /**
7755      * Hide the toolbar
7756      * @method hide
7757      */
7758      
7759       
7760     hide : function()
7761     {
7762         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7763         this.el.child('div').hide();
7764     },
7765     /**
7766      * Show the toolbar
7767      * @method show
7768      */
7769     show : function()
7770     {
7771         this.el.child('div').show();
7772     },
7773       
7774     // private
7775     nextBlock : function(){
7776         var td = document.createElement("td");
7777         this.tr.appendChild(td);
7778         return td;
7779     },
7780
7781     // private
7782     destroy : function(){
7783         if(this.items){ // rendered?
7784             Roo.destroy.apply(Roo, this.items.items);
7785         }
7786         if(this.fields){ // rendered?
7787             Roo.destroy.apply(Roo, this.fields.items);
7788         }
7789         Roo.Element.uncache(this.el, this.tr);
7790     }
7791 };
7792
7793 /**
7794  * @class Roo.Toolbar.Item
7795  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7796  * @constructor
7797  * Creates a new Item
7798  * @param {HTMLElement} el 
7799  */
7800 Roo.Toolbar.Item = function(el){
7801     var cfg = {};
7802     if (typeof (el.xtype) != 'undefined') {
7803         cfg = el;
7804         el = cfg.el;
7805     }
7806     
7807     this.el = Roo.getDom(el);
7808     this.id = Roo.id(this.el);
7809     this.hidden = false;
7810     
7811     this.addEvents({
7812          /**
7813              * @event render
7814              * Fires when the button is rendered
7815              * @param {Button} this
7816              */
7817         'render': true
7818     });
7819     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7820 };
7821 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7822 //Roo.Toolbar.Item.prototype = {
7823     
7824     /**
7825      * Get this item's HTML Element
7826      * @return {HTMLElement}
7827      */
7828     getEl : function(){
7829        return this.el;  
7830     },
7831
7832     // private
7833     render : function(td){
7834         
7835          this.td = td;
7836         td.appendChild(this.el);
7837         
7838         this.fireEvent('render', this);
7839     },
7840     
7841     /**
7842      * Removes and destroys this item.
7843      */
7844     destroy : function(){
7845         this.td.parentNode.removeChild(this.td);
7846     },
7847     
7848     /**
7849      * Shows this item.
7850      */
7851     show: function(){
7852         this.hidden = false;
7853         this.td.style.display = "";
7854     },
7855     
7856     /**
7857      * Hides this item.
7858      */
7859     hide: function(){
7860         this.hidden = true;
7861         this.td.style.display = "none";
7862     },
7863     
7864     /**
7865      * Convenience function for boolean show/hide.
7866      * @param {Boolean} visible true to show/false to hide
7867      */
7868     setVisible: function(visible){
7869         if(visible) {
7870             this.show();
7871         }else{
7872             this.hide();
7873         }
7874     },
7875     
7876     /**
7877      * Try to focus this item.
7878      */
7879     focus : function(){
7880         Roo.fly(this.el).focus();
7881     },
7882     
7883     /**
7884      * Disables this item.
7885      */
7886     disable : function(){
7887         Roo.fly(this.td).addClass("x-item-disabled");
7888         this.disabled = true;
7889         this.el.disabled = true;
7890     },
7891     
7892     /**
7893      * Enables this item.
7894      */
7895     enable : function(){
7896         Roo.fly(this.td).removeClass("x-item-disabled");
7897         this.disabled = false;
7898         this.el.disabled = false;
7899     }
7900 });
7901
7902
7903 /**
7904  * @class Roo.Toolbar.Separator
7905  * @extends Roo.Toolbar.Item
7906  * A simple toolbar separator class
7907  * @constructor
7908  * Creates a new Separator
7909  */
7910 Roo.Toolbar.Separator = function(cfg){
7911     
7912     var s = document.createElement("span");
7913     s.className = "ytb-sep";
7914     if (cfg) {
7915         cfg.el = s;
7916     }
7917     
7918     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7919 };
7920 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7921     enable:Roo.emptyFn,
7922     disable:Roo.emptyFn,
7923     focus:Roo.emptyFn
7924 });
7925
7926 /**
7927  * @class Roo.Toolbar.Spacer
7928  * @extends Roo.Toolbar.Item
7929  * A simple element that adds extra horizontal space to a toolbar.
7930  * @constructor
7931  * Creates a new Spacer
7932  */
7933 Roo.Toolbar.Spacer = function(cfg){
7934     var s = document.createElement("div");
7935     s.className = "ytb-spacer";
7936     if (cfg) {
7937         cfg.el = s;
7938     }
7939     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7940 };
7941 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7942     enable:Roo.emptyFn,
7943     disable:Roo.emptyFn,
7944     focus:Roo.emptyFn
7945 });
7946
7947 /**
7948  * @class Roo.Toolbar.Fill
7949  * @extends Roo.Toolbar.Spacer
7950  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7951  * @constructor
7952  * Creates a new Spacer
7953  */
7954 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7955     // private
7956     render : function(td){
7957         td.style.width = '100%';
7958         Roo.Toolbar.Fill.superclass.render.call(this, td);
7959     }
7960 });
7961
7962 /**
7963  * @class Roo.Toolbar.TextItem
7964  * @extends Roo.Toolbar.Item
7965  * A simple class that renders text directly into a toolbar.
7966  * @constructor
7967  * Creates a new TextItem
7968  * @param {String} text
7969  */
7970 Roo.Toolbar.TextItem = function(cfg){
7971     var  text = cfg || "";
7972     if (typeof(cfg) == 'object') {
7973         text = cfg.text || "";
7974     }  else {
7975         cfg = null;
7976     }
7977     var s = document.createElement("span");
7978     s.className = "ytb-text";
7979     s.innerHTML = text;
7980     if (cfg) {
7981         cfg.el  = s;
7982     }
7983     
7984     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7985 };
7986 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7987     
7988      
7989     enable:Roo.emptyFn,
7990     disable:Roo.emptyFn,
7991     focus:Roo.emptyFn
7992 });
7993
7994 /**
7995  * @class Roo.Toolbar.Button
7996  * @extends Roo.Button
7997  * A button that renders into a toolbar.
7998  * @constructor
7999  * Creates a new Button
8000  * @param {Object} config A standard {@link Roo.Button} config object
8001  */
8002 Roo.Toolbar.Button = function(config){
8003     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
8004 };
8005 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
8006     render : function(td){
8007         this.td = td;
8008         Roo.Toolbar.Button.superclass.render.call(this, td);
8009     },
8010     
8011     /**
8012      * Removes and destroys this button
8013      */
8014     destroy : function(){
8015         Roo.Toolbar.Button.superclass.destroy.call(this);
8016         this.td.parentNode.removeChild(this.td);
8017     },
8018     
8019     /**
8020      * Shows this button
8021      */
8022     show: function(){
8023         this.hidden = false;
8024         this.td.style.display = "";
8025     },
8026     
8027     /**
8028      * Hides this button
8029      */
8030     hide: function(){
8031         this.hidden = true;
8032         this.td.style.display = "none";
8033     },
8034
8035     /**
8036      * Disables this item
8037      */
8038     disable : function(){
8039         Roo.fly(this.td).addClass("x-item-disabled");
8040         this.disabled = true;
8041     },
8042
8043     /**
8044      * Enables this item
8045      */
8046     enable : function(){
8047         Roo.fly(this.td).removeClass("x-item-disabled");
8048         this.disabled = false;
8049     }
8050 });
8051 // backwards compat
8052 Roo.ToolbarButton = Roo.Toolbar.Button;
8053
8054 /**
8055  * @class Roo.Toolbar.SplitButton
8056  * @extends Roo.SplitButton
8057  * A menu button that renders into a toolbar.
8058  * @constructor
8059  * Creates a new SplitButton
8060  * @param {Object} config A standard {@link Roo.SplitButton} config object
8061  */
8062 Roo.Toolbar.SplitButton = function(config){
8063     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8064 };
8065 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8066     render : function(td){
8067         this.td = td;
8068         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8069     },
8070     
8071     /**
8072      * Removes and destroys this button
8073      */
8074     destroy : function(){
8075         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8076         this.td.parentNode.removeChild(this.td);
8077     },
8078     
8079     /**
8080      * Shows this button
8081      */
8082     show: function(){
8083         this.hidden = false;
8084         this.td.style.display = "";
8085     },
8086     
8087     /**
8088      * Hides this button
8089      */
8090     hide: function(){
8091         this.hidden = true;
8092         this.td.style.display = "none";
8093     }
8094 });
8095
8096 // backwards compat
8097 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8098  * Based on:
8099  * Ext JS Library 1.1.1
8100  * Copyright(c) 2006-2007, Ext JS, LLC.
8101  *
8102  * Originally Released Under LGPL - original licence link has changed is not relivant.
8103  *
8104  * Fork - LGPL
8105  * <script type="text/javascript">
8106  */
8107  
8108 /**
8109  * @class Roo.PagingToolbar
8110  * @extends Roo.Toolbar
8111  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8112  * @constructor
8113  * Create a new PagingToolbar
8114  * @param {Object} config The config object
8115  */
8116 Roo.PagingToolbar = function(el, ds, config)
8117 {
8118     // old args format still supported... - xtype is prefered..
8119     if (typeof(el) == 'object' && el.xtype) {
8120         // created from xtype...
8121         config = el;
8122         ds = el.dataSource;
8123         el = config.container;
8124     }
8125     var items = [];
8126     if (config.items) {
8127         items = config.items;
8128         config.items = [];
8129     }
8130     
8131     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8132     this.ds = ds;
8133     this.cursor = 0;
8134     this.renderButtons(this.el);
8135     this.bind(ds);
8136     
8137     // supprot items array.
8138    
8139     Roo.each(items, function(e) {
8140         this.add(Roo.factory(e));
8141     },this);
8142     
8143 };
8144
8145 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8146     /**
8147      * @cfg {Roo.data.Store} dataSource
8148      * The underlying data store providing the paged data
8149      */
8150     /**
8151      * @cfg {String/HTMLElement/Element} container
8152      * container The id or element that will contain the toolbar
8153      */
8154     /**
8155      * @cfg {Boolean} displayInfo
8156      * True to display the displayMsg (defaults to false)
8157      */
8158     /**
8159      * @cfg {Number} pageSize
8160      * The number of records to display per page (defaults to 20)
8161      */
8162     pageSize: 20,
8163     /**
8164      * @cfg {String} displayMsg
8165      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8166      */
8167     displayMsg : 'Displaying {0} - {1} of {2}',
8168     /**
8169      * @cfg {String} emptyMsg
8170      * The message to display when no records are found (defaults to "No data to display")
8171      */
8172     emptyMsg : 'No data to display',
8173     /**
8174      * Customizable piece of the default paging text (defaults to "Page")
8175      * @type String
8176      */
8177     beforePageText : "Page",
8178     /**
8179      * Customizable piece of the default paging text (defaults to "of %0")
8180      * @type String
8181      */
8182     afterPageText : "of {0}",
8183     /**
8184      * Customizable piece of the default paging text (defaults to "First Page")
8185      * @type String
8186      */
8187     firstText : "First Page",
8188     /**
8189      * Customizable piece of the default paging text (defaults to "Previous Page")
8190      * @type String
8191      */
8192     prevText : "Previous Page",
8193     /**
8194      * Customizable piece of the default paging text (defaults to "Next Page")
8195      * @type String
8196      */
8197     nextText : "Next Page",
8198     /**
8199      * Customizable piece of the default paging text (defaults to "Last Page")
8200      * @type String
8201      */
8202     lastText : "Last Page",
8203     /**
8204      * Customizable piece of the default paging text (defaults to "Refresh")
8205      * @type String
8206      */
8207     refreshText : "Refresh",
8208
8209     // private
8210     renderButtons : function(el){
8211         Roo.PagingToolbar.superclass.render.call(this, el);
8212         this.first = this.addButton({
8213             tooltip: this.firstText,
8214             cls: "x-btn-icon x-grid-page-first",
8215             disabled: true,
8216             handler: this.onClick.createDelegate(this, ["first"])
8217         });
8218         this.prev = this.addButton({
8219             tooltip: this.prevText,
8220             cls: "x-btn-icon x-grid-page-prev",
8221             disabled: true,
8222             handler: this.onClick.createDelegate(this, ["prev"])
8223         });
8224         //this.addSeparator();
8225         this.add(this.beforePageText);
8226         this.field = Roo.get(this.addDom({
8227            tag: "input",
8228            type: "text",
8229            size: "3",
8230            value: "1",
8231            cls: "x-grid-page-number"
8232         }).el);
8233         this.field.on("keydown", this.onPagingKeydown, this);
8234         this.field.on("focus", function(){this.dom.select();});
8235         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8236         this.field.setHeight(18);
8237         //this.addSeparator();
8238         this.next = this.addButton({
8239             tooltip: this.nextText,
8240             cls: "x-btn-icon x-grid-page-next",
8241             disabled: true,
8242             handler: this.onClick.createDelegate(this, ["next"])
8243         });
8244         this.last = this.addButton({
8245             tooltip: this.lastText,
8246             cls: "x-btn-icon x-grid-page-last",
8247             disabled: true,
8248             handler: this.onClick.createDelegate(this, ["last"])
8249         });
8250         //this.addSeparator();
8251         this.loading = this.addButton({
8252             tooltip: this.refreshText,
8253             cls: "x-btn-icon x-grid-loading",
8254             handler: this.onClick.createDelegate(this, ["refresh"])
8255         });
8256
8257         if(this.displayInfo){
8258             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8259         }
8260     },
8261
8262     // private
8263     updateInfo : function(){
8264         if(this.displayEl){
8265             var count = this.ds.getCount();
8266             var msg = count == 0 ?
8267                 this.emptyMsg :
8268                 String.format(
8269                     this.displayMsg,
8270                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8271                 );
8272             this.displayEl.update(msg);
8273         }
8274     },
8275
8276     // private
8277     onLoad : function(ds, r, o){
8278        this.cursor = o.params ? o.params.start : 0;
8279        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8280
8281        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8282        this.field.dom.value = ap;
8283        this.first.setDisabled(ap == 1);
8284        this.prev.setDisabled(ap == 1);
8285        this.next.setDisabled(ap == ps);
8286        this.last.setDisabled(ap == ps);
8287        this.loading.enable();
8288        this.updateInfo();
8289     },
8290
8291     // private
8292     getPageData : function(){
8293         var total = this.ds.getTotalCount();
8294         return {
8295             total : total,
8296             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8297             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8298         };
8299     },
8300
8301     // private
8302     onLoadError : function(){
8303         this.loading.enable();
8304     },
8305
8306     // private
8307     onPagingKeydown : function(e){
8308         var k = e.getKey();
8309         var d = this.getPageData();
8310         if(k == e.RETURN){
8311             var v = this.field.dom.value, pageNum;
8312             if(!v || isNaN(pageNum = parseInt(v, 10))){
8313                 this.field.dom.value = d.activePage;
8314                 return;
8315             }
8316             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8317             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8318             e.stopEvent();
8319         }
8320         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))
8321         {
8322           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8323           this.field.dom.value = pageNum;
8324           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8325           e.stopEvent();
8326         }
8327         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8328         {
8329           var v = this.field.dom.value, pageNum; 
8330           var increment = (e.shiftKey) ? 10 : 1;
8331           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8332             increment *= -1;
8333           }
8334           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8335             this.field.dom.value = d.activePage;
8336             return;
8337           }
8338           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8339           {
8340             this.field.dom.value = parseInt(v, 10) + increment;
8341             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8342             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8343           }
8344           e.stopEvent();
8345         }
8346     },
8347
8348     // private
8349     beforeLoad : function(){
8350         if(this.loading){
8351             this.loading.disable();
8352         }
8353     },
8354
8355     // private
8356     onClick : function(which){
8357         var ds = this.ds;
8358         switch(which){
8359             case "first":
8360                 ds.load({params:{start: 0, limit: this.pageSize}});
8361             break;
8362             case "prev":
8363                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8364             break;
8365             case "next":
8366                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8367             break;
8368             case "last":
8369                 var total = ds.getTotalCount();
8370                 var extra = total % this.pageSize;
8371                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8372                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8373             break;
8374             case "refresh":
8375                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8376             break;
8377         }
8378     },
8379
8380     /**
8381      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8382      * @param {Roo.data.Store} store The data store to unbind
8383      */
8384     unbind : function(ds){
8385         ds.un("beforeload", this.beforeLoad, this);
8386         ds.un("load", this.onLoad, this);
8387         ds.un("loadexception", this.onLoadError, this);
8388         ds.un("remove", this.updateInfo, this);
8389         ds.un("add", this.updateInfo, this);
8390         this.ds = undefined;
8391     },
8392
8393     /**
8394      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8395      * @param {Roo.data.Store} store The data store to bind
8396      */
8397     bind : function(ds){
8398         ds.on("beforeload", this.beforeLoad, this);
8399         ds.on("load", this.onLoad, this);
8400         ds.on("loadexception", this.onLoadError, this);
8401         ds.on("remove", this.updateInfo, this);
8402         ds.on("add", this.updateInfo, this);
8403         this.ds = ds;
8404     }
8405 });/*
8406  * Based on:
8407  * Ext JS Library 1.1.1
8408  * Copyright(c) 2006-2007, Ext JS, LLC.
8409  *
8410  * Originally Released Under LGPL - original licence link has changed is not relivant.
8411  *
8412  * Fork - LGPL
8413  * <script type="text/javascript">
8414  */
8415
8416 /**
8417  * @class Roo.Resizable
8418  * @extends Roo.util.Observable
8419  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8420  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8421  * 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
8422  * the element will be wrapped for you automatically.</p>
8423  * <p>Here is the list of valid resize handles:</p>
8424  * <pre>
8425 Value   Description
8426 ------  -------------------
8427  'n'     north
8428  's'     south
8429  'e'     east
8430  'w'     west
8431  'nw'    northwest
8432  'sw'    southwest
8433  'se'    southeast
8434  'ne'    northeast
8435  'hd'    horizontal drag
8436  'all'   all
8437 </pre>
8438  * <p>Here's an example showing the creation of a typical Resizable:</p>
8439  * <pre><code>
8440 var resizer = new Roo.Resizable("element-id", {
8441     handles: 'all',
8442     minWidth: 200,
8443     minHeight: 100,
8444     maxWidth: 500,
8445     maxHeight: 400,
8446     pinned: true
8447 });
8448 resizer.on("resize", myHandler);
8449 </code></pre>
8450  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8451  * resizer.east.setDisplayed(false);</p>
8452  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8453  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8454  * resize operation's new size (defaults to [0, 0])
8455  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8456  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8457  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8458  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8459  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8460  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8461  * @cfg {Number} width The width of the element in pixels (defaults to null)
8462  * @cfg {Number} height The height of the element in pixels (defaults to null)
8463  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8464  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8465  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8466  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8467  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8468  * in favor of the handles config option (defaults to false)
8469  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8470  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8471  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8472  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8473  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8474  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8475  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8476  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8477  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8478  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8479  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8480  * @constructor
8481  * Create a new resizable component
8482  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8483  * @param {Object} config configuration options
8484   */
8485 Roo.Resizable = function(el, config)
8486 {
8487     this.el = Roo.get(el);
8488
8489     if(config && config.wrap){
8490         config.resizeChild = this.el;
8491         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8492         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8493         this.el.setStyle("overflow", "hidden");
8494         this.el.setPositioning(config.resizeChild.getPositioning());
8495         config.resizeChild.clearPositioning();
8496         if(!config.width || !config.height){
8497             var csize = config.resizeChild.getSize();
8498             this.el.setSize(csize.width, csize.height);
8499         }
8500         if(config.pinned && !config.adjustments){
8501             config.adjustments = "auto";
8502         }
8503     }
8504
8505     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8506     this.proxy.unselectable();
8507     this.proxy.enableDisplayMode('block');
8508
8509     Roo.apply(this, config);
8510
8511     if(this.pinned){
8512         this.disableTrackOver = true;
8513         this.el.addClass("x-resizable-pinned");
8514     }
8515     // if the element isn't positioned, make it relative
8516     var position = this.el.getStyle("position");
8517     if(position != "absolute" && position != "fixed"){
8518         this.el.setStyle("position", "relative");
8519     }
8520     if(!this.handles){ // no handles passed, must be legacy style
8521         this.handles = 's,e,se';
8522         if(this.multiDirectional){
8523             this.handles += ',n,w';
8524         }
8525     }
8526     if(this.handles == "all"){
8527         this.handles = "n s e w ne nw se sw";
8528     }
8529     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8530     var ps = Roo.Resizable.positions;
8531     for(var i = 0, len = hs.length; i < len; i++){
8532         if(hs[i] && ps[hs[i]]){
8533             var pos = ps[hs[i]];
8534             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8535         }
8536     }
8537     // legacy
8538     this.corner = this.southeast;
8539     
8540     // updateBox = the box can move..
8541     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8542         this.updateBox = true;
8543     }
8544
8545     this.activeHandle = null;
8546
8547     if(this.resizeChild){
8548         if(typeof this.resizeChild == "boolean"){
8549             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8550         }else{
8551             this.resizeChild = Roo.get(this.resizeChild, true);
8552         }
8553     }
8554     
8555     if(this.adjustments == "auto"){
8556         var rc = this.resizeChild;
8557         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8558         if(rc && (hw || hn)){
8559             rc.position("relative");
8560             rc.setLeft(hw ? hw.el.getWidth() : 0);
8561             rc.setTop(hn ? hn.el.getHeight() : 0);
8562         }
8563         this.adjustments = [
8564             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8565             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8566         ];
8567     }
8568
8569     if(this.draggable){
8570         this.dd = this.dynamic ?
8571             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8572         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8573     }
8574
8575     // public events
8576     this.addEvents({
8577         /**
8578          * @event beforeresize
8579          * Fired before resize is allowed. Set enabled to false to cancel resize.
8580          * @param {Roo.Resizable} this
8581          * @param {Roo.EventObject} e The mousedown event
8582          */
8583         "beforeresize" : true,
8584         /**
8585          * @event resizing
8586          * Fired a resizing.
8587          * @param {Roo.Resizable} this
8588          * @param {Number} x The new x position
8589          * @param {Number} y The new y position
8590          * @param {Number} w The new w width
8591          * @param {Number} h The new h hight
8592          * @param {Roo.EventObject} e The mouseup event
8593          */
8594         "resizing" : true,
8595         /**
8596          * @event resize
8597          * Fired after a resize.
8598          * @param {Roo.Resizable} this
8599          * @param {Number} width The new width
8600          * @param {Number} height The new height
8601          * @param {Roo.EventObject} e The mouseup event
8602          */
8603         "resize" : true
8604     });
8605
8606     if(this.width !== null && this.height !== null){
8607         this.resizeTo(this.width, this.height);
8608     }else{
8609         this.updateChildSize();
8610     }
8611     if(Roo.isIE){
8612         this.el.dom.style.zoom = 1;
8613     }
8614     Roo.Resizable.superclass.constructor.call(this);
8615 };
8616
8617 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8618         resizeChild : false,
8619         adjustments : [0, 0],
8620         minWidth : 5,
8621         minHeight : 5,
8622         maxWidth : 10000,
8623         maxHeight : 10000,
8624         enabled : true,
8625         animate : false,
8626         duration : .35,
8627         dynamic : false,
8628         handles : false,
8629         multiDirectional : false,
8630         disableTrackOver : false,
8631         easing : 'easeOutStrong',
8632         widthIncrement : 0,
8633         heightIncrement : 0,
8634         pinned : false,
8635         width : null,
8636         height : null,
8637         preserveRatio : false,
8638         transparent: false,
8639         minX: 0,
8640         minY: 0,
8641         draggable: false,
8642
8643         /**
8644          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8645          */
8646         constrainTo: undefined,
8647         /**
8648          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8649          */
8650         resizeRegion: undefined,
8651
8652
8653     /**
8654      * Perform a manual resize
8655      * @param {Number} width
8656      * @param {Number} height
8657      */
8658     resizeTo : function(width, height){
8659         this.el.setSize(width, height);
8660         this.updateChildSize();
8661         this.fireEvent("resize", this, width, height, null);
8662     },
8663
8664     // private
8665     startSizing : function(e, handle){
8666         this.fireEvent("beforeresize", this, e);
8667         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8668
8669             if(!this.overlay){
8670                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8671                 this.overlay.unselectable();
8672                 this.overlay.enableDisplayMode("block");
8673                 this.overlay.on("mousemove", this.onMouseMove, this);
8674                 this.overlay.on("mouseup", this.onMouseUp, this);
8675             }
8676             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8677
8678             this.resizing = true;
8679             this.startBox = this.el.getBox();
8680             this.startPoint = e.getXY();
8681             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8682                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8683
8684             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8685             this.overlay.show();
8686
8687             if(this.constrainTo) {
8688                 var ct = Roo.get(this.constrainTo);
8689                 this.resizeRegion = ct.getRegion().adjust(
8690                     ct.getFrameWidth('t'),
8691                     ct.getFrameWidth('l'),
8692                     -ct.getFrameWidth('b'),
8693                     -ct.getFrameWidth('r')
8694                 );
8695             }
8696
8697             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8698             this.proxy.show();
8699             this.proxy.setBox(this.startBox);
8700             if(!this.dynamic){
8701                 this.proxy.setStyle('visibility', 'visible');
8702             }
8703         }
8704     },
8705
8706     // private
8707     onMouseDown : function(handle, e){
8708         if(this.enabled){
8709             e.stopEvent();
8710             this.activeHandle = handle;
8711             this.startSizing(e, handle);
8712         }
8713     },
8714
8715     // private
8716     onMouseUp : function(e){
8717         var size = this.resizeElement();
8718         this.resizing = false;
8719         this.handleOut();
8720         this.overlay.hide();
8721         this.proxy.hide();
8722         this.fireEvent("resize", this, size.width, size.height, e);
8723     },
8724
8725     // private
8726     updateChildSize : function(){
8727         
8728         if(this.resizeChild){
8729             var el = this.el;
8730             var child = this.resizeChild;
8731             var adj = this.adjustments;
8732             if(el.dom.offsetWidth){
8733                 var b = el.getSize(true);
8734                 child.setSize(b.width+adj[0], b.height+adj[1]);
8735             }
8736             // Second call here for IE
8737             // The first call enables instant resizing and
8738             // the second call corrects scroll bars if they
8739             // exist
8740             if(Roo.isIE){
8741                 setTimeout(function(){
8742                     if(el.dom.offsetWidth){
8743                         var b = el.getSize(true);
8744                         child.setSize(b.width+adj[0], b.height+adj[1]);
8745                     }
8746                 }, 10);
8747             }
8748         }
8749     },
8750
8751     // private
8752     snap : function(value, inc, min){
8753         if(!inc || !value) {
8754             return value;
8755         }
8756         var newValue = value;
8757         var m = value % inc;
8758         if(m > 0){
8759             if(m > (inc/2)){
8760                 newValue = value + (inc-m);
8761             }else{
8762                 newValue = value - m;
8763             }
8764         }
8765         return Math.max(min, newValue);
8766     },
8767
8768     // private
8769     resizeElement : function(){
8770         var box = this.proxy.getBox();
8771         if(this.updateBox){
8772             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8773         }else{
8774             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8775         }
8776         this.updateChildSize();
8777         if(!this.dynamic){
8778             this.proxy.hide();
8779         }
8780         return box;
8781     },
8782
8783     // private
8784     constrain : function(v, diff, m, mx){
8785         if(v - diff < m){
8786             diff = v - m;
8787         }else if(v - diff > mx){
8788             diff = mx - v;
8789         }
8790         return diff;
8791     },
8792
8793     // private
8794     onMouseMove : function(e){
8795         
8796         if(this.enabled){
8797             try{// try catch so if something goes wrong the user doesn't get hung
8798
8799             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8800                 return;
8801             }
8802
8803             //var curXY = this.startPoint;
8804             var curSize = this.curSize || this.startBox;
8805             var x = this.startBox.x, y = this.startBox.y;
8806             var ox = x, oy = y;
8807             var w = curSize.width, h = curSize.height;
8808             var ow = w, oh = h;
8809             var mw = this.minWidth, mh = this.minHeight;
8810             var mxw = this.maxWidth, mxh = this.maxHeight;
8811             var wi = this.widthIncrement;
8812             var hi = this.heightIncrement;
8813
8814             var eventXY = e.getXY();
8815             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8816             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8817
8818             var pos = this.activeHandle.position;
8819
8820             switch(pos){
8821                 case "east":
8822                     w += diffX;
8823                     w = Math.min(Math.max(mw, w), mxw);
8824                     break;
8825              
8826                 case "south":
8827                     h += diffY;
8828                     h = Math.min(Math.max(mh, h), mxh);
8829                     break;
8830                 case "southeast":
8831                     w += diffX;
8832                     h += diffY;
8833                     w = Math.min(Math.max(mw, w), mxw);
8834                     h = Math.min(Math.max(mh, h), mxh);
8835                     break;
8836                 case "north":
8837                     diffY = this.constrain(h, diffY, mh, mxh);
8838                     y += diffY;
8839                     h -= diffY;
8840                     break;
8841                 case "hdrag":
8842                     
8843                     if (wi) {
8844                         var adiffX = Math.abs(diffX);
8845                         var sub = (adiffX % wi); // how much 
8846                         if (sub > (wi/2)) { // far enough to snap
8847                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8848                         } else {
8849                             // remove difference.. 
8850                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8851                         }
8852                     }
8853                     x += diffX;
8854                     x = Math.max(this.minX, x);
8855                     break;
8856                 case "west":
8857                     diffX = this.constrain(w, diffX, mw, mxw);
8858                     x += diffX;
8859                     w -= diffX;
8860                     break;
8861                 case "northeast":
8862                     w += diffX;
8863                     w = Math.min(Math.max(mw, w), mxw);
8864                     diffY = this.constrain(h, diffY, mh, mxh);
8865                     y += diffY;
8866                     h -= diffY;
8867                     break;
8868                 case "northwest":
8869                     diffX = this.constrain(w, diffX, mw, mxw);
8870                     diffY = this.constrain(h, diffY, mh, mxh);
8871                     y += diffY;
8872                     h -= diffY;
8873                     x += diffX;
8874                     w -= diffX;
8875                     break;
8876                case "southwest":
8877                     diffX = this.constrain(w, diffX, mw, mxw);
8878                     h += diffY;
8879                     h = Math.min(Math.max(mh, h), mxh);
8880                     x += diffX;
8881                     w -= diffX;
8882                     break;
8883             }
8884
8885             var sw = this.snap(w, wi, mw);
8886             var sh = this.snap(h, hi, mh);
8887             if(sw != w || sh != h){
8888                 switch(pos){
8889                     case "northeast":
8890                         y -= sh - h;
8891                     break;
8892                     case "north":
8893                         y -= sh - h;
8894                         break;
8895                     case "southwest":
8896                         x -= sw - w;
8897                     break;
8898                     case "west":
8899                         x -= sw - w;
8900                         break;
8901                     case "northwest":
8902                         x -= sw - w;
8903                         y -= sh - h;
8904                     break;
8905                 }
8906                 w = sw;
8907                 h = sh;
8908             }
8909
8910             if(this.preserveRatio){
8911                 switch(pos){
8912                     case "southeast":
8913                     case "east":
8914                         h = oh * (w/ow);
8915                         h = Math.min(Math.max(mh, h), mxh);
8916                         w = ow * (h/oh);
8917                        break;
8918                     case "south":
8919                         w = ow * (h/oh);
8920                         w = Math.min(Math.max(mw, w), mxw);
8921                         h = oh * (w/ow);
8922                         break;
8923                     case "northeast":
8924                         w = ow * (h/oh);
8925                         w = Math.min(Math.max(mw, w), mxw);
8926                         h = oh * (w/ow);
8927                     break;
8928                     case "north":
8929                         var tw = w;
8930                         w = ow * (h/oh);
8931                         w = Math.min(Math.max(mw, w), mxw);
8932                         h = oh * (w/ow);
8933                         x += (tw - w) / 2;
8934                         break;
8935                     case "southwest":
8936                         h = oh * (w/ow);
8937                         h = Math.min(Math.max(mh, h), mxh);
8938                         var tw = w;
8939                         w = ow * (h/oh);
8940                         x += tw - w;
8941                         break;
8942                     case "west":
8943                         var th = h;
8944                         h = oh * (w/ow);
8945                         h = Math.min(Math.max(mh, h), mxh);
8946                         y += (th - h) / 2;
8947                         var tw = w;
8948                         w = ow * (h/oh);
8949                         x += tw - w;
8950                        break;
8951                     case "northwest":
8952                         var tw = w;
8953                         var th = h;
8954                         h = oh * (w/ow);
8955                         h = Math.min(Math.max(mh, h), mxh);
8956                         w = ow * (h/oh);
8957                         y += th - h;
8958                         x += tw - w;
8959                        break;
8960
8961                 }
8962             }
8963             if (pos == 'hdrag') {
8964                 w = ow;
8965             }
8966             this.proxy.setBounds(x, y, w, h);
8967             if(this.dynamic){
8968                 this.resizeElement();
8969             }
8970             }catch(e){}
8971         }
8972         this.fireEvent("resizing", this, x, y, w, h, e);
8973     },
8974
8975     // private
8976     handleOver : function(){
8977         if(this.enabled){
8978             this.el.addClass("x-resizable-over");
8979         }
8980     },
8981
8982     // private
8983     handleOut : function(){
8984         if(!this.resizing){
8985             this.el.removeClass("x-resizable-over");
8986         }
8987     },
8988
8989     /**
8990      * Returns the element this component is bound to.
8991      * @return {Roo.Element}
8992      */
8993     getEl : function(){
8994         return this.el;
8995     },
8996
8997     /**
8998      * Returns the resizeChild element (or null).
8999      * @return {Roo.Element}
9000      */
9001     getResizeChild : function(){
9002         return this.resizeChild;
9003     },
9004     groupHandler : function()
9005     {
9006         
9007     },
9008     /**
9009      * Destroys this resizable. If the element was wrapped and
9010      * removeEl is not true then the element remains.
9011      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9012      */
9013     destroy : function(removeEl){
9014         this.proxy.remove();
9015         if(this.overlay){
9016             this.overlay.removeAllListeners();
9017             this.overlay.remove();
9018         }
9019         var ps = Roo.Resizable.positions;
9020         for(var k in ps){
9021             if(typeof ps[k] != "function" && this[ps[k]]){
9022                 var h = this[ps[k]];
9023                 h.el.removeAllListeners();
9024                 h.el.remove();
9025             }
9026         }
9027         if(removeEl){
9028             this.el.update("");
9029             this.el.remove();
9030         }
9031     }
9032 });
9033
9034 // private
9035 // hash to map config positions to true positions
9036 Roo.Resizable.positions = {
9037     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9038     hd: "hdrag"
9039 };
9040
9041 // private
9042 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9043     if(!this.tpl){
9044         // only initialize the template if resizable is used
9045         var tpl = Roo.DomHelper.createTemplate(
9046             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9047         );
9048         tpl.compile();
9049         Roo.Resizable.Handle.prototype.tpl = tpl;
9050     }
9051     this.position = pos;
9052     this.rz = rz;
9053     // show north drag fro topdra
9054     var handlepos = pos == 'hdrag' ? 'north' : pos;
9055     
9056     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9057     if (pos == 'hdrag') {
9058         this.el.setStyle('cursor', 'pointer');
9059     }
9060     this.el.unselectable();
9061     if(transparent){
9062         this.el.setOpacity(0);
9063     }
9064     this.el.on("mousedown", this.onMouseDown, this);
9065     if(!disableTrackOver){
9066         this.el.on("mouseover", this.onMouseOver, this);
9067         this.el.on("mouseout", this.onMouseOut, this);
9068     }
9069 };
9070
9071 // private
9072 Roo.Resizable.Handle.prototype = {
9073     afterResize : function(rz){
9074         Roo.log('after?');
9075         // do nothing
9076     },
9077     // private
9078     onMouseDown : function(e){
9079         this.rz.onMouseDown(this, e);
9080     },
9081     // private
9082     onMouseOver : function(e){
9083         this.rz.handleOver(this, e);
9084     },
9085     // private
9086     onMouseOut : function(e){
9087         this.rz.handleOut(this, e);
9088     }
9089 };/*
9090  * Based on:
9091  * Ext JS Library 1.1.1
9092  * Copyright(c) 2006-2007, Ext JS, LLC.
9093  *
9094  * Originally Released Under LGPL - original licence link has changed is not relivant.
9095  *
9096  * Fork - LGPL
9097  * <script type="text/javascript">
9098  */
9099
9100 /**
9101  * @class Roo.Editor
9102  * @extends Roo.Component
9103  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9104  * @constructor
9105  * Create a new Editor
9106  * @param {Roo.form.Field} field The Field object (or descendant)
9107  * @param {Object} config The config object
9108  */
9109 Roo.Editor = function(field, config){
9110     Roo.Editor.superclass.constructor.call(this, config);
9111     this.field = field;
9112     this.addEvents({
9113         /**
9114              * @event beforestartedit
9115              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9116              * false from the handler of this event.
9117              * @param {Editor} this
9118              * @param {Roo.Element} boundEl The underlying element bound to this editor
9119              * @param {Mixed} value The field value being set
9120              */
9121         "beforestartedit" : true,
9122         /**
9123              * @event startedit
9124              * Fires when this editor is displayed
9125              * @param {Roo.Element} boundEl The underlying element bound to this editor
9126              * @param {Mixed} value The starting field value
9127              */
9128         "startedit" : true,
9129         /**
9130              * @event beforecomplete
9131              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9132              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9133              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9134              * event will not fire since no edit actually occurred.
9135              * @param {Editor} this
9136              * @param {Mixed} value The current field value
9137              * @param {Mixed} startValue The original field value
9138              */
9139         "beforecomplete" : true,
9140         /**
9141              * @event complete
9142              * Fires after editing is complete and any changed value has been written to the underlying field.
9143              * @param {Editor} this
9144              * @param {Mixed} value The current field value
9145              * @param {Mixed} startValue The original field value
9146              */
9147         "complete" : true,
9148         /**
9149          * @event specialkey
9150          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9151          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9152          * @param {Roo.form.Field} this
9153          * @param {Roo.EventObject} e The event object
9154          */
9155         "specialkey" : true
9156     });
9157 };
9158
9159 Roo.extend(Roo.Editor, Roo.Component, {
9160     /**
9161      * @cfg {Boolean/String} autosize
9162      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9163      * or "height" to adopt the height only (defaults to false)
9164      */
9165     /**
9166      * @cfg {Boolean} revertInvalid
9167      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9168      * validation fails (defaults to true)
9169      */
9170     /**
9171      * @cfg {Boolean} ignoreNoChange
9172      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9173      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9174      * will never be ignored.
9175      */
9176     /**
9177      * @cfg {Boolean} hideEl
9178      * False to keep the bound element visible while the editor is displayed (defaults to true)
9179      */
9180     /**
9181      * @cfg {Mixed} value
9182      * The data value of the underlying field (defaults to "")
9183      */
9184     value : "",
9185     /**
9186      * @cfg {String} alignment
9187      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9188      */
9189     alignment: "c-c?",
9190     /**
9191      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9192      * for bottom-right shadow (defaults to "frame")
9193      */
9194     shadow : "frame",
9195     /**
9196      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9197      */
9198     constrain : false,
9199     /**
9200      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9201      */
9202     completeOnEnter : false,
9203     /**
9204      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9205      */
9206     cancelOnEsc : false,
9207     /**
9208      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9209      */
9210     updateEl : false,
9211
9212     // private
9213     onRender : function(ct, position){
9214         this.el = new Roo.Layer({
9215             shadow: this.shadow,
9216             cls: "x-editor",
9217             parentEl : ct,
9218             shim : this.shim,
9219             shadowOffset:4,
9220             id: this.id,
9221             constrain: this.constrain
9222         });
9223         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9224         if(this.field.msgTarget != 'title'){
9225             this.field.msgTarget = 'qtip';
9226         }
9227         this.field.render(this.el);
9228         if(Roo.isGecko){
9229             this.field.el.dom.setAttribute('autocomplete', 'off');
9230         }
9231         this.field.on("specialkey", this.onSpecialKey, this);
9232         if(this.swallowKeys){
9233             this.field.el.swallowEvent(['keydown','keypress']);
9234         }
9235         this.field.show();
9236         this.field.on("blur", this.onBlur, this);
9237         if(this.field.grow){
9238             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9239         }
9240     },
9241
9242     onSpecialKey : function(field, e)
9243     {
9244         //Roo.log('editor onSpecialKey');
9245         if(this.completeOnEnter && e.getKey() == e.ENTER){
9246             e.stopEvent();
9247             this.completeEdit();
9248             return;
9249         }
9250         // do not fire special key otherwise it might hide close the editor...
9251         if(e.getKey() == e.ENTER){    
9252             return;
9253         }
9254         if(this.cancelOnEsc && e.getKey() == e.ESC){
9255             this.cancelEdit();
9256             return;
9257         } 
9258         this.fireEvent('specialkey', field, e);
9259     
9260     },
9261
9262     /**
9263      * Starts the editing process and shows the editor.
9264      * @param {String/HTMLElement/Element} el The element to edit
9265      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9266       * to the innerHTML of el.
9267      */
9268     startEdit : function(el, value){
9269         if(this.editing){
9270             this.completeEdit();
9271         }
9272         this.boundEl = Roo.get(el);
9273         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9274         if(!this.rendered){
9275             this.render(this.parentEl || document.body);
9276         }
9277         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9278             return;
9279         }
9280         this.startValue = v;
9281         this.field.setValue(v);
9282         if(this.autoSize){
9283             var sz = this.boundEl.getSize();
9284             switch(this.autoSize){
9285                 case "width":
9286                 this.setSize(sz.width,  "");
9287                 break;
9288                 case "height":
9289                 this.setSize("",  sz.height);
9290                 break;
9291                 default:
9292                 this.setSize(sz.width,  sz.height);
9293             }
9294         }
9295         this.el.alignTo(this.boundEl, this.alignment);
9296         this.editing = true;
9297         if(Roo.QuickTips){
9298             Roo.QuickTips.disable();
9299         }
9300         this.show();
9301     },
9302
9303     /**
9304      * Sets the height and width of this editor.
9305      * @param {Number} width The new width
9306      * @param {Number} height The new height
9307      */
9308     setSize : function(w, h){
9309         this.field.setSize(w, h);
9310         if(this.el){
9311             this.el.sync();
9312         }
9313     },
9314
9315     /**
9316      * Realigns the editor to the bound field based on the current alignment config value.
9317      */
9318     realign : function(){
9319         this.el.alignTo(this.boundEl, this.alignment);
9320     },
9321
9322     /**
9323      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9324      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9325      */
9326     completeEdit : function(remainVisible){
9327         if(!this.editing){
9328             return;
9329         }
9330         var v = this.getValue();
9331         if(this.revertInvalid !== false && !this.field.isValid()){
9332             v = this.startValue;
9333             this.cancelEdit(true);
9334         }
9335         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9336             this.editing = false;
9337             this.hide();
9338             return;
9339         }
9340         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9341             this.editing = false;
9342             if(this.updateEl && this.boundEl){
9343                 this.boundEl.update(v);
9344             }
9345             if(remainVisible !== true){
9346                 this.hide();
9347             }
9348             this.fireEvent("complete", this, v, this.startValue);
9349         }
9350     },
9351
9352     // private
9353     onShow : function(){
9354         this.el.show();
9355         if(this.hideEl !== false){
9356             this.boundEl.hide();
9357         }
9358         this.field.show();
9359         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9360             this.fixIEFocus = true;
9361             this.deferredFocus.defer(50, this);
9362         }else{
9363             this.field.focus();
9364         }
9365         this.fireEvent("startedit", this.boundEl, this.startValue);
9366     },
9367
9368     deferredFocus : function(){
9369         if(this.editing){
9370             this.field.focus();
9371         }
9372     },
9373
9374     /**
9375      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9376      * reverted to the original starting value.
9377      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9378      * cancel (defaults to false)
9379      */
9380     cancelEdit : function(remainVisible){
9381         if(this.editing){
9382             this.setValue(this.startValue);
9383             if(remainVisible !== true){
9384                 this.hide();
9385             }
9386         }
9387     },
9388
9389     // private
9390     onBlur : function(){
9391         if(this.allowBlur !== true && this.editing){
9392             this.completeEdit();
9393         }
9394     },
9395
9396     // private
9397     onHide : function(){
9398         if(this.editing){
9399             this.completeEdit();
9400             return;
9401         }
9402         this.field.blur();
9403         if(this.field.collapse){
9404             this.field.collapse();
9405         }
9406         this.el.hide();
9407         if(this.hideEl !== false){
9408             this.boundEl.show();
9409         }
9410         if(Roo.QuickTips){
9411             Roo.QuickTips.enable();
9412         }
9413     },
9414
9415     /**
9416      * Sets the data value of the editor
9417      * @param {Mixed} value Any valid value supported by the underlying field
9418      */
9419     setValue : function(v){
9420         this.field.setValue(v);
9421     },
9422
9423     /**
9424      * Gets the data value of the editor
9425      * @return {Mixed} The data value
9426      */
9427     getValue : function(){
9428         return this.field.getValue();
9429     }
9430 });/*
9431  * Based on:
9432  * Ext JS Library 1.1.1
9433  * Copyright(c) 2006-2007, Ext JS, LLC.
9434  *
9435  * Originally Released Under LGPL - original licence link has changed is not relivant.
9436  *
9437  * Fork - LGPL
9438  * <script type="text/javascript">
9439  */
9440  
9441 /**
9442  * @class Roo.BasicDialog
9443  * @extends Roo.util.Observable
9444  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9445  * <pre><code>
9446 var dlg = new Roo.BasicDialog("my-dlg", {
9447     height: 200,
9448     width: 300,
9449     minHeight: 100,
9450     minWidth: 150,
9451     modal: true,
9452     proxyDrag: true,
9453     shadow: true
9454 });
9455 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9456 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9457 dlg.addButton('Cancel', dlg.hide, dlg);
9458 dlg.show();
9459 </code></pre>
9460   <b>A Dialog should always be a direct child of the body element.</b>
9461  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9462  * @cfg {String} title Default text to display in the title bar (defaults to null)
9463  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9464  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9465  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9466  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9467  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9468  * (defaults to null with no animation)
9469  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9470  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9471  * property for valid values (defaults to 'all')
9472  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9473  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9474  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9475  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9476  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9477  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9478  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9479  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9480  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9481  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9482  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9483  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9484  * draggable = true (defaults to false)
9485  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9486  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9487  * shadow (defaults to false)
9488  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9489  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9490  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9491  * @cfg {Array} buttons Array of buttons
9492  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9493  * @constructor
9494  * Create a new BasicDialog.
9495  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9496  * @param {Object} config Configuration options
9497  */
9498 Roo.BasicDialog = function(el, config){
9499     this.el = Roo.get(el);
9500     var dh = Roo.DomHelper;
9501     if(!this.el && config && config.autoCreate){
9502         if(typeof config.autoCreate == "object"){
9503             if(!config.autoCreate.id){
9504                 config.autoCreate.id = el;
9505             }
9506             this.el = dh.append(document.body,
9507                         config.autoCreate, true);
9508         }else{
9509             this.el = dh.append(document.body,
9510                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9511         }
9512     }
9513     el = this.el;
9514     el.setDisplayed(true);
9515     el.hide = this.hideAction;
9516     this.id = el.id;
9517     el.addClass("x-dlg");
9518
9519     Roo.apply(this, config);
9520
9521     this.proxy = el.createProxy("x-dlg-proxy");
9522     this.proxy.hide = this.hideAction;
9523     this.proxy.setOpacity(.5);
9524     this.proxy.hide();
9525
9526     if(config.width){
9527         el.setWidth(config.width);
9528     }
9529     if(config.height){
9530         el.setHeight(config.height);
9531     }
9532     this.size = el.getSize();
9533     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9534         this.xy = [config.x,config.y];
9535     }else{
9536         this.xy = el.getCenterXY(true);
9537     }
9538     /** The header element @type Roo.Element */
9539     this.header = el.child("> .x-dlg-hd");
9540     /** The body element @type Roo.Element */
9541     this.body = el.child("> .x-dlg-bd");
9542     /** The footer element @type Roo.Element */
9543     this.footer = el.child("> .x-dlg-ft");
9544
9545     if(!this.header){
9546         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9547     }
9548     if(!this.body){
9549         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9550     }
9551
9552     this.header.unselectable();
9553     if(this.title){
9554         this.header.update(this.title);
9555     }
9556     // this element allows the dialog to be focused for keyboard event
9557     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9558     this.focusEl.swallowEvent("click", true);
9559
9560     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9561
9562     // wrap the body and footer for special rendering
9563     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9564     if(this.footer){
9565         this.bwrap.dom.appendChild(this.footer.dom);
9566     }
9567
9568     this.bg = this.el.createChild({
9569         tag: "div", cls:"x-dlg-bg",
9570         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9571     });
9572     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9573
9574
9575     if(this.autoScroll !== false && !this.autoTabs){
9576         this.body.setStyle("overflow", "auto");
9577     }
9578
9579     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9580
9581     if(this.closable !== false){
9582         this.el.addClass("x-dlg-closable");
9583         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9584         this.close.on("click", this.closeClick, this);
9585         this.close.addClassOnOver("x-dlg-close-over");
9586     }
9587     if(this.collapsible !== false){
9588         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9589         this.collapseBtn.on("click", this.collapseClick, this);
9590         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9591         this.header.on("dblclick", this.collapseClick, this);
9592     }
9593     if(this.resizable !== false){
9594         this.el.addClass("x-dlg-resizable");
9595         this.resizer = new Roo.Resizable(el, {
9596             minWidth: this.minWidth || 80,
9597             minHeight:this.minHeight || 80,
9598             handles: this.resizeHandles || "all",
9599             pinned: true
9600         });
9601         this.resizer.on("beforeresize", this.beforeResize, this);
9602         this.resizer.on("resize", this.onResize, this);
9603     }
9604     if(this.draggable !== false){
9605         el.addClass("x-dlg-draggable");
9606         if (!this.proxyDrag) {
9607             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9608         }
9609         else {
9610             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9611         }
9612         dd.setHandleElId(this.header.id);
9613         dd.endDrag = this.endMove.createDelegate(this);
9614         dd.startDrag = this.startMove.createDelegate(this);
9615         dd.onDrag = this.onDrag.createDelegate(this);
9616         dd.scroll = false;
9617         this.dd = dd;
9618     }
9619     if(this.modal){
9620         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9621         this.mask.enableDisplayMode("block");
9622         this.mask.hide();
9623         this.el.addClass("x-dlg-modal");
9624     }
9625     if(this.shadow){
9626         this.shadow = new Roo.Shadow({
9627             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9628             offset : this.shadowOffset
9629         });
9630     }else{
9631         this.shadowOffset = 0;
9632     }
9633     if(Roo.useShims && this.shim !== false){
9634         this.shim = this.el.createShim();
9635         this.shim.hide = this.hideAction;
9636         this.shim.hide();
9637     }else{
9638         this.shim = false;
9639     }
9640     if(this.autoTabs){
9641         this.initTabs();
9642     }
9643     if (this.buttons) { 
9644         var bts= this.buttons;
9645         this.buttons = [];
9646         Roo.each(bts, function(b) {
9647             this.addButton(b);
9648         }, this);
9649     }
9650     
9651     
9652     this.addEvents({
9653         /**
9654          * @event keydown
9655          * Fires when a key is pressed
9656          * @param {Roo.BasicDialog} this
9657          * @param {Roo.EventObject} e
9658          */
9659         "keydown" : true,
9660         /**
9661          * @event move
9662          * Fires when this dialog is moved by the user.
9663          * @param {Roo.BasicDialog} this
9664          * @param {Number} x The new page X
9665          * @param {Number} y The new page Y
9666          */
9667         "move" : true,
9668         /**
9669          * @event resize
9670          * Fires when this dialog is resized by the user.
9671          * @param {Roo.BasicDialog} this
9672          * @param {Number} width The new width
9673          * @param {Number} height The new height
9674          */
9675         "resize" : true,
9676         /**
9677          * @event beforehide
9678          * Fires before this dialog is hidden.
9679          * @param {Roo.BasicDialog} this
9680          */
9681         "beforehide" : true,
9682         /**
9683          * @event hide
9684          * Fires when this dialog is hidden.
9685          * @param {Roo.BasicDialog} this
9686          */
9687         "hide" : true,
9688         /**
9689          * @event beforeshow
9690          * Fires before this dialog is shown.
9691          * @param {Roo.BasicDialog} this
9692          */
9693         "beforeshow" : true,
9694         /**
9695          * @event show
9696          * Fires when this dialog is shown.
9697          * @param {Roo.BasicDialog} this
9698          */
9699         "show" : true
9700     });
9701     el.on("keydown", this.onKeyDown, this);
9702     el.on("mousedown", this.toFront, this);
9703     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9704     this.el.hide();
9705     Roo.DialogManager.register(this);
9706     Roo.BasicDialog.superclass.constructor.call(this);
9707 };
9708
9709 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9710     shadowOffset: Roo.isIE ? 6 : 5,
9711     minHeight: 80,
9712     minWidth: 200,
9713     minButtonWidth: 75,
9714     defaultButton: null,
9715     buttonAlign: "right",
9716     tabTag: 'div',
9717     firstShow: true,
9718
9719     /**
9720      * Sets the dialog title text
9721      * @param {String} text The title text to display
9722      * @return {Roo.BasicDialog} this
9723      */
9724     setTitle : function(text){
9725         this.header.update(text);
9726         return this;
9727     },
9728
9729     // private
9730     closeClick : function(){
9731         this.hide();
9732     },
9733
9734     // private
9735     collapseClick : function(){
9736         this[this.collapsed ? "expand" : "collapse"]();
9737     },
9738
9739     /**
9740      * Collapses the dialog to its minimized state (only the title bar is visible).
9741      * Equivalent to the user clicking the collapse dialog button.
9742      */
9743     collapse : function(){
9744         if(!this.collapsed){
9745             this.collapsed = true;
9746             this.el.addClass("x-dlg-collapsed");
9747             this.restoreHeight = this.el.getHeight();
9748             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9749         }
9750     },
9751
9752     /**
9753      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9754      * clicking the expand dialog button.
9755      */
9756     expand : function(){
9757         if(this.collapsed){
9758             this.collapsed = false;
9759             this.el.removeClass("x-dlg-collapsed");
9760             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9761         }
9762     },
9763
9764     /**
9765      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9766      * @return {Roo.TabPanel} The tabs component
9767      */
9768     initTabs : function(){
9769         var tabs = this.getTabs();
9770         while(tabs.getTab(0)){
9771             tabs.removeTab(0);
9772         }
9773         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9774             var dom = el.dom;
9775             tabs.addTab(Roo.id(dom), dom.title);
9776             dom.title = "";
9777         });
9778         tabs.activate(0);
9779         return tabs;
9780     },
9781
9782     // private
9783     beforeResize : function(){
9784         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9785     },
9786
9787     // private
9788     onResize : function(){
9789         this.refreshSize();
9790         this.syncBodyHeight();
9791         this.adjustAssets();
9792         this.focus();
9793         this.fireEvent("resize", this, this.size.width, this.size.height);
9794     },
9795
9796     // private
9797     onKeyDown : function(e){
9798         if(this.isVisible()){
9799             this.fireEvent("keydown", this, e);
9800         }
9801     },
9802
9803     /**
9804      * Resizes the dialog.
9805      * @param {Number} width
9806      * @param {Number} height
9807      * @return {Roo.BasicDialog} this
9808      */
9809     resizeTo : function(width, height){
9810         this.el.setSize(width, height);
9811         this.size = {width: width, height: height};
9812         this.syncBodyHeight();
9813         if(this.fixedcenter){
9814             this.center();
9815         }
9816         if(this.isVisible()){
9817             this.constrainXY();
9818             this.adjustAssets();
9819         }
9820         this.fireEvent("resize", this, width, height);
9821         return this;
9822     },
9823
9824
9825     /**
9826      * Resizes the dialog to fit the specified content size.
9827      * @param {Number} width
9828      * @param {Number} height
9829      * @return {Roo.BasicDialog} this
9830      */
9831     setContentSize : function(w, h){
9832         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9833         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9834         //if(!this.el.isBorderBox()){
9835             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9836             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9837         //}
9838         if(this.tabs){
9839             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9840             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9841         }
9842         this.resizeTo(w, h);
9843         return this;
9844     },
9845
9846     /**
9847      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9848      * executed in response to a particular key being pressed while the dialog is active.
9849      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9850      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9851      * @param {Function} fn The function to call
9852      * @param {Object} scope (optional) The scope of the function
9853      * @return {Roo.BasicDialog} this
9854      */
9855     addKeyListener : function(key, fn, scope){
9856         var keyCode, shift, ctrl, alt;
9857         if(typeof key == "object" && !(key instanceof Array)){
9858             keyCode = key["key"];
9859             shift = key["shift"];
9860             ctrl = key["ctrl"];
9861             alt = key["alt"];
9862         }else{
9863             keyCode = key;
9864         }
9865         var handler = function(dlg, e){
9866             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9867                 var k = e.getKey();
9868                 if(keyCode instanceof Array){
9869                     for(var i = 0, len = keyCode.length; i < len; i++){
9870                         if(keyCode[i] == k){
9871                           fn.call(scope || window, dlg, k, e);
9872                           return;
9873                         }
9874                     }
9875                 }else{
9876                     if(k == keyCode){
9877                         fn.call(scope || window, dlg, k, e);
9878                     }
9879                 }
9880             }
9881         };
9882         this.on("keydown", handler);
9883         return this;
9884     },
9885
9886     /**
9887      * Returns the TabPanel component (creates it if it doesn't exist).
9888      * Note: If you wish to simply check for the existence of tabs without creating them,
9889      * check for a null 'tabs' property.
9890      * @return {Roo.TabPanel} The tabs component
9891      */
9892     getTabs : function(){
9893         if(!this.tabs){
9894             this.el.addClass("x-dlg-auto-tabs");
9895             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9896             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9897         }
9898         return this.tabs;
9899     },
9900
9901     /**
9902      * Adds a button to the footer section of the dialog.
9903      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9904      * object or a valid Roo.DomHelper element config
9905      * @param {Function} handler The function called when the button is clicked
9906      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9907      * @return {Roo.Button} The new button
9908      */
9909     addButton : function(config, handler, scope){
9910         var dh = Roo.DomHelper;
9911         if(!this.footer){
9912             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9913         }
9914         if(!this.btnContainer){
9915             var tb = this.footer.createChild({
9916
9917                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9918                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9919             }, null, true);
9920             this.btnContainer = tb.firstChild.firstChild.firstChild;
9921         }
9922         var bconfig = {
9923             handler: handler,
9924             scope: scope,
9925             minWidth: this.minButtonWidth,
9926             hideParent:true
9927         };
9928         if(typeof config == "string"){
9929             bconfig.text = config;
9930         }else{
9931             if(config.tag){
9932                 bconfig.dhconfig = config;
9933             }else{
9934                 Roo.apply(bconfig, config);
9935             }
9936         }
9937         var fc = false;
9938         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9939             bconfig.position = Math.max(0, bconfig.position);
9940             fc = this.btnContainer.childNodes[bconfig.position];
9941         }
9942          
9943         var btn = new Roo.Button(
9944             fc ? 
9945                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9946                 : this.btnContainer.appendChild(document.createElement("td")),
9947             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9948             bconfig
9949         );
9950         this.syncBodyHeight();
9951         if(!this.buttons){
9952             /**
9953              * Array of all the buttons that have been added to this dialog via addButton
9954              * @type Array
9955              */
9956             this.buttons = [];
9957         }
9958         this.buttons.push(btn);
9959         return btn;
9960     },
9961
9962     /**
9963      * Sets the default button to be focused when the dialog is displayed.
9964      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9965      * @return {Roo.BasicDialog} this
9966      */
9967     setDefaultButton : function(btn){
9968         this.defaultButton = btn;
9969         return this;
9970     },
9971
9972     // private
9973     getHeaderFooterHeight : function(safe){
9974         var height = 0;
9975         if(this.header){
9976            height += this.header.getHeight();
9977         }
9978         if(this.footer){
9979            var fm = this.footer.getMargins();
9980             height += (this.footer.getHeight()+fm.top+fm.bottom);
9981         }
9982         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9983         height += this.centerBg.getPadding("tb");
9984         return height;
9985     },
9986
9987     // private
9988     syncBodyHeight : function()
9989     {
9990         var bd = this.body, // the text
9991             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9992             bw = this.bwrap;
9993         var height = this.size.height - this.getHeaderFooterHeight(false);
9994         bd.setHeight(height-bd.getMargins("tb"));
9995         var hh = this.header.getHeight();
9996         var h = this.size.height-hh;
9997         cb.setHeight(h);
9998         
9999         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
10000         bw.setHeight(h-cb.getPadding("tb"));
10001         
10002         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
10003         bd.setWidth(bw.getWidth(true));
10004         if(this.tabs){
10005             this.tabs.syncHeight();
10006             if(Roo.isIE){
10007                 this.tabs.el.repaint();
10008             }
10009         }
10010     },
10011
10012     /**
10013      * Restores the previous state of the dialog if Roo.state is configured.
10014      * @return {Roo.BasicDialog} this
10015      */
10016     restoreState : function(){
10017         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
10018         if(box && box.width){
10019             this.xy = [box.x, box.y];
10020             this.resizeTo(box.width, box.height);
10021         }
10022         return this;
10023     },
10024
10025     // private
10026     beforeShow : function(){
10027         this.expand();
10028         if(this.fixedcenter){
10029             this.xy = this.el.getCenterXY(true);
10030         }
10031         if(this.modal){
10032             Roo.get(document.body).addClass("x-body-masked");
10033             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10034             this.mask.show();
10035         }
10036         this.constrainXY();
10037     },
10038
10039     // private
10040     animShow : function(){
10041         var b = Roo.get(this.animateTarget).getBox();
10042         this.proxy.setSize(b.width, b.height);
10043         this.proxy.setLocation(b.x, b.y);
10044         this.proxy.show();
10045         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10046                     true, .35, this.showEl.createDelegate(this));
10047     },
10048
10049     /**
10050      * Shows the dialog.
10051      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10052      * @return {Roo.BasicDialog} this
10053      */
10054     show : function(animateTarget){
10055         if (this.fireEvent("beforeshow", this) === false){
10056             return;
10057         }
10058         if(this.syncHeightBeforeShow){
10059             this.syncBodyHeight();
10060         }else if(this.firstShow){
10061             this.firstShow = false;
10062             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10063         }
10064         this.animateTarget = animateTarget || this.animateTarget;
10065         if(!this.el.isVisible()){
10066             this.beforeShow();
10067             if(this.animateTarget && Roo.get(this.animateTarget)){
10068                 this.animShow();
10069             }else{
10070                 this.showEl();
10071             }
10072         }
10073         return this;
10074     },
10075
10076     // private
10077     showEl : function(){
10078         this.proxy.hide();
10079         this.el.setXY(this.xy);
10080         this.el.show();
10081         this.adjustAssets(true);
10082         this.toFront();
10083         this.focus();
10084         // IE peekaboo bug - fix found by Dave Fenwick
10085         if(Roo.isIE){
10086             this.el.repaint();
10087         }
10088         this.fireEvent("show", this);
10089     },
10090
10091     /**
10092      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10093      * dialog itself will receive focus.
10094      */
10095     focus : function(){
10096         if(this.defaultButton){
10097             this.defaultButton.focus();
10098         }else{
10099             this.focusEl.focus();
10100         }
10101     },
10102
10103     // private
10104     constrainXY : function(){
10105         if(this.constraintoviewport !== false){
10106             if(!this.viewSize){
10107                 if(this.container){
10108                     var s = this.container.getSize();
10109                     this.viewSize = [s.width, s.height];
10110                 }else{
10111                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10112                 }
10113             }
10114             var s = Roo.get(this.container||document).getScroll();
10115
10116             var x = this.xy[0], y = this.xy[1];
10117             var w = this.size.width, h = this.size.height;
10118             var vw = this.viewSize[0], vh = this.viewSize[1];
10119             // only move it if it needs it
10120             var moved = false;
10121             // first validate right/bottom
10122             if(x + w > vw+s.left){
10123                 x = vw - w;
10124                 moved = true;
10125             }
10126             if(y + h > vh+s.top){
10127                 y = vh - h;
10128                 moved = true;
10129             }
10130             // then make sure top/left isn't negative
10131             if(x < s.left){
10132                 x = s.left;
10133                 moved = true;
10134             }
10135             if(y < s.top){
10136                 y = s.top;
10137                 moved = true;
10138             }
10139             if(moved){
10140                 // cache xy
10141                 this.xy = [x, y];
10142                 if(this.isVisible()){
10143                     this.el.setLocation(x, y);
10144                     this.adjustAssets();
10145                 }
10146             }
10147         }
10148     },
10149
10150     // private
10151     onDrag : function(){
10152         if(!this.proxyDrag){
10153             this.xy = this.el.getXY();
10154             this.adjustAssets();
10155         }
10156     },
10157
10158     // private
10159     adjustAssets : function(doShow){
10160         var x = this.xy[0], y = this.xy[1];
10161         var w = this.size.width, h = this.size.height;
10162         if(doShow === true){
10163             if(this.shadow){
10164                 this.shadow.show(this.el);
10165             }
10166             if(this.shim){
10167                 this.shim.show();
10168             }
10169         }
10170         if(this.shadow && this.shadow.isVisible()){
10171             this.shadow.show(this.el);
10172         }
10173         if(this.shim && this.shim.isVisible()){
10174             this.shim.setBounds(x, y, w, h);
10175         }
10176     },
10177
10178     // private
10179     adjustViewport : function(w, h){
10180         if(!w || !h){
10181             w = Roo.lib.Dom.getViewWidth();
10182             h = Roo.lib.Dom.getViewHeight();
10183         }
10184         // cache the size
10185         this.viewSize = [w, h];
10186         if(this.modal && this.mask.isVisible()){
10187             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10188             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10189         }
10190         if(this.isVisible()){
10191             this.constrainXY();
10192         }
10193     },
10194
10195     /**
10196      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10197      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10198      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10199      */
10200     destroy : function(removeEl){
10201         if(this.isVisible()){
10202             this.animateTarget = null;
10203             this.hide();
10204         }
10205         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10206         if(this.tabs){
10207             this.tabs.destroy(removeEl);
10208         }
10209         Roo.destroy(
10210              this.shim,
10211              this.proxy,
10212              this.resizer,
10213              this.close,
10214              this.mask
10215         );
10216         if(this.dd){
10217             this.dd.unreg();
10218         }
10219         if(this.buttons){
10220            for(var i = 0, len = this.buttons.length; i < len; i++){
10221                this.buttons[i].destroy();
10222            }
10223         }
10224         this.el.removeAllListeners();
10225         if(removeEl === true){
10226             this.el.update("");
10227             this.el.remove();
10228         }
10229         Roo.DialogManager.unregister(this);
10230     },
10231
10232     // private
10233     startMove : function(){
10234         if(this.proxyDrag){
10235             this.proxy.show();
10236         }
10237         if(this.constraintoviewport !== false){
10238             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10239         }
10240     },
10241
10242     // private
10243     endMove : function(){
10244         if(!this.proxyDrag){
10245             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10246         }else{
10247             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10248             this.proxy.hide();
10249         }
10250         this.refreshSize();
10251         this.adjustAssets();
10252         this.focus();
10253         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10254     },
10255
10256     /**
10257      * Brings this dialog to the front of any other visible dialogs
10258      * @return {Roo.BasicDialog} this
10259      */
10260     toFront : function(){
10261         Roo.DialogManager.bringToFront(this);
10262         return this;
10263     },
10264
10265     /**
10266      * Sends this dialog to the back (under) of any other visible dialogs
10267      * @return {Roo.BasicDialog} this
10268      */
10269     toBack : function(){
10270         Roo.DialogManager.sendToBack(this);
10271         return this;
10272     },
10273
10274     /**
10275      * Centers this dialog in the viewport
10276      * @return {Roo.BasicDialog} this
10277      */
10278     center : function(){
10279         var xy = this.el.getCenterXY(true);
10280         this.moveTo(xy[0], xy[1]);
10281         return this;
10282     },
10283
10284     /**
10285      * Moves the dialog's top-left corner to the specified point
10286      * @param {Number} x
10287      * @param {Number} y
10288      * @return {Roo.BasicDialog} this
10289      */
10290     moveTo : function(x, y){
10291         this.xy = [x,y];
10292         if(this.isVisible()){
10293             this.el.setXY(this.xy);
10294             this.adjustAssets();
10295         }
10296         return this;
10297     },
10298
10299     /**
10300      * Aligns the dialog to the specified element
10301      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10302      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10303      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10304      * @return {Roo.BasicDialog} this
10305      */
10306     alignTo : function(element, position, offsets){
10307         this.xy = this.el.getAlignToXY(element, position, offsets);
10308         if(this.isVisible()){
10309             this.el.setXY(this.xy);
10310             this.adjustAssets();
10311         }
10312         return this;
10313     },
10314
10315     /**
10316      * Anchors an element to another element and realigns it when the window is resized.
10317      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10318      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10319      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10320      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10321      * is a number, it is used as the buffer delay (defaults to 50ms).
10322      * @return {Roo.BasicDialog} this
10323      */
10324     anchorTo : function(el, alignment, offsets, monitorScroll){
10325         var action = function(){
10326             this.alignTo(el, alignment, offsets);
10327         };
10328         Roo.EventManager.onWindowResize(action, this);
10329         var tm = typeof monitorScroll;
10330         if(tm != 'undefined'){
10331             Roo.EventManager.on(window, 'scroll', action, this,
10332                 {buffer: tm == 'number' ? monitorScroll : 50});
10333         }
10334         action.call(this);
10335         return this;
10336     },
10337
10338     /**
10339      * Returns true if the dialog is visible
10340      * @return {Boolean}
10341      */
10342     isVisible : function(){
10343         return this.el.isVisible();
10344     },
10345
10346     // private
10347     animHide : function(callback){
10348         var b = Roo.get(this.animateTarget).getBox();
10349         this.proxy.show();
10350         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10351         this.el.hide();
10352         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10353                     this.hideEl.createDelegate(this, [callback]));
10354     },
10355
10356     /**
10357      * Hides the dialog.
10358      * @param {Function} callback (optional) Function to call when the dialog is hidden
10359      * @return {Roo.BasicDialog} this
10360      */
10361     hide : function(callback){
10362         if (this.fireEvent("beforehide", this) === false){
10363             return;
10364         }
10365         if(this.shadow){
10366             this.shadow.hide();
10367         }
10368         if(this.shim) {
10369           this.shim.hide();
10370         }
10371         // sometimes animateTarget seems to get set.. causing problems...
10372         // this just double checks..
10373         if(this.animateTarget && Roo.get(this.animateTarget)) {
10374            this.animHide(callback);
10375         }else{
10376             this.el.hide();
10377             this.hideEl(callback);
10378         }
10379         return this;
10380     },
10381
10382     // private
10383     hideEl : function(callback){
10384         this.proxy.hide();
10385         if(this.modal){
10386             this.mask.hide();
10387             Roo.get(document.body).removeClass("x-body-masked");
10388         }
10389         this.fireEvent("hide", this);
10390         if(typeof callback == "function"){
10391             callback();
10392         }
10393     },
10394
10395     // private
10396     hideAction : function(){
10397         this.setLeft("-10000px");
10398         this.setTop("-10000px");
10399         this.setStyle("visibility", "hidden");
10400     },
10401
10402     // private
10403     refreshSize : function(){
10404         this.size = this.el.getSize();
10405         this.xy = this.el.getXY();
10406         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10407     },
10408
10409     // private
10410     // z-index is managed by the DialogManager and may be overwritten at any time
10411     setZIndex : function(index){
10412         if(this.modal){
10413             this.mask.setStyle("z-index", index);
10414         }
10415         if(this.shim){
10416             this.shim.setStyle("z-index", ++index);
10417         }
10418         if(this.shadow){
10419             this.shadow.setZIndex(++index);
10420         }
10421         this.el.setStyle("z-index", ++index);
10422         if(this.proxy){
10423             this.proxy.setStyle("z-index", ++index);
10424         }
10425         if(this.resizer){
10426             this.resizer.proxy.setStyle("z-index", ++index);
10427         }
10428
10429         this.lastZIndex = index;
10430     },
10431
10432     /**
10433      * Returns the element for this dialog
10434      * @return {Roo.Element} The underlying dialog Element
10435      */
10436     getEl : function(){
10437         return this.el;
10438     }
10439 });
10440
10441 /**
10442  * @class Roo.DialogManager
10443  * Provides global access to BasicDialogs that have been created and
10444  * support for z-indexing (layering) multiple open dialogs.
10445  */
10446 Roo.DialogManager = function(){
10447     var list = {};
10448     var accessList = [];
10449     var front = null;
10450
10451     // private
10452     var sortDialogs = function(d1, d2){
10453         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10454     };
10455
10456     // private
10457     var orderDialogs = function(){
10458         accessList.sort(sortDialogs);
10459         var seed = Roo.DialogManager.zseed;
10460         for(var i = 0, len = accessList.length; i < len; i++){
10461             var dlg = accessList[i];
10462             if(dlg){
10463                 dlg.setZIndex(seed + (i*10));
10464             }
10465         }
10466     };
10467
10468     return {
10469         /**
10470          * The starting z-index for BasicDialogs (defaults to 9000)
10471          * @type Number The z-index value
10472          */
10473         zseed : 9000,
10474
10475         // private
10476         register : function(dlg){
10477             list[dlg.id] = dlg;
10478             accessList.push(dlg);
10479         },
10480
10481         // private
10482         unregister : function(dlg){
10483             delete list[dlg.id];
10484             var i=0;
10485             var len=0;
10486             if(!accessList.indexOf){
10487                 for(  i = 0, len = accessList.length; i < len; i++){
10488                     if(accessList[i] == dlg){
10489                         accessList.splice(i, 1);
10490                         return;
10491                     }
10492                 }
10493             }else{
10494                  i = accessList.indexOf(dlg);
10495                 if(i != -1){
10496                     accessList.splice(i, 1);
10497                 }
10498             }
10499         },
10500
10501         /**
10502          * Gets a registered dialog by id
10503          * @param {String/Object} id The id of the dialog or a dialog
10504          * @return {Roo.BasicDialog} this
10505          */
10506         get : function(id){
10507             return typeof id == "object" ? id : list[id];
10508         },
10509
10510         /**
10511          * Brings the specified dialog to the front
10512          * @param {String/Object} dlg The id of the dialog or a dialog
10513          * @return {Roo.BasicDialog} this
10514          */
10515         bringToFront : function(dlg){
10516             dlg = this.get(dlg);
10517             if(dlg != front){
10518                 front = dlg;
10519                 dlg._lastAccess = new Date().getTime();
10520                 orderDialogs();
10521             }
10522             return dlg;
10523         },
10524
10525         /**
10526          * Sends the specified dialog to the back
10527          * @param {String/Object} dlg The id of the dialog or a dialog
10528          * @return {Roo.BasicDialog} this
10529          */
10530         sendToBack : function(dlg){
10531             dlg = this.get(dlg);
10532             dlg._lastAccess = -(new Date().getTime());
10533             orderDialogs();
10534             return dlg;
10535         },
10536
10537         /**
10538          * Hides all dialogs
10539          */
10540         hideAll : function(){
10541             for(var id in list){
10542                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10543                     list[id].hide();
10544                 }
10545             }
10546         }
10547     };
10548 }();
10549
10550 /**
10551  * @class Roo.LayoutDialog
10552  * @extends Roo.BasicDialog
10553  * Dialog which provides adjustments for working with a layout in a Dialog.
10554  * Add your necessary layout config options to the dialog's config.<br>
10555  * Example usage (including a nested layout):
10556  * <pre><code>
10557 if(!dialog){
10558     dialog = new Roo.LayoutDialog("download-dlg", {
10559         modal: true,
10560         width:600,
10561         height:450,
10562         shadow:true,
10563         minWidth:500,
10564         minHeight:350,
10565         autoTabs:true,
10566         proxyDrag:true,
10567         // layout config merges with the dialog config
10568         center:{
10569             tabPosition: "top",
10570             alwaysShowTabs: true
10571         }
10572     });
10573     dialog.addKeyListener(27, dialog.hide, dialog);
10574     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10575     dialog.addButton("Build It!", this.getDownload, this);
10576
10577     // we can even add nested layouts
10578     var innerLayout = new Roo.BorderLayout("dl-inner", {
10579         east: {
10580             initialSize: 200,
10581             autoScroll:true,
10582             split:true
10583         },
10584         center: {
10585             autoScroll:true
10586         }
10587     });
10588     innerLayout.beginUpdate();
10589     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10590     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10591     innerLayout.endUpdate(true);
10592
10593     var layout = dialog.getLayout();
10594     layout.beginUpdate();
10595     layout.add("center", new Roo.ContentPanel("standard-panel",
10596                         {title: "Download the Source", fitToFrame:true}));
10597     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10598                {title: "Build your own roo.js"}));
10599     layout.getRegion("center").showPanel(sp);
10600     layout.endUpdate();
10601 }
10602 </code></pre>
10603     * @constructor
10604     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10605     * @param {Object} config configuration options
10606   */
10607 Roo.LayoutDialog = function(el, cfg){
10608     
10609     var config=  cfg;
10610     if (typeof(cfg) == 'undefined') {
10611         config = Roo.apply({}, el);
10612         // not sure why we use documentElement here.. - it should always be body.
10613         // IE7 borks horribly if we use documentElement.
10614         // webkit also does not like documentElement - it creates a body element...
10615         el = Roo.get( document.body || document.documentElement ).createChild();
10616         //config.autoCreate = true;
10617     }
10618     
10619     
10620     config.autoTabs = false;
10621     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10622     this.body.setStyle({overflow:"hidden", position:"relative"});
10623     this.layout = new Roo.BorderLayout(this.body.dom, config);
10624     this.layout.monitorWindowResize = false;
10625     this.el.addClass("x-dlg-auto-layout");
10626     // fix case when center region overwrites center function
10627     this.center = Roo.BasicDialog.prototype.center;
10628     this.on("show", this.layout.layout, this.layout, true);
10629     if (config.items) {
10630         var xitems = config.items;
10631         delete config.items;
10632         Roo.each(xitems, this.addxtype, this);
10633     }
10634     
10635     
10636 };
10637 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10638     /**
10639      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10640      * @deprecated
10641      */
10642     endUpdate : function(){
10643         this.layout.endUpdate();
10644     },
10645
10646     /**
10647      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10648      *  @deprecated
10649      */
10650     beginUpdate : function(){
10651         this.layout.beginUpdate();
10652     },
10653
10654     /**
10655      * Get the BorderLayout for this dialog
10656      * @return {Roo.BorderLayout}
10657      */
10658     getLayout : function(){
10659         return this.layout;
10660     },
10661
10662     showEl : function(){
10663         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10664         if(Roo.isIE7){
10665             this.layout.layout();
10666         }
10667     },
10668
10669     // private
10670     // Use the syncHeightBeforeShow config option to control this automatically
10671     syncBodyHeight : function(){
10672         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10673         if(this.layout){this.layout.layout();}
10674     },
10675     
10676       /**
10677      * Add an xtype element (actually adds to the layout.)
10678      * @return {Object} xdata xtype object data.
10679      */
10680     
10681     addxtype : function(c) {
10682         return this.layout.addxtype(c);
10683     }
10684 });/*
10685  * Based on:
10686  * Ext JS Library 1.1.1
10687  * Copyright(c) 2006-2007, Ext JS, LLC.
10688  *
10689  * Originally Released Under LGPL - original licence link has changed is not relivant.
10690  *
10691  * Fork - LGPL
10692  * <script type="text/javascript">
10693  */
10694  
10695 /**
10696  * @class Roo.MessageBox
10697  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10698  * Example usage:
10699  *<pre><code>
10700 // Basic alert:
10701 Roo.Msg.alert('Status', 'Changes saved successfully.');
10702
10703 // Prompt for user data:
10704 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10705     if (btn == 'ok'){
10706         // process text value...
10707     }
10708 });
10709
10710 // Show a dialog using config options:
10711 Roo.Msg.show({
10712    title:'Save Changes?',
10713    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10714    buttons: Roo.Msg.YESNOCANCEL,
10715    fn: processResult,
10716    animEl: 'elId'
10717 });
10718 </code></pre>
10719  * @singleton
10720  */
10721 Roo.MessageBox = function(){
10722     var dlg, opt, mask, waitTimer;
10723     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10724     var buttons, activeTextEl, bwidth;
10725
10726     // private
10727     var handleButton = function(button){
10728         dlg.hide();
10729         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10730     };
10731
10732     // private
10733     var handleHide = function(){
10734         if(opt && opt.cls){
10735             dlg.el.removeClass(opt.cls);
10736         }
10737         if(waitTimer){
10738             Roo.TaskMgr.stop(waitTimer);
10739             waitTimer = null;
10740         }
10741     };
10742
10743     // private
10744     var updateButtons = function(b){
10745         var width = 0;
10746         if(!b){
10747             buttons["ok"].hide();
10748             buttons["cancel"].hide();
10749             buttons["yes"].hide();
10750             buttons["no"].hide();
10751             dlg.footer.dom.style.display = 'none';
10752             return width;
10753         }
10754         dlg.footer.dom.style.display = '';
10755         for(var k in buttons){
10756             if(typeof buttons[k] != "function"){
10757                 if(b[k]){
10758                     buttons[k].show();
10759                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10760                     width += buttons[k].el.getWidth()+15;
10761                 }else{
10762                     buttons[k].hide();
10763                 }
10764             }
10765         }
10766         return width;
10767     };
10768
10769     // private
10770     var handleEsc = function(d, k, e){
10771         if(opt && opt.closable !== false){
10772             dlg.hide();
10773         }
10774         if(e){
10775             e.stopEvent();
10776         }
10777     };
10778
10779     return {
10780         /**
10781          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10782          * @return {Roo.BasicDialog} The BasicDialog element
10783          */
10784         getDialog : function(){
10785            if(!dlg){
10786                 dlg = new Roo.BasicDialog("x-msg-box", {
10787                     autoCreate : true,
10788                     shadow: true,
10789                     draggable: true,
10790                     resizable:false,
10791                     constraintoviewport:false,
10792                     fixedcenter:true,
10793                     collapsible : false,
10794                     shim:true,
10795                     modal: true,
10796                     width:400, height:100,
10797                     buttonAlign:"center",
10798                     closeClick : function(){
10799                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10800                             handleButton("no");
10801                         }else{
10802                             handleButton("cancel");
10803                         }
10804                     }
10805                 });
10806                 dlg.on("hide", handleHide);
10807                 mask = dlg.mask;
10808                 dlg.addKeyListener(27, handleEsc);
10809                 buttons = {};
10810                 var bt = this.buttonText;
10811                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10812                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10813                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10814                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10815                 bodyEl = dlg.body.createChild({
10816
10817                     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>'
10818                 });
10819                 msgEl = bodyEl.dom.firstChild;
10820                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10821                 textboxEl.enableDisplayMode();
10822                 textboxEl.addKeyListener([10,13], function(){
10823                     if(dlg.isVisible() && opt && opt.buttons){
10824                         if(opt.buttons.ok){
10825                             handleButton("ok");
10826                         }else if(opt.buttons.yes){
10827                             handleButton("yes");
10828                         }
10829                     }
10830                 });
10831                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10832                 textareaEl.enableDisplayMode();
10833                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10834                 progressEl.enableDisplayMode();
10835                 var pf = progressEl.dom.firstChild;
10836                 if (pf) {
10837                     pp = Roo.get(pf.firstChild);
10838                     pp.setHeight(pf.offsetHeight);
10839                 }
10840                 
10841             }
10842             return dlg;
10843         },
10844
10845         /**
10846          * Updates the message box body text
10847          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10848          * the XHTML-compliant non-breaking space character '&amp;#160;')
10849          * @return {Roo.MessageBox} This message box
10850          */
10851         updateText : function(text){
10852             if(!dlg.isVisible() && !opt.width){
10853                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10854             }
10855             msgEl.innerHTML = text || '&#160;';
10856       
10857             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10858             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10859             var w = Math.max(
10860                     Math.min(opt.width || cw , this.maxWidth), 
10861                     Math.max(opt.minWidth || this.minWidth, bwidth)
10862             );
10863             if(opt.prompt){
10864                 activeTextEl.setWidth(w);
10865             }
10866             if(dlg.isVisible()){
10867                 dlg.fixedcenter = false;
10868             }
10869             // to big, make it scroll. = But as usual stupid IE does not support
10870             // !important..
10871             
10872             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10873                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10874                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10875             } else {
10876                 bodyEl.dom.style.height = '';
10877                 bodyEl.dom.style.overflowY = '';
10878             }
10879             if (cw > w) {
10880                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10881             } else {
10882                 bodyEl.dom.style.overflowX = '';
10883             }
10884             
10885             dlg.setContentSize(w, bodyEl.getHeight());
10886             if(dlg.isVisible()){
10887                 dlg.fixedcenter = true;
10888             }
10889             return this;
10890         },
10891
10892         /**
10893          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10894          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10895          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10896          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10897          * @return {Roo.MessageBox} This message box
10898          */
10899         updateProgress : function(value, text){
10900             if(text){
10901                 this.updateText(text);
10902             }
10903             if (pp) { // weird bug on my firefox - for some reason this is not defined
10904                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10905             }
10906             return this;
10907         },        
10908
10909         /**
10910          * Returns true if the message box is currently displayed
10911          * @return {Boolean} True if the message box is visible, else false
10912          */
10913         isVisible : function(){
10914             return dlg && dlg.isVisible();  
10915         },
10916
10917         /**
10918          * Hides the message box if it is displayed
10919          */
10920         hide : function(){
10921             if(this.isVisible()){
10922                 dlg.hide();
10923             }  
10924         },
10925
10926         /**
10927          * Displays a new message box, or reinitializes an existing message box, based on the config options
10928          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10929          * The following config object properties are supported:
10930          * <pre>
10931 Property    Type             Description
10932 ----------  ---------------  ------------------------------------------------------------------------------------
10933 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10934                                    closes (defaults to undefined)
10935 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10936                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10937 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10938                                    progress and wait dialogs will ignore this property and always hide the
10939                                    close button as they can only be closed programmatically.
10940 cls               String           A custom CSS class to apply to the message box element
10941 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10942                                    displayed (defaults to 75)
10943 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10944                                    function will be btn (the name of the button that was clicked, if applicable,
10945                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10946                                    Progress and wait dialogs will ignore this option since they do not respond to
10947                                    user actions and can only be closed programmatically, so any required function
10948                                    should be called by the same code after it closes the dialog.
10949 icon              String           A CSS class that provides a background image to be used as an icon for
10950                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10951 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10952 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10953 modal             Boolean          False to allow user interaction with the page while the message box is
10954                                    displayed (defaults to true)
10955 msg               String           A string that will replace the existing message box body text (defaults
10956                                    to the XHTML-compliant non-breaking space character '&#160;')
10957 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10958 progress          Boolean          True to display a progress bar (defaults to false)
10959 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10960 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10961 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10962 title             String           The title text
10963 value             String           The string value to set into the active textbox element if displayed
10964 wait              Boolean          True to display a progress bar (defaults to false)
10965 width             Number           The width of the dialog in pixels
10966 </pre>
10967          *
10968          * Example usage:
10969          * <pre><code>
10970 Roo.Msg.show({
10971    title: 'Address',
10972    msg: 'Please enter your address:',
10973    width: 300,
10974    buttons: Roo.MessageBox.OKCANCEL,
10975    multiline: true,
10976    fn: saveAddress,
10977    animEl: 'addAddressBtn'
10978 });
10979 </code></pre>
10980          * @param {Object} config Configuration options
10981          * @return {Roo.MessageBox} This message box
10982          */
10983         show : function(options)
10984         {
10985             
10986             // this causes nightmares if you show one dialog after another
10987             // especially on callbacks..
10988              
10989             if(this.isVisible()){
10990                 
10991                 this.hide();
10992                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10993                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10994                 Roo.log("New Dialog Message:" +  options.msg )
10995                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10996                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10997                 
10998             }
10999             var d = this.getDialog();
11000             opt = options;
11001             d.setTitle(opt.title || "&#160;");
11002             d.close.setDisplayed(opt.closable !== false);
11003             activeTextEl = textboxEl;
11004             opt.prompt = opt.prompt || (opt.multiline ? true : false);
11005             if(opt.prompt){
11006                 if(opt.multiline){
11007                     textboxEl.hide();
11008                     textareaEl.show();
11009                     textareaEl.setHeight(typeof opt.multiline == "number" ?
11010                         opt.multiline : this.defaultTextHeight);
11011                     activeTextEl = textareaEl;
11012                 }else{
11013                     textboxEl.show();
11014                     textareaEl.hide();
11015                 }
11016             }else{
11017                 textboxEl.hide();
11018                 textareaEl.hide();
11019             }
11020             progressEl.setDisplayed(opt.progress === true);
11021             this.updateProgress(0);
11022             activeTextEl.dom.value = opt.value || "";
11023             if(opt.prompt){
11024                 dlg.setDefaultButton(activeTextEl);
11025             }else{
11026                 var bs = opt.buttons;
11027                 var db = null;
11028                 if(bs && bs.ok){
11029                     db = buttons["ok"];
11030                 }else if(bs && bs.yes){
11031                     db = buttons["yes"];
11032                 }
11033                 dlg.setDefaultButton(db);
11034             }
11035             bwidth = updateButtons(opt.buttons);
11036             this.updateText(opt.msg);
11037             if(opt.cls){
11038                 d.el.addClass(opt.cls);
11039             }
11040             d.proxyDrag = opt.proxyDrag === true;
11041             d.modal = opt.modal !== false;
11042             d.mask = opt.modal !== false ? mask : false;
11043             if(!d.isVisible()){
11044                 // force it to the end of the z-index stack so it gets a cursor in FF
11045                 document.body.appendChild(dlg.el.dom);
11046                 d.animateTarget = null;
11047                 d.show(options.animEl);
11048             }
11049             return this;
11050         },
11051
11052         /**
11053          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11054          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11055          * and closing the message box when the process is complete.
11056          * @param {String} title The title bar text
11057          * @param {String} msg The message box body text
11058          * @return {Roo.MessageBox} This message box
11059          */
11060         progress : function(title, msg){
11061             this.show({
11062                 title : title,
11063                 msg : msg,
11064                 buttons: false,
11065                 progress:true,
11066                 closable:false,
11067                 minWidth: this.minProgressWidth,
11068                 modal : true
11069             });
11070             return this;
11071         },
11072
11073         /**
11074          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11075          * If a callback function is passed it will be called after the user clicks the button, and the
11076          * id of the button that was clicked will be passed as the only parameter to the callback
11077          * (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         alert : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.OK,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11098          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11099          * You are responsible for closing the message box when the process is complete.
11100          * @param {String} msg The message box body text
11101          * @param {String} title (optional) The title bar text
11102          * @return {Roo.MessageBox} This message box
11103          */
11104         wait : function(msg, title){
11105             this.show({
11106                 title : title,
11107                 msg : msg,
11108                 buttons: false,
11109                 closable:false,
11110                 progress:true,
11111                 modal:true,
11112                 width:300,
11113                 wait:true
11114             });
11115             waitTimer = Roo.TaskMgr.start({
11116                 run: function(i){
11117                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11118                 },
11119                 interval: 1000
11120             });
11121             return this;
11122         },
11123
11124         /**
11125          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11126          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11127          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11128          * @param {String} title The title bar text
11129          * @param {String} msg The message box body text
11130          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11131          * @param {Object} scope (optional) The scope of the callback function
11132          * @return {Roo.MessageBox} This message box
11133          */
11134         confirm : function(title, msg, fn, scope){
11135             this.show({
11136                 title : title,
11137                 msg : msg,
11138                 buttons: this.YESNO,
11139                 fn: fn,
11140                 scope : scope,
11141                 modal : true
11142             });
11143             return this;
11144         },
11145
11146         /**
11147          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11148          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11149          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11150          * (could also be the top-right close button) and the text that was entered will be passed as the two
11151          * parameters to the callback.
11152          * @param {String} title The title bar text
11153          * @param {String} msg The message box body text
11154          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11155          * @param {Object} scope (optional) The scope of the callback function
11156          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11157          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11158          * @return {Roo.MessageBox} This message box
11159          */
11160         prompt : function(title, msg, fn, scope, multiline){
11161             this.show({
11162                 title : title,
11163                 msg : msg,
11164                 buttons: this.OKCANCEL,
11165                 fn: fn,
11166                 minWidth:250,
11167                 scope : scope,
11168                 prompt:true,
11169                 multiline: multiline,
11170                 modal : true
11171             });
11172             return this;
11173         },
11174
11175         /**
11176          * Button config that displays a single OK button
11177          * @type Object
11178          */
11179         OK : {ok:true},
11180         /**
11181          * Button config that displays Yes and No buttons
11182          * @type Object
11183          */
11184         YESNO : {yes:true, no:true},
11185         /**
11186          * Button config that displays OK and Cancel buttons
11187          * @type Object
11188          */
11189         OKCANCEL : {ok:true, cancel:true},
11190         /**
11191          * Button config that displays Yes, No and Cancel buttons
11192          * @type Object
11193          */
11194         YESNOCANCEL : {yes:true, no:true, cancel:true},
11195
11196         /**
11197          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11198          * @type Number
11199          */
11200         defaultTextHeight : 75,
11201         /**
11202          * The maximum width in pixels of the message box (defaults to 600)
11203          * @type Number
11204          */
11205         maxWidth : 600,
11206         /**
11207          * The minimum width in pixels of the message box (defaults to 100)
11208          * @type Number
11209          */
11210         minWidth : 100,
11211         /**
11212          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11213          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11214          * @type Number
11215          */
11216         minProgressWidth : 250,
11217         /**
11218          * An object containing the default button text strings that can be overriden for localized language support.
11219          * Supported properties are: ok, cancel, yes and no.
11220          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11221          * @type Object
11222          */
11223         buttonText : {
11224             ok : "OK",
11225             cancel : "Cancel",
11226             yes : "Yes",
11227             no : "No"
11228         }
11229     };
11230 }();
11231
11232 /**
11233  * Shorthand for {@link Roo.MessageBox}
11234  */
11235 Roo.Msg = Roo.MessageBox;/*
11236  * Based on:
11237  * Ext JS Library 1.1.1
11238  * Copyright(c) 2006-2007, Ext JS, LLC.
11239  *
11240  * Originally Released Under LGPL - original licence link has changed is not relivant.
11241  *
11242  * Fork - LGPL
11243  * <script type="text/javascript">
11244  */
11245 /**
11246  * @class Roo.QuickTips
11247  * Provides attractive and customizable tooltips for any element.
11248  * @singleton
11249  */
11250 Roo.QuickTips = function(){
11251     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11252     var ce, bd, xy, dd;
11253     var visible = false, disabled = true, inited = false;
11254     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11255     
11256     var onOver = function(e){
11257         if(disabled){
11258             return;
11259         }
11260         var t = e.getTarget();
11261         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11262             return;
11263         }
11264         if(ce && t == ce.el){
11265             clearTimeout(hideProc);
11266             return;
11267         }
11268         if(t && tagEls[t.id]){
11269             tagEls[t.id].el = t;
11270             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11271             return;
11272         }
11273         var ttp, et = Roo.fly(t);
11274         var ns = cfg.namespace;
11275         if(tm.interceptTitles && t.title){
11276             ttp = t.title;
11277             t.qtip = ttp;
11278             t.removeAttribute("title");
11279             e.preventDefault();
11280         }else{
11281             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11282         }
11283         if(ttp){
11284             showProc = show.defer(tm.showDelay, tm, [{
11285                 el: t, 
11286                 text: ttp.replace(/\\n/g,'<br/>'),
11287                 width: et.getAttributeNS(ns, cfg.width),
11288                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11289                 title: et.getAttributeNS(ns, cfg.title),
11290                     cls: et.getAttributeNS(ns, cfg.cls)
11291             }]);
11292         }
11293     };
11294     
11295     var onOut = function(e){
11296         clearTimeout(showProc);
11297         var t = e.getTarget();
11298         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11299             hideProc = setTimeout(hide, tm.hideDelay);
11300         }
11301     };
11302     
11303     var onMove = function(e){
11304         if(disabled){
11305             return;
11306         }
11307         xy = e.getXY();
11308         xy[1] += 18;
11309         if(tm.trackMouse && ce){
11310             el.setXY(xy);
11311         }
11312     };
11313     
11314     var onDown = function(e){
11315         clearTimeout(showProc);
11316         clearTimeout(hideProc);
11317         if(!e.within(el)){
11318             if(tm.hideOnClick){
11319                 hide();
11320                 tm.disable();
11321                 tm.enable.defer(100, tm);
11322             }
11323         }
11324     };
11325     
11326     var getPad = function(){
11327         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11328     };
11329
11330     var show = function(o){
11331         if(disabled){
11332             return;
11333         }
11334         clearTimeout(dismissProc);
11335         ce = o;
11336         if(removeCls){ // in case manually hidden
11337             el.removeClass(removeCls);
11338             removeCls = null;
11339         }
11340         if(ce.cls){
11341             el.addClass(ce.cls);
11342             removeCls = ce.cls;
11343         }
11344         if(ce.title){
11345             tipTitle.update(ce.title);
11346             tipTitle.show();
11347         }else{
11348             tipTitle.update('');
11349             tipTitle.hide();
11350         }
11351         el.dom.style.width  = tm.maxWidth+'px';
11352         //tipBody.dom.style.width = '';
11353         tipBodyText.update(o.text);
11354         var p = getPad(), w = ce.width;
11355         if(!w){
11356             var td = tipBodyText.dom;
11357             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11358             if(aw > tm.maxWidth){
11359                 w = tm.maxWidth;
11360             }else if(aw < tm.minWidth){
11361                 w = tm.minWidth;
11362             }else{
11363                 w = aw;
11364             }
11365         }
11366         //tipBody.setWidth(w);
11367         el.setWidth(parseInt(w, 10) + p);
11368         if(ce.autoHide === false){
11369             close.setDisplayed(true);
11370             if(dd){
11371                 dd.unlock();
11372             }
11373         }else{
11374             close.setDisplayed(false);
11375             if(dd){
11376                 dd.lock();
11377             }
11378         }
11379         if(xy){
11380             el.avoidY = xy[1]-18;
11381             el.setXY(xy);
11382         }
11383         if(tm.animate){
11384             el.setOpacity(.1);
11385             el.setStyle("visibility", "visible");
11386             el.fadeIn({callback: afterShow});
11387         }else{
11388             afterShow();
11389         }
11390     };
11391     
11392     var afterShow = function(){
11393         if(ce){
11394             el.show();
11395             esc.enable();
11396             if(tm.autoDismiss && ce.autoHide !== false){
11397                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11398             }
11399         }
11400     };
11401     
11402     var hide = function(noanim){
11403         clearTimeout(dismissProc);
11404         clearTimeout(hideProc);
11405         ce = null;
11406         if(el.isVisible()){
11407             esc.disable();
11408             if(noanim !== true && tm.animate){
11409                 el.fadeOut({callback: afterHide});
11410             }else{
11411                 afterHide();
11412             } 
11413         }
11414     };
11415     
11416     var afterHide = function(){
11417         el.hide();
11418         if(removeCls){
11419             el.removeClass(removeCls);
11420             removeCls = null;
11421         }
11422     };
11423     
11424     return {
11425         /**
11426         * @cfg {Number} minWidth
11427         * The minimum width of the quick tip (defaults to 40)
11428         */
11429        minWidth : 40,
11430         /**
11431         * @cfg {Number} maxWidth
11432         * The maximum width of the quick tip (defaults to 300)
11433         */
11434        maxWidth : 300,
11435         /**
11436         * @cfg {Boolean} interceptTitles
11437         * True to automatically use the element's DOM title value if available (defaults to false)
11438         */
11439        interceptTitles : false,
11440         /**
11441         * @cfg {Boolean} trackMouse
11442         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11443         */
11444        trackMouse : false,
11445         /**
11446         * @cfg {Boolean} hideOnClick
11447         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11448         */
11449        hideOnClick : true,
11450         /**
11451         * @cfg {Number} showDelay
11452         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11453         */
11454        showDelay : 500,
11455         /**
11456         * @cfg {Number} hideDelay
11457         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11458         */
11459        hideDelay : 200,
11460         /**
11461         * @cfg {Boolean} autoHide
11462         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11463         * Used in conjunction with hideDelay.
11464         */
11465        autoHide : true,
11466         /**
11467         * @cfg {Boolean}
11468         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11469         * (defaults to true).  Used in conjunction with autoDismissDelay.
11470         */
11471        autoDismiss : true,
11472         /**
11473         * @cfg {Number}
11474         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11475         */
11476        autoDismissDelay : 5000,
11477        /**
11478         * @cfg {Boolean} animate
11479         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11480         */
11481        animate : false,
11482
11483        /**
11484         * @cfg {String} title
11485         * Title text to display (defaults to '').  This can be any valid HTML markup.
11486         */
11487         title: '',
11488        /**
11489         * @cfg {String} text
11490         * Body text to display (defaults to '').  This can be any valid HTML markup.
11491         */
11492         text : '',
11493        /**
11494         * @cfg {String} cls
11495         * A CSS class to apply to the base quick tip element (defaults to '').
11496         */
11497         cls : '',
11498        /**
11499         * @cfg {Number} width
11500         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11501         * minWidth or maxWidth.
11502         */
11503         width : null,
11504
11505     /**
11506      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11507      * or display QuickTips in a page.
11508      */
11509        init : function(){
11510           tm = Roo.QuickTips;
11511           cfg = tm.tagConfig;
11512           if(!inited){
11513               if(!Roo.isReady){ // allow calling of init() before onReady
11514                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11515                   return;
11516               }
11517               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11518               el.fxDefaults = {stopFx: true};
11519               // maximum custom styling
11520               //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>');
11521               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>');              
11522               tipTitle = el.child('h3');
11523               tipTitle.enableDisplayMode("block");
11524               tipBody = el.child('div.x-tip-bd');
11525               tipBodyText = el.child('div.x-tip-bd-inner');
11526               //bdLeft = el.child('div.x-tip-bd-left');
11527               //bdRight = el.child('div.x-tip-bd-right');
11528               close = el.child('div.x-tip-close');
11529               close.enableDisplayMode("block");
11530               close.on("click", hide);
11531               var d = Roo.get(document);
11532               d.on("mousedown", onDown);
11533               d.on("mouseover", onOver);
11534               d.on("mouseout", onOut);
11535               d.on("mousemove", onMove);
11536               esc = d.addKeyListener(27, hide);
11537               esc.disable();
11538               if(Roo.dd.DD){
11539                   dd = el.initDD("default", null, {
11540                       onDrag : function(){
11541                           el.sync();  
11542                       }
11543                   });
11544                   dd.setHandleElId(tipTitle.id);
11545                   dd.lock();
11546               }
11547               inited = true;
11548           }
11549           this.enable(); 
11550        },
11551
11552     /**
11553      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11554      * are supported:
11555      * <pre>
11556 Property    Type                   Description
11557 ----------  ---------------------  ------------------------------------------------------------------------
11558 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11559      * </ul>
11560      * @param {Object} config The config object
11561      */
11562        register : function(config){
11563            var cs = config instanceof Array ? config : arguments;
11564            for(var i = 0, len = cs.length; i < len; i++) {
11565                var c = cs[i];
11566                var target = c.target;
11567                if(target){
11568                    if(target instanceof Array){
11569                        for(var j = 0, jlen = target.length; j < jlen; j++){
11570                            tagEls[target[j]] = c;
11571                        }
11572                    }else{
11573                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11574                    }
11575                }
11576            }
11577        },
11578
11579     /**
11580      * Removes this quick tip from its element and destroys it.
11581      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11582      */
11583        unregister : function(el){
11584            delete tagEls[Roo.id(el)];
11585        },
11586
11587     /**
11588      * Enable this quick tip.
11589      */
11590        enable : function(){
11591            if(inited && disabled){
11592                locks.pop();
11593                if(locks.length < 1){
11594                    disabled = false;
11595                }
11596            }
11597        },
11598
11599     /**
11600      * Disable this quick tip.
11601      */
11602        disable : function(){
11603           disabled = true;
11604           clearTimeout(showProc);
11605           clearTimeout(hideProc);
11606           clearTimeout(dismissProc);
11607           if(ce){
11608               hide(true);
11609           }
11610           locks.push(1);
11611        },
11612
11613     /**
11614      * Returns true if the quick tip is enabled, else false.
11615      */
11616        isEnabled : function(){
11617             return !disabled;
11618        },
11619
11620         // private
11621        tagConfig : {
11622            namespace : "roo", // was ext?? this may break..
11623            alt_namespace : "ext",
11624            attribute : "qtip",
11625            width : "width",
11626            target : "target",
11627            title : "qtitle",
11628            hide : "hide",
11629            cls : "qclass"
11630        }
11631    };
11632 }();
11633
11634 // backwards compat
11635 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11636  * Based on:
11637  * Ext JS Library 1.1.1
11638  * Copyright(c) 2006-2007, Ext JS, LLC.
11639  *
11640  * Originally Released Under LGPL - original licence link has changed is not relivant.
11641  *
11642  * Fork - LGPL
11643  * <script type="text/javascript">
11644  */
11645  
11646
11647 /**
11648  * @class Roo.tree.TreePanel
11649  * @extends Roo.data.Tree
11650
11651  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11652  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11653  * @cfg {Boolean} enableDD true to enable drag and drop
11654  * @cfg {Boolean} enableDrag true to enable just drag
11655  * @cfg {Boolean} enableDrop true to enable just drop
11656  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11657  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11658  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11659  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11660  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11661  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11662  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11663  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11664  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11665  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11666  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11667  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11668  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11669  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11670  * @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>
11671  * @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>
11672  * 
11673  * @constructor
11674  * @param {String/HTMLElement/Element} el The container element
11675  * @param {Object} config
11676  */
11677 Roo.tree.TreePanel = function(el, config){
11678     var root = false;
11679     var loader = false;
11680     if (config.root) {
11681         root = config.root;
11682         delete config.root;
11683     }
11684     if (config.loader) {
11685         loader = config.loader;
11686         delete config.loader;
11687     }
11688     
11689     Roo.apply(this, config);
11690     Roo.tree.TreePanel.superclass.constructor.call(this);
11691     this.el = Roo.get(el);
11692     this.el.addClass('x-tree');
11693     //console.log(root);
11694     if (root) {
11695         this.setRootNode( Roo.factory(root, Roo.tree));
11696     }
11697     if (loader) {
11698         this.loader = Roo.factory(loader, Roo.tree);
11699     }
11700    /**
11701     * Read-only. The id of the container element becomes this TreePanel's id.
11702     */
11703     this.id = this.el.id;
11704     this.addEvents({
11705         /**
11706         * @event beforeload
11707         * Fires before a node is loaded, return false to cancel
11708         * @param {Node} node The node being loaded
11709         */
11710         "beforeload" : true,
11711         /**
11712         * @event load
11713         * Fires when a node is loaded
11714         * @param {Node} node The node that was loaded
11715         */
11716         "load" : true,
11717         /**
11718         * @event textchange
11719         * Fires when the text for a node is changed
11720         * @param {Node} node The node
11721         * @param {String} text The new text
11722         * @param {String} oldText The old text
11723         */
11724         "textchange" : true,
11725         /**
11726         * @event beforeexpand
11727         * Fires before a node is expanded, return false to cancel.
11728         * @param {Node} node The node
11729         * @param {Boolean} deep
11730         * @param {Boolean} anim
11731         */
11732         "beforeexpand" : true,
11733         /**
11734         * @event beforecollapse
11735         * Fires before a node is collapsed, return false to cancel.
11736         * @param {Node} node The node
11737         * @param {Boolean} deep
11738         * @param {Boolean} anim
11739         */
11740         "beforecollapse" : true,
11741         /**
11742         * @event expand
11743         * Fires when a node is expanded
11744         * @param {Node} node The node
11745         */
11746         "expand" : true,
11747         /**
11748         * @event disabledchange
11749         * Fires when the disabled status of a node changes
11750         * @param {Node} node The node
11751         * @param {Boolean} disabled
11752         */
11753         "disabledchange" : true,
11754         /**
11755         * @event collapse
11756         * Fires when a node is collapsed
11757         * @param {Node} node The node
11758         */
11759         "collapse" : true,
11760         /**
11761         * @event beforeclick
11762         * Fires before click processing on a node. Return false to cancel the default action.
11763         * @param {Node} node The node
11764         * @param {Roo.EventObject} e The event object
11765         */
11766         "beforeclick":true,
11767         /**
11768         * @event checkchange
11769         * Fires when a node with a checkbox's checked property changes
11770         * @param {Node} this This node
11771         * @param {Boolean} checked
11772         */
11773         "checkchange":true,
11774         /**
11775         * @event click
11776         * Fires when a node is clicked
11777         * @param {Node} node The node
11778         * @param {Roo.EventObject} e The event object
11779         */
11780         "click":true,
11781         /**
11782         * @event dblclick
11783         * Fires when a node is double clicked
11784         * @param {Node} node The node
11785         * @param {Roo.EventObject} e The event object
11786         */
11787         "dblclick":true,
11788         /**
11789         * @event contextmenu
11790         * Fires when a node is right clicked
11791         * @param {Node} node The node
11792         * @param {Roo.EventObject} e The event object
11793         */
11794         "contextmenu":true,
11795         /**
11796         * @event beforechildrenrendered
11797         * Fires right before the child nodes for a node are rendered
11798         * @param {Node} node The node
11799         */
11800         "beforechildrenrendered":true,
11801         /**
11802         * @event startdrag
11803         * Fires when a node starts being dragged
11804         * @param {Roo.tree.TreePanel} this
11805         * @param {Roo.tree.TreeNode} node
11806         * @param {event} e The raw browser event
11807         */ 
11808        "startdrag" : true,
11809        /**
11810         * @event enddrag
11811         * Fires when a drag operation is complete
11812         * @param {Roo.tree.TreePanel} this
11813         * @param {Roo.tree.TreeNode} node
11814         * @param {event} e The raw browser event
11815         */
11816        "enddrag" : true,
11817        /**
11818         * @event dragdrop
11819         * Fires when a dragged node is dropped on a valid DD target
11820         * @param {Roo.tree.TreePanel} this
11821         * @param {Roo.tree.TreeNode} node
11822         * @param {DD} dd The dd it was dropped on
11823         * @param {event} e The raw browser event
11824         */
11825        "dragdrop" : true,
11826        /**
11827         * @event beforenodedrop
11828         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11829         * passed to handlers has the following properties:<br />
11830         * <ul style="padding:5px;padding-left:16px;">
11831         * <li>tree - The TreePanel</li>
11832         * <li>target - The node being targeted for the drop</li>
11833         * <li>data - The drag data from the drag source</li>
11834         * <li>point - The point of the drop - append, above or below</li>
11835         * <li>source - The drag source</li>
11836         * <li>rawEvent - Raw mouse event</li>
11837         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11838         * to be inserted by setting them on this object.</li>
11839         * <li>cancel - Set this to true to cancel the drop.</li>
11840         * </ul>
11841         * @param {Object} dropEvent
11842         */
11843        "beforenodedrop" : true,
11844        /**
11845         * @event nodedrop
11846         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11847         * passed to handlers has the following properties:<br />
11848         * <ul style="padding:5px;padding-left:16px;">
11849         * <li>tree - The TreePanel</li>
11850         * <li>target - The node being targeted for the drop</li>
11851         * <li>data - The drag data from the drag source</li>
11852         * <li>point - The point of the drop - append, above or below</li>
11853         * <li>source - The drag source</li>
11854         * <li>rawEvent - Raw mouse event</li>
11855         * <li>dropNode - Dropped node(s).</li>
11856         * </ul>
11857         * @param {Object} dropEvent
11858         */
11859        "nodedrop" : true,
11860         /**
11861         * @event nodedragover
11862         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11863         * passed to handlers has the following properties:<br />
11864         * <ul style="padding:5px;padding-left:16px;">
11865         * <li>tree - The TreePanel</li>
11866         * <li>target - The node being targeted for the drop</li>
11867         * <li>data - The drag data from the drag source</li>
11868         * <li>point - The point of the drop - append, above or below</li>
11869         * <li>source - The drag source</li>
11870         * <li>rawEvent - Raw mouse event</li>
11871         * <li>dropNode - Drop node(s) provided by the source.</li>
11872         * <li>cancel - Set this to true to signal drop not allowed.</li>
11873         * </ul>
11874         * @param {Object} dragOverEvent
11875         */
11876        "nodedragover" : true,
11877        /**
11878         * @event appendnode
11879         * Fires when append node to the tree
11880         * @param {Roo.tree.TreePanel} this
11881         * @param {Roo.tree.TreeNode} node
11882         * @param {Number} index The index of the newly appended node
11883         */
11884        "appendnode" : true
11885         
11886     });
11887     if(this.singleExpand){
11888        this.on("beforeexpand", this.restrictExpand, this);
11889     }
11890     if (this.editor) {
11891         this.editor.tree = this;
11892         this.editor = Roo.factory(this.editor, Roo.tree);
11893     }
11894     
11895     if (this.selModel) {
11896         this.selModel = Roo.factory(this.selModel, Roo.tree);
11897     }
11898    
11899 };
11900 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11901     rootVisible : true,
11902     animate: Roo.enableFx,
11903     lines : true,
11904     enableDD : false,
11905     hlDrop : Roo.enableFx,
11906   
11907     renderer: false,
11908     
11909     rendererTip: false,
11910     // private
11911     restrictExpand : function(node){
11912         var p = node.parentNode;
11913         if(p){
11914             if(p.expandedChild && p.expandedChild.parentNode == p){
11915                 p.expandedChild.collapse();
11916             }
11917             p.expandedChild = node;
11918         }
11919     },
11920
11921     // private override
11922     setRootNode : function(node){
11923         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11924         if(!this.rootVisible){
11925             node.ui = new Roo.tree.RootTreeNodeUI(node);
11926         }
11927         return node;
11928     },
11929
11930     /**
11931      * Returns the container element for this TreePanel
11932      */
11933     getEl : function(){
11934         return this.el;
11935     },
11936
11937     /**
11938      * Returns the default TreeLoader for this TreePanel
11939      */
11940     getLoader : function(){
11941         return this.loader;
11942     },
11943
11944     /**
11945      * Expand all nodes
11946      */
11947     expandAll : function(){
11948         this.root.expand(true);
11949     },
11950
11951     /**
11952      * Collapse all nodes
11953      */
11954     collapseAll : function(){
11955         this.root.collapse(true);
11956     },
11957
11958     /**
11959      * Returns the selection model used by this TreePanel
11960      */
11961     getSelectionModel : function(){
11962         if(!this.selModel){
11963             this.selModel = new Roo.tree.DefaultSelectionModel();
11964         }
11965         return this.selModel;
11966     },
11967
11968     /**
11969      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11970      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11971      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11972      * @return {Array}
11973      */
11974     getChecked : function(a, startNode){
11975         startNode = startNode || this.root;
11976         var r = [];
11977         var f = function(){
11978             if(this.attributes.checked){
11979                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11980             }
11981         }
11982         startNode.cascade(f);
11983         return r;
11984     },
11985
11986     /**
11987      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11988      * @param {String} path
11989      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11990      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11991      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11992      */
11993     expandPath : function(path, attr, callback){
11994         attr = attr || "id";
11995         var keys = path.split(this.pathSeparator);
11996         var curNode = this.root;
11997         if(curNode.attributes[attr] != keys[1]){ // invalid root
11998             if(callback){
11999                 callback(false, null);
12000             }
12001             return;
12002         }
12003         var index = 1;
12004         var f = function(){
12005             if(++index == keys.length){
12006                 if(callback){
12007                     callback(true, curNode);
12008                 }
12009                 return;
12010             }
12011             var c = curNode.findChild(attr, keys[index]);
12012             if(!c){
12013                 if(callback){
12014                     callback(false, curNode);
12015                 }
12016                 return;
12017             }
12018             curNode = c;
12019             c.expand(false, false, f);
12020         };
12021         curNode.expand(false, false, f);
12022     },
12023
12024     /**
12025      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12026      * @param {String} path
12027      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12028      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12029      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12030      */
12031     selectPath : function(path, attr, callback){
12032         attr = attr || "id";
12033         var keys = path.split(this.pathSeparator);
12034         var v = keys.pop();
12035         if(keys.length > 0){
12036             var f = function(success, node){
12037                 if(success && node){
12038                     var n = node.findChild(attr, v);
12039                     if(n){
12040                         n.select();
12041                         if(callback){
12042                             callback(true, n);
12043                         }
12044                     }else if(callback){
12045                         callback(false, n);
12046                     }
12047                 }else{
12048                     if(callback){
12049                         callback(false, n);
12050                     }
12051                 }
12052             };
12053             this.expandPath(keys.join(this.pathSeparator), attr, f);
12054         }else{
12055             this.root.select();
12056             if(callback){
12057                 callback(true, this.root);
12058             }
12059         }
12060     },
12061
12062     getTreeEl : function(){
12063         return this.el;
12064     },
12065
12066     /**
12067      * Trigger rendering of this TreePanel
12068      */
12069     render : function(){
12070         if (this.innerCt) {
12071             return this; // stop it rendering more than once!!
12072         }
12073         
12074         this.innerCt = this.el.createChild({tag:"ul",
12075                cls:"x-tree-root-ct " +
12076                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12077
12078         if(this.containerScroll){
12079             Roo.dd.ScrollManager.register(this.el);
12080         }
12081         if((this.enableDD || this.enableDrop) && !this.dropZone){
12082            /**
12083             * The dropZone used by this tree if drop is enabled
12084             * @type Roo.tree.TreeDropZone
12085             */
12086              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12087                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12088            });
12089         }
12090         if((this.enableDD || this.enableDrag) && !this.dragZone){
12091            /**
12092             * The dragZone used by this tree if drag is enabled
12093             * @type Roo.tree.TreeDragZone
12094             */
12095             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12096                ddGroup: this.ddGroup || "TreeDD",
12097                scroll: this.ddScroll
12098            });
12099         }
12100         this.getSelectionModel().init(this);
12101         if (!this.root) {
12102             Roo.log("ROOT not set in tree");
12103             return this;
12104         }
12105         this.root.render();
12106         if(!this.rootVisible){
12107             this.root.renderChildren();
12108         }
12109         return this;
12110     }
12111 });/*
12112  * Based on:
12113  * Ext JS Library 1.1.1
12114  * Copyright(c) 2006-2007, Ext JS, LLC.
12115  *
12116  * Originally Released Under LGPL - original licence link has changed is not relivant.
12117  *
12118  * Fork - LGPL
12119  * <script type="text/javascript">
12120  */
12121  
12122
12123 /**
12124  * @class Roo.tree.DefaultSelectionModel
12125  * @extends Roo.util.Observable
12126  * The default single selection for a TreePanel.
12127  * @param {Object} cfg Configuration
12128  */
12129 Roo.tree.DefaultSelectionModel = function(cfg){
12130    this.selNode = null;
12131    
12132    
12133    
12134    this.addEvents({
12135        /**
12136         * @event selectionchange
12137         * Fires when the selected node changes
12138         * @param {DefaultSelectionModel} this
12139         * @param {TreeNode} node the new selection
12140         */
12141        "selectionchange" : true,
12142
12143        /**
12144         * @event beforeselect
12145         * Fires before the selected node changes, return false to cancel the change
12146         * @param {DefaultSelectionModel} this
12147         * @param {TreeNode} node the new selection
12148         * @param {TreeNode} node the old selection
12149         */
12150        "beforeselect" : true
12151    });
12152    
12153     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12154 };
12155
12156 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12157     init : function(tree){
12158         this.tree = tree;
12159         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12160         tree.on("click", this.onNodeClick, this);
12161     },
12162     
12163     onNodeClick : function(node, e){
12164         if (e.ctrlKey && this.selNode == node)  {
12165             this.unselect(node);
12166             return;
12167         }
12168         this.select(node);
12169     },
12170     
12171     /**
12172      * Select a node.
12173      * @param {TreeNode} node The node to select
12174      * @return {TreeNode} The selected node
12175      */
12176     select : function(node){
12177         var last = this.selNode;
12178         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12179             if(last){
12180                 last.ui.onSelectedChange(false);
12181             }
12182             this.selNode = node;
12183             node.ui.onSelectedChange(true);
12184             this.fireEvent("selectionchange", this, node, last);
12185         }
12186         return node;
12187     },
12188     
12189     /**
12190      * Deselect a node.
12191      * @param {TreeNode} node The node to unselect
12192      */
12193     unselect : function(node){
12194         if(this.selNode == node){
12195             this.clearSelections();
12196         }    
12197     },
12198     
12199     /**
12200      * Clear all selections
12201      */
12202     clearSelections : function(){
12203         var n = this.selNode;
12204         if(n){
12205             n.ui.onSelectedChange(false);
12206             this.selNode = null;
12207             this.fireEvent("selectionchange", this, null);
12208         }
12209         return n;
12210     },
12211     
12212     /**
12213      * Get the selected node
12214      * @return {TreeNode} The selected node
12215      */
12216     getSelectedNode : function(){
12217         return this.selNode;    
12218     },
12219     
12220     /**
12221      * Returns true if the node is selected
12222      * @param {TreeNode} node The node to check
12223      * @return {Boolean}
12224      */
12225     isSelected : function(node){
12226         return this.selNode == node;  
12227     },
12228
12229     /**
12230      * Selects the node above the selected node in the tree, intelligently walking the nodes
12231      * @return TreeNode The new selection
12232      */
12233     selectPrevious : function(){
12234         var s = this.selNode || this.lastSelNode;
12235         if(!s){
12236             return null;
12237         }
12238         var ps = s.previousSibling;
12239         if(ps){
12240             if(!ps.isExpanded() || ps.childNodes.length < 1){
12241                 return this.select(ps);
12242             } else{
12243                 var lc = ps.lastChild;
12244                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12245                     lc = lc.lastChild;
12246                 }
12247                 return this.select(lc);
12248             }
12249         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12250             return this.select(s.parentNode);
12251         }
12252         return null;
12253     },
12254
12255     /**
12256      * Selects the node above the selected node in the tree, intelligently walking the nodes
12257      * @return TreeNode The new selection
12258      */
12259     selectNext : function(){
12260         var s = this.selNode || this.lastSelNode;
12261         if(!s){
12262             return null;
12263         }
12264         if(s.firstChild && s.isExpanded()){
12265              return this.select(s.firstChild);
12266          }else if(s.nextSibling){
12267              return this.select(s.nextSibling);
12268          }else if(s.parentNode){
12269             var newS = null;
12270             s.parentNode.bubble(function(){
12271                 if(this.nextSibling){
12272                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12273                     return false;
12274                 }
12275             });
12276             return newS;
12277          }
12278         return null;
12279     },
12280
12281     onKeyDown : function(e){
12282         var s = this.selNode || this.lastSelNode;
12283         // undesirable, but required
12284         var sm = this;
12285         if(!s){
12286             return;
12287         }
12288         var k = e.getKey();
12289         switch(k){
12290              case e.DOWN:
12291                  e.stopEvent();
12292                  this.selectNext();
12293              break;
12294              case e.UP:
12295                  e.stopEvent();
12296                  this.selectPrevious();
12297              break;
12298              case e.RIGHT:
12299                  e.preventDefault();
12300                  if(s.hasChildNodes()){
12301                      if(!s.isExpanded()){
12302                          s.expand();
12303                      }else if(s.firstChild){
12304                          this.select(s.firstChild, e);
12305                      }
12306                  }
12307              break;
12308              case e.LEFT:
12309                  e.preventDefault();
12310                  if(s.hasChildNodes() && s.isExpanded()){
12311                      s.collapse();
12312                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12313                      this.select(s.parentNode, e);
12314                  }
12315              break;
12316         };
12317     }
12318 });
12319
12320 /**
12321  * @class Roo.tree.MultiSelectionModel
12322  * @extends Roo.util.Observable
12323  * Multi selection for a TreePanel.
12324  * @param {Object} cfg Configuration
12325  */
12326 Roo.tree.MultiSelectionModel = function(){
12327    this.selNodes = [];
12328    this.selMap = {};
12329    this.addEvents({
12330        /**
12331         * @event selectionchange
12332         * Fires when the selected nodes change
12333         * @param {MultiSelectionModel} this
12334         * @param {Array} nodes Array of the selected nodes
12335         */
12336        "selectionchange" : true
12337    });
12338    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12339    
12340 };
12341
12342 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12343     init : function(tree){
12344         this.tree = tree;
12345         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12346         tree.on("click", this.onNodeClick, this);
12347     },
12348     
12349     onNodeClick : function(node, e){
12350         this.select(node, e, e.ctrlKey);
12351     },
12352     
12353     /**
12354      * Select a node.
12355      * @param {TreeNode} node The node to select
12356      * @param {EventObject} e (optional) An event associated with the selection
12357      * @param {Boolean} keepExisting True to retain existing selections
12358      * @return {TreeNode} The selected node
12359      */
12360     select : function(node, e, keepExisting){
12361         if(keepExisting !== true){
12362             this.clearSelections(true);
12363         }
12364         if(this.isSelected(node)){
12365             this.lastSelNode = node;
12366             return node;
12367         }
12368         this.selNodes.push(node);
12369         this.selMap[node.id] = node;
12370         this.lastSelNode = node;
12371         node.ui.onSelectedChange(true);
12372         this.fireEvent("selectionchange", this, this.selNodes);
12373         return node;
12374     },
12375     
12376     /**
12377      * Deselect a node.
12378      * @param {TreeNode} node The node to unselect
12379      */
12380     unselect : function(node){
12381         if(this.selMap[node.id]){
12382             node.ui.onSelectedChange(false);
12383             var sn = this.selNodes;
12384             var index = -1;
12385             if(sn.indexOf){
12386                 index = sn.indexOf(node);
12387             }else{
12388                 for(var i = 0, len = sn.length; i < len; i++){
12389                     if(sn[i] == node){
12390                         index = i;
12391                         break;
12392                     }
12393                 }
12394             }
12395             if(index != -1){
12396                 this.selNodes.splice(index, 1);
12397             }
12398             delete this.selMap[node.id];
12399             this.fireEvent("selectionchange", this, this.selNodes);
12400         }
12401     },
12402     
12403     /**
12404      * Clear all selections
12405      */
12406     clearSelections : function(suppressEvent){
12407         var sn = this.selNodes;
12408         if(sn.length > 0){
12409             for(var i = 0, len = sn.length; i < len; i++){
12410                 sn[i].ui.onSelectedChange(false);
12411             }
12412             this.selNodes = [];
12413             this.selMap = {};
12414             if(suppressEvent !== true){
12415                 this.fireEvent("selectionchange", this, this.selNodes);
12416             }
12417         }
12418     },
12419     
12420     /**
12421      * Returns true if the node is selected
12422      * @param {TreeNode} node The node to check
12423      * @return {Boolean}
12424      */
12425     isSelected : function(node){
12426         return this.selMap[node.id] ? true : false;  
12427     },
12428     
12429     /**
12430      * Returns an array of the selected nodes
12431      * @return {Array}
12432      */
12433     getSelectedNodes : function(){
12434         return this.selNodes;    
12435     },
12436
12437     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12438
12439     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12440
12441     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12442 });/*
12443  * Based on:
12444  * Ext JS Library 1.1.1
12445  * Copyright(c) 2006-2007, Ext JS, LLC.
12446  *
12447  * Originally Released Under LGPL - original licence link has changed is not relivant.
12448  *
12449  * Fork - LGPL
12450  * <script type="text/javascript">
12451  */
12452  
12453 /**
12454  * @class Roo.tree.TreeNode
12455  * @extends Roo.data.Node
12456  * @cfg {String} text The text for this node
12457  * @cfg {Boolean} expanded true to start the node expanded
12458  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12459  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12460  * @cfg {Boolean} disabled true to start the node disabled
12461  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12462  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12463  * @cfg {String} cls A css class to be added to the node
12464  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12465  * @cfg {String} href URL of the link used for the node (defaults to #)
12466  * @cfg {String} hrefTarget target frame for the link
12467  * @cfg {String} qtip An Ext QuickTip for the node
12468  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12469  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12470  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12471  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12472  * (defaults to undefined with no checkbox rendered)
12473  * @constructor
12474  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12475  */
12476 Roo.tree.TreeNode = function(attributes){
12477     attributes = attributes || {};
12478     if(typeof attributes == "string"){
12479         attributes = {text: attributes};
12480     }
12481     this.childrenRendered = false;
12482     this.rendered = false;
12483     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12484     this.expanded = attributes.expanded === true;
12485     this.isTarget = attributes.isTarget !== false;
12486     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12487     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12488
12489     /**
12490      * Read-only. The text for this node. To change it use setText().
12491      * @type String
12492      */
12493     this.text = attributes.text;
12494     /**
12495      * True if this node is disabled.
12496      * @type Boolean
12497      */
12498     this.disabled = attributes.disabled === true;
12499
12500     this.addEvents({
12501         /**
12502         * @event textchange
12503         * Fires when the text for this node is changed
12504         * @param {Node} this This node
12505         * @param {String} text The new text
12506         * @param {String} oldText The old text
12507         */
12508         "textchange" : true,
12509         /**
12510         * @event beforeexpand
12511         * Fires before this node is expanded, return false to cancel.
12512         * @param {Node} this This node
12513         * @param {Boolean} deep
12514         * @param {Boolean} anim
12515         */
12516         "beforeexpand" : true,
12517         /**
12518         * @event beforecollapse
12519         * Fires before this node is collapsed, return false to cancel.
12520         * @param {Node} this This node
12521         * @param {Boolean} deep
12522         * @param {Boolean} anim
12523         */
12524         "beforecollapse" : true,
12525         /**
12526         * @event expand
12527         * Fires when this node is expanded
12528         * @param {Node} this This node
12529         */
12530         "expand" : true,
12531         /**
12532         * @event disabledchange
12533         * Fires when the disabled status of this node changes
12534         * @param {Node} this This node
12535         * @param {Boolean} disabled
12536         */
12537         "disabledchange" : true,
12538         /**
12539         * @event collapse
12540         * Fires when this node is collapsed
12541         * @param {Node} this This node
12542         */
12543         "collapse" : true,
12544         /**
12545         * @event beforeclick
12546         * Fires before click processing. Return false to cancel the default action.
12547         * @param {Node} this This node
12548         * @param {Roo.EventObject} e The event object
12549         */
12550         "beforeclick":true,
12551         /**
12552         * @event checkchange
12553         * Fires when a node with a checkbox's checked property changes
12554         * @param {Node} this This node
12555         * @param {Boolean} checked
12556         */
12557         "checkchange":true,
12558         /**
12559         * @event click
12560         * Fires when this node is clicked
12561         * @param {Node} this This node
12562         * @param {Roo.EventObject} e The event object
12563         */
12564         "click":true,
12565         /**
12566         * @event dblclick
12567         * Fires when this node is double clicked
12568         * @param {Node} this This node
12569         * @param {Roo.EventObject} e The event object
12570         */
12571         "dblclick":true,
12572         /**
12573         * @event contextmenu
12574         * Fires when this node is right clicked
12575         * @param {Node} this This node
12576         * @param {Roo.EventObject} e The event object
12577         */
12578         "contextmenu":true,
12579         /**
12580         * @event beforechildrenrendered
12581         * Fires right before the child nodes for this node are rendered
12582         * @param {Node} this This node
12583         */
12584         "beforechildrenrendered":true
12585     });
12586
12587     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12588
12589     /**
12590      * Read-only. The UI for this node
12591      * @type TreeNodeUI
12592      */
12593     this.ui = new uiClass(this);
12594     
12595     // finally support items[]
12596     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12597         return;
12598     }
12599     
12600     
12601     Roo.each(this.attributes.items, function(c) {
12602         this.appendChild(Roo.factory(c,Roo.Tree));
12603     }, this);
12604     delete this.attributes.items;
12605     
12606     
12607     
12608 };
12609 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12610     preventHScroll: true,
12611     /**
12612      * Returns true if this node is expanded
12613      * @return {Boolean}
12614      */
12615     isExpanded : function(){
12616         return this.expanded;
12617     },
12618
12619     /**
12620      * Returns the UI object for this node
12621      * @return {TreeNodeUI}
12622      */
12623     getUI : function(){
12624         return this.ui;
12625     },
12626
12627     // private override
12628     setFirstChild : function(node){
12629         var of = this.firstChild;
12630         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12631         if(this.childrenRendered && of && node != of){
12632             of.renderIndent(true, true);
12633         }
12634         if(this.rendered){
12635             this.renderIndent(true, true);
12636         }
12637     },
12638
12639     // private override
12640     setLastChild : function(node){
12641         var ol = this.lastChild;
12642         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12643         if(this.childrenRendered && ol && node != ol){
12644             ol.renderIndent(true, true);
12645         }
12646         if(this.rendered){
12647             this.renderIndent(true, true);
12648         }
12649     },
12650
12651     // these methods are overridden to provide lazy rendering support
12652     // private override
12653     appendChild : function()
12654     {
12655         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12656         if(node && this.childrenRendered){
12657             node.render();
12658         }
12659         this.ui.updateExpandIcon();
12660         return node;
12661     },
12662
12663     // private override
12664     removeChild : function(node){
12665         this.ownerTree.getSelectionModel().unselect(node);
12666         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12667         // if it's been rendered remove dom node
12668         if(this.childrenRendered){
12669             node.ui.remove();
12670         }
12671         if(this.childNodes.length < 1){
12672             this.collapse(false, false);
12673         }else{
12674             this.ui.updateExpandIcon();
12675         }
12676         if(!this.firstChild) {
12677             this.childrenRendered = false;
12678         }
12679         return node;
12680     },
12681
12682     // private override
12683     insertBefore : function(node, refNode){
12684         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12685         if(newNode && refNode && this.childrenRendered){
12686             node.render();
12687         }
12688         this.ui.updateExpandIcon();
12689         return newNode;
12690     },
12691
12692     /**
12693      * Sets the text for this node
12694      * @param {String} text
12695      */
12696     setText : function(text){
12697         var oldText = this.text;
12698         this.text = text;
12699         this.attributes.text = text;
12700         if(this.rendered){ // event without subscribing
12701             this.ui.onTextChange(this, text, oldText);
12702         }
12703         this.fireEvent("textchange", this, text, oldText);
12704     },
12705
12706     /**
12707      * Triggers selection of this node
12708      */
12709     select : function(){
12710         this.getOwnerTree().getSelectionModel().select(this);
12711     },
12712
12713     /**
12714      * Triggers deselection of this node
12715      */
12716     unselect : function(){
12717         this.getOwnerTree().getSelectionModel().unselect(this);
12718     },
12719
12720     /**
12721      * Returns true if this node is selected
12722      * @return {Boolean}
12723      */
12724     isSelected : function(){
12725         return this.getOwnerTree().getSelectionModel().isSelected(this);
12726     },
12727
12728     /**
12729      * Expand this node.
12730      * @param {Boolean} deep (optional) True to expand all children as well
12731      * @param {Boolean} anim (optional) false to cancel the default animation
12732      * @param {Function} callback (optional) A callback to be called when
12733      * expanding this node completes (does not wait for deep expand to complete).
12734      * Called with 1 parameter, this node.
12735      */
12736     expand : function(deep, anim, callback){
12737         if(!this.expanded){
12738             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12739                 return;
12740             }
12741             if(!this.childrenRendered){
12742                 this.renderChildren();
12743             }
12744             this.expanded = true;
12745             
12746             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12747                 this.ui.animExpand(function(){
12748                     this.fireEvent("expand", this);
12749                     if(typeof callback == "function"){
12750                         callback(this);
12751                     }
12752                     if(deep === true){
12753                         this.expandChildNodes(true);
12754                     }
12755                 }.createDelegate(this));
12756                 return;
12757             }else{
12758                 this.ui.expand();
12759                 this.fireEvent("expand", this);
12760                 if(typeof callback == "function"){
12761                     callback(this);
12762                 }
12763             }
12764         }else{
12765            if(typeof callback == "function"){
12766                callback(this);
12767            }
12768         }
12769         if(deep === true){
12770             this.expandChildNodes(true);
12771         }
12772     },
12773
12774     isHiddenRoot : function(){
12775         return this.isRoot && !this.getOwnerTree().rootVisible;
12776     },
12777
12778     /**
12779      * Collapse this node.
12780      * @param {Boolean} deep (optional) True to collapse all children as well
12781      * @param {Boolean} anim (optional) false to cancel the default animation
12782      */
12783     collapse : function(deep, anim){
12784         if(this.expanded && !this.isHiddenRoot()){
12785             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12786                 return;
12787             }
12788             this.expanded = false;
12789             if((this.getOwnerTree().animate && anim !== false) || anim){
12790                 this.ui.animCollapse(function(){
12791                     this.fireEvent("collapse", this);
12792                     if(deep === true){
12793                         this.collapseChildNodes(true);
12794                     }
12795                 }.createDelegate(this));
12796                 return;
12797             }else{
12798                 this.ui.collapse();
12799                 this.fireEvent("collapse", this);
12800             }
12801         }
12802         if(deep === true){
12803             var cs = this.childNodes;
12804             for(var i = 0, len = cs.length; i < len; i++) {
12805                 cs[i].collapse(true, false);
12806             }
12807         }
12808     },
12809
12810     // private
12811     delayedExpand : function(delay){
12812         if(!this.expandProcId){
12813             this.expandProcId = this.expand.defer(delay, this);
12814         }
12815     },
12816
12817     // private
12818     cancelExpand : function(){
12819         if(this.expandProcId){
12820             clearTimeout(this.expandProcId);
12821         }
12822         this.expandProcId = false;
12823     },
12824
12825     /**
12826      * Toggles expanded/collapsed state of the node
12827      */
12828     toggle : function(){
12829         if(this.expanded){
12830             this.collapse();
12831         }else{
12832             this.expand();
12833         }
12834     },
12835
12836     /**
12837      * Ensures all parent nodes are expanded
12838      */
12839     ensureVisible : function(callback){
12840         var tree = this.getOwnerTree();
12841         tree.expandPath(this.parentNode.getPath(), false, function(){
12842             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12843             Roo.callback(callback);
12844         }.createDelegate(this));
12845     },
12846
12847     /**
12848      * Expand all child nodes
12849      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12850      */
12851     expandChildNodes : function(deep){
12852         var cs = this.childNodes;
12853         for(var i = 0, len = cs.length; i < len; i++) {
12854                 cs[i].expand(deep);
12855         }
12856     },
12857
12858     /**
12859      * Collapse all child nodes
12860      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12861      */
12862     collapseChildNodes : function(deep){
12863         var cs = this.childNodes;
12864         for(var i = 0, len = cs.length; i < len; i++) {
12865                 cs[i].collapse(deep);
12866         }
12867     },
12868
12869     /**
12870      * Disables this node
12871      */
12872     disable : function(){
12873         this.disabled = true;
12874         this.unselect();
12875         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12876             this.ui.onDisableChange(this, true);
12877         }
12878         this.fireEvent("disabledchange", this, true);
12879     },
12880
12881     /**
12882      * Enables this node
12883      */
12884     enable : function(){
12885         this.disabled = false;
12886         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12887             this.ui.onDisableChange(this, false);
12888         }
12889         this.fireEvent("disabledchange", this, false);
12890     },
12891
12892     // private
12893     renderChildren : function(suppressEvent){
12894         if(suppressEvent !== false){
12895             this.fireEvent("beforechildrenrendered", this);
12896         }
12897         var cs = this.childNodes;
12898         for(var i = 0, len = cs.length; i < len; i++){
12899             cs[i].render(true);
12900         }
12901         this.childrenRendered = true;
12902     },
12903
12904     // private
12905     sort : function(fn, scope){
12906         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12907         if(this.childrenRendered){
12908             var cs = this.childNodes;
12909             for(var i = 0, len = cs.length; i < len; i++){
12910                 cs[i].render(true);
12911             }
12912         }
12913     },
12914
12915     // private
12916     render : function(bulkRender){
12917         this.ui.render(bulkRender);
12918         if(!this.rendered){
12919             this.rendered = true;
12920             if(this.expanded){
12921                 this.expanded = false;
12922                 this.expand(false, false);
12923             }
12924         }
12925     },
12926
12927     // private
12928     renderIndent : function(deep, refresh){
12929         if(refresh){
12930             this.ui.childIndent = null;
12931         }
12932         this.ui.renderIndent();
12933         if(deep === true && this.childrenRendered){
12934             var cs = this.childNodes;
12935             for(var i = 0, len = cs.length; i < len; i++){
12936                 cs[i].renderIndent(true, refresh);
12937             }
12938         }
12939     }
12940 });/*
12941  * Based on:
12942  * Ext JS Library 1.1.1
12943  * Copyright(c) 2006-2007, Ext JS, LLC.
12944  *
12945  * Originally Released Under LGPL - original licence link has changed is not relivant.
12946  *
12947  * Fork - LGPL
12948  * <script type="text/javascript">
12949  */
12950  
12951 /**
12952  * @class Roo.tree.AsyncTreeNode
12953  * @extends Roo.tree.TreeNode
12954  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12955  * @constructor
12956  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12957  */
12958  Roo.tree.AsyncTreeNode = function(config){
12959     this.loaded = false;
12960     this.loading = false;
12961     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12962     /**
12963     * @event beforeload
12964     * Fires before this node is loaded, return false to cancel
12965     * @param {Node} this This node
12966     */
12967     this.addEvents({'beforeload':true, 'load': true});
12968     /**
12969     * @event load
12970     * Fires when this node is loaded
12971     * @param {Node} this This node
12972     */
12973     /**
12974      * The loader used by this node (defaults to using the tree's defined loader)
12975      * @type TreeLoader
12976      * @property loader
12977      */
12978 };
12979 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12980     expand : function(deep, anim, callback){
12981         if(this.loading){ // if an async load is already running, waiting til it's done
12982             var timer;
12983             var f = function(){
12984                 if(!this.loading){ // done loading
12985                     clearInterval(timer);
12986                     this.expand(deep, anim, callback);
12987                 }
12988             }.createDelegate(this);
12989             timer = setInterval(f, 200);
12990             return;
12991         }
12992         if(!this.loaded){
12993             if(this.fireEvent("beforeload", this) === false){
12994                 return;
12995             }
12996             this.loading = true;
12997             this.ui.beforeLoad(this);
12998             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12999             if(loader){
13000                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
13001                 return;
13002             }
13003         }
13004         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
13005     },
13006     
13007     /**
13008      * Returns true if this node is currently loading
13009      * @return {Boolean}
13010      */
13011     isLoading : function(){
13012         return this.loading;  
13013     },
13014     
13015     loadComplete : function(deep, anim, callback){
13016         this.loading = false;
13017         this.loaded = true;
13018         this.ui.afterLoad(this);
13019         this.fireEvent("load", this);
13020         this.expand(deep, anim, callback);
13021     },
13022     
13023     /**
13024      * Returns true if this node has been loaded
13025      * @return {Boolean}
13026      */
13027     isLoaded : function(){
13028         return this.loaded;
13029     },
13030     
13031     hasChildNodes : function(){
13032         if(!this.isLeaf() && !this.loaded){
13033             return true;
13034         }else{
13035             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13036         }
13037     },
13038
13039     /**
13040      * Trigger a reload for this node
13041      * @param {Function} callback
13042      */
13043     reload : function(callback){
13044         this.collapse(false, false);
13045         while(this.firstChild){
13046             this.removeChild(this.firstChild);
13047         }
13048         this.childrenRendered = false;
13049         this.loaded = false;
13050         if(this.isHiddenRoot()){
13051             this.expanded = false;
13052         }
13053         this.expand(false, false, callback);
13054     }
13055 });/*
13056  * Based on:
13057  * Ext JS Library 1.1.1
13058  * Copyright(c) 2006-2007, Ext JS, LLC.
13059  *
13060  * Originally Released Under LGPL - original licence link has changed is not relivant.
13061  *
13062  * Fork - LGPL
13063  * <script type="text/javascript">
13064  */
13065  
13066 /**
13067  * @class Roo.tree.TreeNodeUI
13068  * @constructor
13069  * @param {Object} node The node to render
13070  * The TreeNode UI implementation is separate from the
13071  * tree implementation. Unless you are customizing the tree UI,
13072  * you should never have to use this directly.
13073  */
13074 Roo.tree.TreeNodeUI = function(node){
13075     this.node = node;
13076     this.rendered = false;
13077     this.animating = false;
13078     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13079 };
13080
13081 Roo.tree.TreeNodeUI.prototype = {
13082     removeChild : function(node){
13083         if(this.rendered){
13084             this.ctNode.removeChild(node.ui.getEl());
13085         }
13086     },
13087
13088     beforeLoad : function(){
13089          this.addClass("x-tree-node-loading");
13090     },
13091
13092     afterLoad : function(){
13093          this.removeClass("x-tree-node-loading");
13094     },
13095
13096     onTextChange : function(node, text, oldText){
13097         if(this.rendered){
13098             this.textNode.innerHTML = text;
13099         }
13100     },
13101
13102     onDisableChange : function(node, state){
13103         this.disabled = state;
13104         if(state){
13105             this.addClass("x-tree-node-disabled");
13106         }else{
13107             this.removeClass("x-tree-node-disabled");
13108         }
13109     },
13110
13111     onSelectedChange : function(state){
13112         if(state){
13113             this.focus();
13114             this.addClass("x-tree-selected");
13115         }else{
13116             //this.blur();
13117             this.removeClass("x-tree-selected");
13118         }
13119     },
13120
13121     onMove : function(tree, node, oldParent, newParent, index, refNode){
13122         this.childIndent = null;
13123         if(this.rendered){
13124             var targetNode = newParent.ui.getContainer();
13125             if(!targetNode){//target not rendered
13126                 this.holder = document.createElement("div");
13127                 this.holder.appendChild(this.wrap);
13128                 return;
13129             }
13130             var insertBefore = refNode ? refNode.ui.getEl() : null;
13131             if(insertBefore){
13132                 targetNode.insertBefore(this.wrap, insertBefore);
13133             }else{
13134                 targetNode.appendChild(this.wrap);
13135             }
13136             this.node.renderIndent(true);
13137         }
13138     },
13139
13140     addClass : function(cls){
13141         if(this.elNode){
13142             Roo.fly(this.elNode).addClass(cls);
13143         }
13144     },
13145
13146     removeClass : function(cls){
13147         if(this.elNode){
13148             Roo.fly(this.elNode).removeClass(cls);
13149         }
13150     },
13151
13152     remove : function(){
13153         if(this.rendered){
13154             this.holder = document.createElement("div");
13155             this.holder.appendChild(this.wrap);
13156         }
13157     },
13158
13159     fireEvent : function(){
13160         return this.node.fireEvent.apply(this.node, arguments);
13161     },
13162
13163     initEvents : function(){
13164         this.node.on("move", this.onMove, this);
13165         var E = Roo.EventManager;
13166         var a = this.anchor;
13167
13168         var el = Roo.fly(a, '_treeui');
13169
13170         if(Roo.isOpera){ // opera render bug ignores the CSS
13171             el.setStyle("text-decoration", "none");
13172         }
13173
13174         el.on("click", this.onClick, this);
13175         el.on("dblclick", this.onDblClick, this);
13176
13177         if(this.checkbox){
13178             Roo.EventManager.on(this.checkbox,
13179                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13180         }
13181
13182         el.on("contextmenu", this.onContextMenu, this);
13183
13184         var icon = Roo.fly(this.iconNode);
13185         icon.on("click", this.onClick, this);
13186         icon.on("dblclick", this.onDblClick, this);
13187         icon.on("contextmenu", this.onContextMenu, this);
13188         E.on(this.ecNode, "click", this.ecClick, this, true);
13189
13190         if(this.node.disabled){
13191             this.addClass("x-tree-node-disabled");
13192         }
13193         if(this.node.hidden){
13194             this.addClass("x-tree-node-disabled");
13195         }
13196         var ot = this.node.getOwnerTree();
13197         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13198         if(dd && (!this.node.isRoot || ot.rootVisible)){
13199             Roo.dd.Registry.register(this.elNode, {
13200                 node: this.node,
13201                 handles: this.getDDHandles(),
13202                 isHandle: false
13203             });
13204         }
13205     },
13206
13207     getDDHandles : function(){
13208         return [this.iconNode, this.textNode];
13209     },
13210
13211     hide : function(){
13212         if(this.rendered){
13213             this.wrap.style.display = "none";
13214         }
13215     },
13216
13217     show : function(){
13218         if(this.rendered){
13219             this.wrap.style.display = "";
13220         }
13221     },
13222
13223     onContextMenu : function(e){
13224         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13225             e.preventDefault();
13226             this.focus();
13227             this.fireEvent("contextmenu", this.node, e);
13228         }
13229     },
13230
13231     onClick : function(e){
13232         if(this.dropping){
13233             e.stopEvent();
13234             return;
13235         }
13236         if(this.fireEvent("beforeclick", this.node, e) !== false){
13237             if(!this.disabled && this.node.attributes.href){
13238                 this.fireEvent("click", this.node, e);
13239                 return;
13240             }
13241             e.preventDefault();
13242             if(this.disabled){
13243                 return;
13244             }
13245
13246             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13247                 this.node.toggle();
13248             }
13249
13250             this.fireEvent("click", this.node, e);
13251         }else{
13252             e.stopEvent();
13253         }
13254     },
13255
13256     onDblClick : function(e){
13257         e.preventDefault();
13258         if(this.disabled){
13259             return;
13260         }
13261         if(this.checkbox){
13262             this.toggleCheck();
13263         }
13264         if(!this.animating && this.node.hasChildNodes()){
13265             this.node.toggle();
13266         }
13267         this.fireEvent("dblclick", this.node, e);
13268     },
13269
13270     onCheckChange : function(){
13271         var checked = this.checkbox.checked;
13272         this.node.attributes.checked = checked;
13273         this.fireEvent('checkchange', this.node, checked);
13274     },
13275
13276     ecClick : function(e){
13277         if(!this.animating && this.node.hasChildNodes()){
13278             this.node.toggle();
13279         }
13280     },
13281
13282     startDrop : function(){
13283         this.dropping = true;
13284     },
13285
13286     // delayed drop so the click event doesn't get fired on a drop
13287     endDrop : function(){
13288        setTimeout(function(){
13289            this.dropping = false;
13290        }.createDelegate(this), 50);
13291     },
13292
13293     expand : function(){
13294         this.updateExpandIcon();
13295         this.ctNode.style.display = "";
13296     },
13297
13298     focus : function(){
13299         if(!this.node.preventHScroll){
13300             try{this.anchor.focus();
13301             }catch(e){}
13302         }else if(!Roo.isIE){
13303             try{
13304                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13305                 var l = noscroll.scrollLeft;
13306                 this.anchor.focus();
13307                 noscroll.scrollLeft = l;
13308             }catch(e){}
13309         }
13310     },
13311
13312     toggleCheck : function(value){
13313         var cb = this.checkbox;
13314         if(cb){
13315             cb.checked = (value === undefined ? !cb.checked : value);
13316         }
13317     },
13318
13319     blur : function(){
13320         try{
13321             this.anchor.blur();
13322         }catch(e){}
13323     },
13324
13325     animExpand : function(callback){
13326         var ct = Roo.get(this.ctNode);
13327         ct.stopFx();
13328         if(!this.node.hasChildNodes()){
13329             this.updateExpandIcon();
13330             this.ctNode.style.display = "";
13331             Roo.callback(callback);
13332             return;
13333         }
13334         this.animating = true;
13335         this.updateExpandIcon();
13336
13337         ct.slideIn('t', {
13338            callback : function(){
13339                this.animating = false;
13340                Roo.callback(callback);
13341             },
13342             scope: this,
13343             duration: this.node.ownerTree.duration || .25
13344         });
13345     },
13346
13347     highlight : function(){
13348         var tree = this.node.getOwnerTree();
13349         Roo.fly(this.wrap).highlight(
13350             tree.hlColor || "C3DAF9",
13351             {endColor: tree.hlBaseColor}
13352         );
13353     },
13354
13355     collapse : function(){
13356         this.updateExpandIcon();
13357         this.ctNode.style.display = "none";
13358     },
13359
13360     animCollapse : function(callback){
13361         var ct = Roo.get(this.ctNode);
13362         ct.enableDisplayMode('block');
13363         ct.stopFx();
13364
13365         this.animating = true;
13366         this.updateExpandIcon();
13367
13368         ct.slideOut('t', {
13369             callback : function(){
13370                this.animating = false;
13371                Roo.callback(callback);
13372             },
13373             scope: this,
13374             duration: this.node.ownerTree.duration || .25
13375         });
13376     },
13377
13378     getContainer : function(){
13379         return this.ctNode;
13380     },
13381
13382     getEl : function(){
13383         return this.wrap;
13384     },
13385
13386     appendDDGhost : function(ghostNode){
13387         ghostNode.appendChild(this.elNode.cloneNode(true));
13388     },
13389
13390     getDDRepairXY : function(){
13391         return Roo.lib.Dom.getXY(this.iconNode);
13392     },
13393
13394     onRender : function(){
13395         this.render();
13396     },
13397
13398     render : function(bulkRender){
13399         var n = this.node, a = n.attributes;
13400         var targetNode = n.parentNode ?
13401               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13402
13403         if(!this.rendered){
13404             this.rendered = true;
13405
13406             this.renderElements(n, a, targetNode, bulkRender);
13407
13408             if(a.qtip){
13409                if(this.textNode.setAttributeNS){
13410                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13411                    if(a.qtipTitle){
13412                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13413                    }
13414                }else{
13415                    this.textNode.setAttribute("ext:qtip", a.qtip);
13416                    if(a.qtipTitle){
13417                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13418                    }
13419                }
13420             }else if(a.qtipCfg){
13421                 a.qtipCfg.target = Roo.id(this.textNode);
13422                 Roo.QuickTips.register(a.qtipCfg);
13423             }
13424             this.initEvents();
13425             if(!this.node.expanded){
13426                 this.updateExpandIcon();
13427             }
13428         }else{
13429             if(bulkRender === true) {
13430                 targetNode.appendChild(this.wrap);
13431             }
13432         }
13433     },
13434
13435     renderElements : function(n, a, targetNode, bulkRender)
13436     {
13437         // add some indent caching, this helps performance when rendering a large tree
13438         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13439         var t = n.getOwnerTree();
13440         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13441         if (typeof(n.attributes.html) != 'undefined') {
13442             txt = n.attributes.html;
13443         }
13444         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13445         var cb = typeof a.checked == 'boolean';
13446         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13447         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13448             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13449             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13450             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13451             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13452             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13453              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13454                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13455             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13456             "</li>"];
13457
13458         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13459             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13460                                 n.nextSibling.ui.getEl(), buf.join(""));
13461         }else{
13462             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13463         }
13464
13465         this.elNode = this.wrap.childNodes[0];
13466         this.ctNode = this.wrap.childNodes[1];
13467         var cs = this.elNode.childNodes;
13468         this.indentNode = cs[0];
13469         this.ecNode = cs[1];
13470         this.iconNode = cs[2];
13471         var index = 3;
13472         if(cb){
13473             this.checkbox = cs[3];
13474             index++;
13475         }
13476         this.anchor = cs[index];
13477         this.textNode = cs[index].firstChild;
13478     },
13479
13480     getAnchor : function(){
13481         return this.anchor;
13482     },
13483
13484     getTextEl : function(){
13485         return this.textNode;
13486     },
13487
13488     getIconEl : function(){
13489         return this.iconNode;
13490     },
13491
13492     isChecked : function(){
13493         return this.checkbox ? this.checkbox.checked : false;
13494     },
13495
13496     updateExpandIcon : function(){
13497         if(this.rendered){
13498             var n = this.node, c1, c2;
13499             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13500             var hasChild = n.hasChildNodes();
13501             if(hasChild){
13502                 if(n.expanded){
13503                     cls += "-minus";
13504                     c1 = "x-tree-node-collapsed";
13505                     c2 = "x-tree-node-expanded";
13506                 }else{
13507                     cls += "-plus";
13508                     c1 = "x-tree-node-expanded";
13509                     c2 = "x-tree-node-collapsed";
13510                 }
13511                 if(this.wasLeaf){
13512                     this.removeClass("x-tree-node-leaf");
13513                     this.wasLeaf = false;
13514                 }
13515                 if(this.c1 != c1 || this.c2 != c2){
13516                     Roo.fly(this.elNode).replaceClass(c1, c2);
13517                     this.c1 = c1; this.c2 = c2;
13518                 }
13519             }else{
13520                 // this changes non-leafs into leafs if they have no children.
13521                 // it's not very rational behaviour..
13522                 
13523                 if(!this.wasLeaf && this.node.leaf){
13524                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13525                     delete this.c1;
13526                     delete this.c2;
13527                     this.wasLeaf = true;
13528                 }
13529             }
13530             var ecc = "x-tree-ec-icon "+cls;
13531             if(this.ecc != ecc){
13532                 this.ecNode.className = ecc;
13533                 this.ecc = ecc;
13534             }
13535         }
13536     },
13537
13538     getChildIndent : function(){
13539         if(!this.childIndent){
13540             var buf = [];
13541             var p = this.node;
13542             while(p){
13543                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13544                     if(!p.isLast()) {
13545                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13546                     } else {
13547                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13548                     }
13549                 }
13550                 p = p.parentNode;
13551             }
13552             this.childIndent = buf.join("");
13553         }
13554         return this.childIndent;
13555     },
13556
13557     renderIndent : function(){
13558         if(this.rendered){
13559             var indent = "";
13560             var p = this.node.parentNode;
13561             if(p){
13562                 indent = p.ui.getChildIndent();
13563             }
13564             if(this.indentMarkup != indent){ // don't rerender if not required
13565                 this.indentNode.innerHTML = indent;
13566                 this.indentMarkup = indent;
13567             }
13568             this.updateExpandIcon();
13569         }
13570     }
13571 };
13572
13573 Roo.tree.RootTreeNodeUI = function(){
13574     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13575 };
13576 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13577     render : function(){
13578         if(!this.rendered){
13579             var targetNode = this.node.ownerTree.innerCt.dom;
13580             this.node.expanded = true;
13581             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13582             this.wrap = this.ctNode = targetNode.firstChild;
13583         }
13584     },
13585     collapse : function(){
13586     },
13587     expand : function(){
13588     }
13589 });/*
13590  * Based on:
13591  * Ext JS Library 1.1.1
13592  * Copyright(c) 2006-2007, Ext JS, LLC.
13593  *
13594  * Originally Released Under LGPL - original licence link has changed is not relivant.
13595  *
13596  * Fork - LGPL
13597  * <script type="text/javascript">
13598  */
13599 /**
13600  * @class Roo.tree.TreeLoader
13601  * @extends Roo.util.Observable
13602  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13603  * nodes from a specified URL. The response must be a javascript Array definition
13604  * who's elements are node definition objects. eg:
13605  * <pre><code>
13606 {  success : true,
13607    data :      [
13608    
13609     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13610     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13611     ]
13612 }
13613
13614
13615 </code></pre>
13616  * <br><br>
13617  * The old style respose with just an array is still supported, but not recommended.
13618  * <br><br>
13619  *
13620  * A server request is sent, and child nodes are loaded only when a node is expanded.
13621  * The loading node's id is passed to the server under the parameter name "node" to
13622  * enable the server to produce the correct child nodes.
13623  * <br><br>
13624  * To pass extra parameters, an event handler may be attached to the "beforeload"
13625  * event, and the parameters specified in the TreeLoader's baseParams property:
13626  * <pre><code>
13627     myTreeLoader.on("beforeload", function(treeLoader, node) {
13628         this.baseParams.category = node.attributes.category;
13629     }, this);
13630     
13631 </code></pre>
13632  *
13633  * This would pass an HTTP parameter called "category" to the server containing
13634  * the value of the Node's "category" attribute.
13635  * @constructor
13636  * Creates a new Treeloader.
13637  * @param {Object} config A config object containing config properties.
13638  */
13639 Roo.tree.TreeLoader = function(config){
13640     this.baseParams = {};
13641     this.requestMethod = "POST";
13642     Roo.apply(this, config);
13643
13644     this.addEvents({
13645     
13646         /**
13647          * @event beforeload
13648          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13649          * @param {Object} This TreeLoader object.
13650          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13651          * @param {Object} callback The callback function specified in the {@link #load} call.
13652          */
13653         beforeload : true,
13654         /**
13655          * @event load
13656          * Fires when the node has been successfuly loaded.
13657          * @param {Object} This TreeLoader object.
13658          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13659          * @param {Object} response The response object containing the data from the server.
13660          */
13661         load : true,
13662         /**
13663          * @event loadexception
13664          * Fires if the network request failed.
13665          * @param {Object} This TreeLoader object.
13666          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13667          * @param {Object} response The response object containing the data from the server.
13668          */
13669         loadexception : true,
13670         /**
13671          * @event create
13672          * Fires before a node is created, enabling you to return custom Node types 
13673          * @param {Object} This TreeLoader object.
13674          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13675          */
13676         create : true
13677     });
13678
13679     Roo.tree.TreeLoader.superclass.constructor.call(this);
13680 };
13681
13682 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13683     /**
13684     * @cfg {String} dataUrl The URL from which to request a Json string which
13685     * specifies an array of node definition object representing the child nodes
13686     * to be loaded.
13687     */
13688     /**
13689     * @cfg {String} requestMethod either GET or POST
13690     * defaults to POST (due to BC)
13691     * to be loaded.
13692     */
13693     /**
13694     * @cfg {Object} baseParams (optional) An object containing properties which
13695     * specify HTTP parameters to be passed to each request for child nodes.
13696     */
13697     /**
13698     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13699     * created by this loader. If the attributes sent by the server have an attribute in this object,
13700     * they take priority.
13701     */
13702     /**
13703     * @cfg {Object} uiProviders (optional) An object containing properties which
13704     * 
13705     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13706     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13707     * <i>uiProvider</i> attribute of a returned child node is a string rather
13708     * than a reference to a TreeNodeUI implementation, this that string value
13709     * is used as a property name in the uiProviders object. You can define the provider named
13710     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13711     */
13712     uiProviders : {},
13713
13714     /**
13715     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13716     * child nodes before loading.
13717     */
13718     clearOnLoad : true,
13719
13720     /**
13721     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13722     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13723     * Grid query { data : [ .....] }
13724     */
13725     
13726     root : false,
13727      /**
13728     * @cfg {String} queryParam (optional) 
13729     * Name of the query as it will be passed on the querystring (defaults to 'node')
13730     * eg. the request will be ?node=[id]
13731     */
13732     
13733     
13734     queryParam: false,
13735     
13736     /**
13737      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13738      * This is called automatically when a node is expanded, but may be used to reload
13739      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13740      * @param {Roo.tree.TreeNode} node
13741      * @param {Function} callback
13742      */
13743     load : function(node, callback){
13744         if(this.clearOnLoad){
13745             while(node.firstChild){
13746                 node.removeChild(node.firstChild);
13747             }
13748         }
13749         if(node.attributes.children){ // preloaded json children
13750             var cs = node.attributes.children;
13751             for(var i = 0, len = cs.length; i < len; i++){
13752                 node.appendChild(this.createNode(cs[i]));
13753             }
13754             if(typeof callback == "function"){
13755                 callback();
13756             }
13757         }else if(this.dataUrl){
13758             this.requestData(node, callback);
13759         }
13760     },
13761
13762     getParams: function(node){
13763         var buf = [], bp = this.baseParams;
13764         for(var key in bp){
13765             if(typeof bp[key] != "function"){
13766                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13767             }
13768         }
13769         var n = this.queryParam === false ? 'node' : this.queryParam;
13770         buf.push(n + "=", encodeURIComponent(node.id));
13771         return buf.join("");
13772     },
13773
13774     requestData : function(node, callback){
13775         if(this.fireEvent("beforeload", this, node, callback) !== false){
13776             this.transId = Roo.Ajax.request({
13777                 method:this.requestMethod,
13778                 url: this.dataUrl||this.url,
13779                 success: this.handleResponse,
13780                 failure: this.handleFailure,
13781                 scope: this,
13782                 argument: {callback: callback, node: node},
13783                 params: this.getParams(node)
13784             });
13785         }else{
13786             // if the load is cancelled, make sure we notify
13787             // the node that we are done
13788             if(typeof callback == "function"){
13789                 callback();
13790             }
13791         }
13792     },
13793
13794     isLoading : function(){
13795         return this.transId ? true : false;
13796     },
13797
13798     abort : function(){
13799         if(this.isLoading()){
13800             Roo.Ajax.abort(this.transId);
13801         }
13802     },
13803
13804     // private
13805     createNode : function(attr)
13806     {
13807         // apply baseAttrs, nice idea Corey!
13808         if(this.baseAttrs){
13809             Roo.applyIf(attr, this.baseAttrs);
13810         }
13811         if(this.applyLoader !== false){
13812             attr.loader = this;
13813         }
13814         // uiProvider = depreciated..
13815         
13816         if(typeof(attr.uiProvider) == 'string'){
13817            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13818                 /**  eval:var:attr */ eval(attr.uiProvider);
13819         }
13820         if(typeof(this.uiProviders['default']) != 'undefined') {
13821             attr.uiProvider = this.uiProviders['default'];
13822         }
13823         
13824         this.fireEvent('create', this, attr);
13825         
13826         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13827         return(attr.leaf ?
13828                         new Roo.tree.TreeNode(attr) :
13829                         new Roo.tree.AsyncTreeNode(attr));
13830     },
13831
13832     processResponse : function(response, node, callback)
13833     {
13834         var json = response.responseText;
13835         try {
13836             
13837             var o = Roo.decode(json);
13838             
13839             if (this.root === false && typeof(o.success) != undefined) {
13840                 this.root = 'data'; // the default behaviour for list like data..
13841                 }
13842                 
13843             if (this.root !== false &&  !o.success) {
13844                 // it's a failure condition.
13845                 var a = response.argument;
13846                 this.fireEvent("loadexception", this, a.node, response);
13847                 Roo.log("Load failed - should have a handler really");
13848                 return;
13849             }
13850             
13851             
13852             
13853             if (this.root !== false) {
13854                  o = o[this.root];
13855             }
13856             
13857             for(var i = 0, len = o.length; i < len; i++){
13858                 var n = this.createNode(o[i]);
13859                 if(n){
13860                     node.appendChild(n);
13861                 }
13862             }
13863             if(typeof callback == "function"){
13864                 callback(this, node);
13865             }
13866         }catch(e){
13867             this.handleFailure(response);
13868         }
13869     },
13870
13871     handleResponse : function(response){
13872         this.transId = false;
13873         var a = response.argument;
13874         this.processResponse(response, a.node, a.callback);
13875         this.fireEvent("load", this, a.node, response);
13876     },
13877
13878     handleFailure : function(response)
13879     {
13880         // should handle failure better..
13881         this.transId = false;
13882         var a = response.argument;
13883         this.fireEvent("loadexception", this, a.node, response);
13884         if(typeof a.callback == "function"){
13885             a.callback(this, a.node);
13886         }
13887     }
13888 });/*
13889  * Based on:
13890  * Ext JS Library 1.1.1
13891  * Copyright(c) 2006-2007, Ext JS, LLC.
13892  *
13893  * Originally Released Under LGPL - original licence link has changed is not relivant.
13894  *
13895  * Fork - LGPL
13896  * <script type="text/javascript">
13897  */
13898
13899 /**
13900 * @class Roo.tree.TreeFilter
13901 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13902 * @param {TreePanel} tree
13903 * @param {Object} config (optional)
13904  */
13905 Roo.tree.TreeFilter = function(tree, config){
13906     this.tree = tree;
13907     this.filtered = {};
13908     Roo.apply(this, config);
13909 };
13910
13911 Roo.tree.TreeFilter.prototype = {
13912     clearBlank:false,
13913     reverse:false,
13914     autoClear:false,
13915     remove:false,
13916
13917      /**
13918      * Filter the data by a specific attribute.
13919      * @param {String/RegExp} value Either string that the attribute value
13920      * should start with or a RegExp to test against the attribute
13921      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13922      * @param {TreeNode} startNode (optional) The node to start the filter at.
13923      */
13924     filter : function(value, attr, startNode){
13925         attr = attr || "text";
13926         var f;
13927         if(typeof value == "string"){
13928             var vlen = value.length;
13929             // auto clear empty filter
13930             if(vlen == 0 && this.clearBlank){
13931                 this.clear();
13932                 return;
13933             }
13934             value = value.toLowerCase();
13935             f = function(n){
13936                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13937             };
13938         }else if(value.exec){ // regex?
13939             f = function(n){
13940                 return value.test(n.attributes[attr]);
13941             };
13942         }else{
13943             throw 'Illegal filter type, must be string or regex';
13944         }
13945         this.filterBy(f, null, startNode);
13946         },
13947
13948     /**
13949      * Filter by a function. The passed function will be called with each
13950      * node in the tree (or from the startNode). If the function returns true, the node is kept
13951      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13952      * @param {Function} fn The filter function
13953      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13954      */
13955     filterBy : function(fn, scope, startNode){
13956         startNode = startNode || this.tree.root;
13957         if(this.autoClear){
13958             this.clear();
13959         }
13960         var af = this.filtered, rv = this.reverse;
13961         var f = function(n){
13962             if(n == startNode){
13963                 return true;
13964             }
13965             if(af[n.id]){
13966                 return false;
13967             }
13968             var m = fn.call(scope || n, n);
13969             if(!m || rv){
13970                 af[n.id] = n;
13971                 n.ui.hide();
13972                 return false;
13973             }
13974             return true;
13975         };
13976         startNode.cascade(f);
13977         if(this.remove){
13978            for(var id in af){
13979                if(typeof id != "function"){
13980                    var n = af[id];
13981                    if(n && n.parentNode){
13982                        n.parentNode.removeChild(n);
13983                    }
13984                }
13985            }
13986         }
13987     },
13988
13989     /**
13990      * Clears the current filter. Note: with the "remove" option
13991      * set a filter cannot be cleared.
13992      */
13993     clear : function(){
13994         var t = this.tree;
13995         var af = this.filtered;
13996         for(var id in af){
13997             if(typeof id != "function"){
13998                 var n = af[id];
13999                 if(n){
14000                     n.ui.show();
14001                 }
14002             }
14003         }
14004         this.filtered = {};
14005     }
14006 };
14007 /*
14008  * Based on:
14009  * Ext JS Library 1.1.1
14010  * Copyright(c) 2006-2007, Ext JS, LLC.
14011  *
14012  * Originally Released Under LGPL - original licence link has changed is not relivant.
14013  *
14014  * Fork - LGPL
14015  * <script type="text/javascript">
14016  */
14017  
14018
14019 /**
14020  * @class Roo.tree.TreeSorter
14021  * Provides sorting of nodes in a TreePanel
14022  * 
14023  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14024  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14025  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14026  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14027  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14028  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14029  * @constructor
14030  * @param {TreePanel} tree
14031  * @param {Object} config
14032  */
14033 Roo.tree.TreeSorter = function(tree, config){
14034     Roo.apply(this, config);
14035     tree.on("beforechildrenrendered", this.doSort, this);
14036     tree.on("append", this.updateSort, this);
14037     tree.on("insert", this.updateSort, this);
14038     
14039     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14040     var p = this.property || "text";
14041     var sortType = this.sortType;
14042     var fs = this.folderSort;
14043     var cs = this.caseSensitive === true;
14044     var leafAttr = this.leafAttr || 'leaf';
14045
14046     this.sortFn = function(n1, n2){
14047         if(fs){
14048             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14049                 return 1;
14050             }
14051             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14052                 return -1;
14053             }
14054         }
14055         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14056         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14057         if(v1 < v2){
14058                         return dsc ? +1 : -1;
14059                 }else if(v1 > v2){
14060                         return dsc ? -1 : +1;
14061         }else{
14062                 return 0;
14063         }
14064     };
14065 };
14066
14067 Roo.tree.TreeSorter.prototype = {
14068     doSort : function(node){
14069         node.sort(this.sortFn);
14070     },
14071     
14072     compareNodes : function(n1, n2){
14073         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14074     },
14075     
14076     updateSort : function(tree, node){
14077         if(node.childrenRendered){
14078             this.doSort.defer(1, this, [node]);
14079         }
14080     }
14081 };/*
14082  * Based on:
14083  * Ext JS Library 1.1.1
14084  * Copyright(c) 2006-2007, Ext JS, LLC.
14085  *
14086  * Originally Released Under LGPL - original licence link has changed is not relivant.
14087  *
14088  * Fork - LGPL
14089  * <script type="text/javascript">
14090  */
14091
14092 if(Roo.dd.DropZone){
14093     
14094 Roo.tree.TreeDropZone = function(tree, config){
14095     this.allowParentInsert = false;
14096     this.allowContainerDrop = false;
14097     this.appendOnly = false;
14098     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14099     this.tree = tree;
14100     this.lastInsertClass = "x-tree-no-status";
14101     this.dragOverData = {};
14102 };
14103
14104 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14105     ddGroup : "TreeDD",
14106     scroll:  true,
14107     
14108     expandDelay : 1000,
14109     
14110     expandNode : function(node){
14111         if(node.hasChildNodes() && !node.isExpanded()){
14112             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14113         }
14114     },
14115     
14116     queueExpand : function(node){
14117         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14118     },
14119     
14120     cancelExpand : function(){
14121         if(this.expandProcId){
14122             clearTimeout(this.expandProcId);
14123             this.expandProcId = false;
14124         }
14125     },
14126     
14127     isValidDropPoint : function(n, pt, dd, e, data){
14128         if(!n || !data){ return false; }
14129         var targetNode = n.node;
14130         var dropNode = data.node;
14131         // default drop rules
14132         if(!(targetNode && targetNode.isTarget && pt)){
14133             return false;
14134         }
14135         if(pt == "append" && targetNode.allowChildren === false){
14136             return false;
14137         }
14138         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14139             return false;
14140         }
14141         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14142             return false;
14143         }
14144         // reuse the object
14145         var overEvent = this.dragOverData;
14146         overEvent.tree = this.tree;
14147         overEvent.target = targetNode;
14148         overEvent.data = data;
14149         overEvent.point = pt;
14150         overEvent.source = dd;
14151         overEvent.rawEvent = e;
14152         overEvent.dropNode = dropNode;
14153         overEvent.cancel = false;  
14154         var result = this.tree.fireEvent("nodedragover", overEvent);
14155         return overEvent.cancel === false && result !== false;
14156     },
14157     
14158     getDropPoint : function(e, n, dd)
14159     {
14160         var tn = n.node;
14161         if(tn.isRoot){
14162             return tn.allowChildren !== false ? "append" : false; // always append for root
14163         }
14164         var dragEl = n.ddel;
14165         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14166         var y = Roo.lib.Event.getPageY(e);
14167         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14168         
14169         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14170         var noAppend = tn.allowChildren === false;
14171         if(this.appendOnly || tn.parentNode.allowChildren === false){
14172             return noAppend ? false : "append";
14173         }
14174         var noBelow = false;
14175         if(!this.allowParentInsert){
14176             noBelow = tn.hasChildNodes() && tn.isExpanded();
14177         }
14178         var q = (b - t) / (noAppend ? 2 : 3);
14179         if(y >= t && y < (t + q)){
14180             return "above";
14181         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14182             return "below";
14183         }else{
14184             return "append";
14185         }
14186     },
14187     
14188     onNodeEnter : function(n, dd, e, data)
14189     {
14190         this.cancelExpand();
14191     },
14192     
14193     onNodeOver : function(n, dd, e, data)
14194     {
14195        
14196         var pt = this.getDropPoint(e, n, dd);
14197         var node = n.node;
14198         
14199         // auto node expand check
14200         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14201             this.queueExpand(node);
14202         }else if(pt != "append"){
14203             this.cancelExpand();
14204         }
14205         
14206         // set the insert point style on the target node
14207         var returnCls = this.dropNotAllowed;
14208         if(this.isValidDropPoint(n, pt, dd, e, data)){
14209            if(pt){
14210                var el = n.ddel;
14211                var cls;
14212                if(pt == "above"){
14213                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14214                    cls = "x-tree-drag-insert-above";
14215                }else if(pt == "below"){
14216                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14217                    cls = "x-tree-drag-insert-below";
14218                }else{
14219                    returnCls = "x-tree-drop-ok-append";
14220                    cls = "x-tree-drag-append";
14221                }
14222                if(this.lastInsertClass != cls){
14223                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14224                    this.lastInsertClass = cls;
14225                }
14226            }
14227        }
14228        return returnCls;
14229     },
14230     
14231     onNodeOut : function(n, dd, e, data){
14232         
14233         this.cancelExpand();
14234         this.removeDropIndicators(n);
14235     },
14236     
14237     onNodeDrop : function(n, dd, e, data){
14238         var point = this.getDropPoint(e, n, dd);
14239         var targetNode = n.node;
14240         targetNode.ui.startDrop();
14241         if(!this.isValidDropPoint(n, point, dd, e, data)){
14242             targetNode.ui.endDrop();
14243             return false;
14244         }
14245         // first try to find the drop node
14246         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14247         var dropEvent = {
14248             tree : this.tree,
14249             target: targetNode,
14250             data: data,
14251             point: point,
14252             source: dd,
14253             rawEvent: e,
14254             dropNode: dropNode,
14255             cancel: !dropNode   
14256         };
14257         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14258         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14259             targetNode.ui.endDrop();
14260             return false;
14261         }
14262         // allow target changing
14263         targetNode = dropEvent.target;
14264         if(point == "append" && !targetNode.isExpanded()){
14265             targetNode.expand(false, null, function(){
14266                 this.completeDrop(dropEvent);
14267             }.createDelegate(this));
14268         }else{
14269             this.completeDrop(dropEvent);
14270         }
14271         return true;
14272     },
14273     
14274     completeDrop : function(de){
14275         var ns = de.dropNode, p = de.point, t = de.target;
14276         if(!(ns instanceof Array)){
14277             ns = [ns];
14278         }
14279         var n;
14280         for(var i = 0, len = ns.length; i < len; i++){
14281             n = ns[i];
14282             if(p == "above"){
14283                 t.parentNode.insertBefore(n, t);
14284             }else if(p == "below"){
14285                 t.parentNode.insertBefore(n, t.nextSibling);
14286             }else{
14287                 t.appendChild(n);
14288             }
14289         }
14290         n.ui.focus();
14291         if(this.tree.hlDrop){
14292             n.ui.highlight();
14293         }
14294         t.ui.endDrop();
14295         this.tree.fireEvent("nodedrop", de);
14296     },
14297     
14298     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14299         if(this.tree.hlDrop){
14300             dropNode.ui.focus();
14301             dropNode.ui.highlight();
14302         }
14303         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14304     },
14305     
14306     getTree : function(){
14307         return this.tree;
14308     },
14309     
14310     removeDropIndicators : function(n){
14311         if(n && n.ddel){
14312             var el = n.ddel;
14313             Roo.fly(el).removeClass([
14314                     "x-tree-drag-insert-above",
14315                     "x-tree-drag-insert-below",
14316                     "x-tree-drag-append"]);
14317             this.lastInsertClass = "_noclass";
14318         }
14319     },
14320     
14321     beforeDragDrop : function(target, e, id){
14322         this.cancelExpand();
14323         return true;
14324     },
14325     
14326     afterRepair : function(data){
14327         if(data && Roo.enableFx){
14328             data.node.ui.highlight();
14329         }
14330         this.hideProxy();
14331     } 
14332     
14333 });
14334
14335 }
14336 /*
14337  * Based on:
14338  * Ext JS Library 1.1.1
14339  * Copyright(c) 2006-2007, Ext JS, LLC.
14340  *
14341  * Originally Released Under LGPL - original licence link has changed is not relivant.
14342  *
14343  * Fork - LGPL
14344  * <script type="text/javascript">
14345  */
14346  
14347
14348 if(Roo.dd.DragZone){
14349 Roo.tree.TreeDragZone = function(tree, config){
14350     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14351     this.tree = tree;
14352 };
14353
14354 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14355     ddGroup : "TreeDD",
14356    
14357     onBeforeDrag : function(data, e){
14358         var n = data.node;
14359         return n && n.draggable && !n.disabled;
14360     },
14361      
14362     
14363     onInitDrag : function(e){
14364         var data = this.dragData;
14365         this.tree.getSelectionModel().select(data.node);
14366         this.proxy.update("");
14367         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14368         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14369     },
14370     
14371     getRepairXY : function(e, data){
14372         return data.node.ui.getDDRepairXY();
14373     },
14374     
14375     onEndDrag : function(data, e){
14376         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14377         
14378         
14379     },
14380     
14381     onValidDrop : function(dd, e, id){
14382         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14383         this.hideProxy();
14384     },
14385     
14386     beforeInvalidDrop : function(e, id){
14387         // this scrolls the original position back into view
14388         var sm = this.tree.getSelectionModel();
14389         sm.clearSelections();
14390         sm.select(this.dragData.node);
14391     }
14392 });
14393 }/*
14394  * Based on:
14395  * Ext JS Library 1.1.1
14396  * Copyright(c) 2006-2007, Ext JS, LLC.
14397  *
14398  * Originally Released Under LGPL - original licence link has changed is not relivant.
14399  *
14400  * Fork - LGPL
14401  * <script type="text/javascript">
14402  */
14403 /**
14404  * @class Roo.tree.TreeEditor
14405  * @extends Roo.Editor
14406  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14407  * as the editor field.
14408  * @constructor
14409  * @param {Object} config (used to be the tree panel.)
14410  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14411  * 
14412  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14413  * @cfg {Roo.form.TextField|Object} field The field configuration
14414  *
14415  * 
14416  */
14417 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14418     var tree = config;
14419     var field;
14420     if (oldconfig) { // old style..
14421         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14422     } else {
14423         // new style..
14424         tree = config.tree;
14425         config.field = config.field  || {};
14426         config.field.xtype = 'TextField';
14427         field = Roo.factory(config.field, Roo.form);
14428     }
14429     config = config || {};
14430     
14431     
14432     this.addEvents({
14433         /**
14434          * @event beforenodeedit
14435          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14436          * false from the handler of this event.
14437          * @param {Editor} this
14438          * @param {Roo.tree.Node} node 
14439          */
14440         "beforenodeedit" : true
14441     });
14442     
14443     //Roo.log(config);
14444     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14445
14446     this.tree = tree;
14447
14448     tree.on('beforeclick', this.beforeNodeClick, this);
14449     tree.getTreeEl().on('mousedown', this.hide, this);
14450     this.on('complete', this.updateNode, this);
14451     this.on('beforestartedit', this.fitToTree, this);
14452     this.on('startedit', this.bindScroll, this, {delay:10});
14453     this.on('specialkey', this.onSpecialKey, this);
14454 };
14455
14456 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14457     /**
14458      * @cfg {String} alignment
14459      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14460      */
14461     alignment: "l-l",
14462     // inherit
14463     autoSize: false,
14464     /**
14465      * @cfg {Boolean} hideEl
14466      * True to hide the bound element while the editor is displayed (defaults to false)
14467      */
14468     hideEl : false,
14469     /**
14470      * @cfg {String} cls
14471      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14472      */
14473     cls: "x-small-editor x-tree-editor",
14474     /**
14475      * @cfg {Boolean} shim
14476      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14477      */
14478     shim:false,
14479     // inherit
14480     shadow:"frame",
14481     /**
14482      * @cfg {Number} maxWidth
14483      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14484      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14485      * scroll and client offsets into account prior to each edit.
14486      */
14487     maxWidth: 250,
14488
14489     editDelay : 350,
14490
14491     // private
14492     fitToTree : function(ed, el){
14493         var td = this.tree.getTreeEl().dom, nd = el.dom;
14494         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14495             td.scrollLeft = nd.offsetLeft;
14496         }
14497         var w = Math.min(
14498                 this.maxWidth,
14499                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14500         this.setSize(w, '');
14501         
14502         return this.fireEvent('beforenodeedit', this, this.editNode);
14503         
14504     },
14505
14506     // private
14507     triggerEdit : function(node){
14508         this.completeEdit();
14509         this.editNode = node;
14510         this.startEdit(node.ui.textNode, node.text);
14511     },
14512
14513     // private
14514     bindScroll : function(){
14515         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14516     },
14517
14518     // private
14519     beforeNodeClick : function(node, e){
14520         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14521         this.lastClick = new Date();
14522         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14523             e.stopEvent();
14524             this.triggerEdit(node);
14525             return false;
14526         }
14527         return true;
14528     },
14529
14530     // private
14531     updateNode : function(ed, value){
14532         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14533         this.editNode.setText(value);
14534     },
14535
14536     // private
14537     onHide : function(){
14538         Roo.tree.TreeEditor.superclass.onHide.call(this);
14539         if(this.editNode){
14540             this.editNode.ui.focus();
14541         }
14542     },
14543
14544     // private
14545     onSpecialKey : function(field, e){
14546         var k = e.getKey();
14547         if(k == e.ESC){
14548             e.stopEvent();
14549             this.cancelEdit();
14550         }else if(k == e.ENTER && !e.hasModifier()){
14551             e.stopEvent();
14552             this.completeEdit();
14553         }
14554     }
14555 });//<Script type="text/javascript">
14556 /*
14557  * Based on:
14558  * Ext JS Library 1.1.1
14559  * Copyright(c) 2006-2007, Ext JS, LLC.
14560  *
14561  * Originally Released Under LGPL - original licence link has changed is not relivant.
14562  *
14563  * Fork - LGPL
14564  * <script type="text/javascript">
14565  */
14566  
14567 /**
14568  * Not documented??? - probably should be...
14569  */
14570
14571 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14572     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14573     
14574     renderElements : function(n, a, targetNode, bulkRender){
14575         //consel.log("renderElements?");
14576         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14577
14578         var t = n.getOwnerTree();
14579         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14580         
14581         var cols = t.columns;
14582         var bw = t.borderWidth;
14583         var c = cols[0];
14584         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14585          var cb = typeof a.checked == "boolean";
14586         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14587         var colcls = 'x-t-' + tid + '-c0';
14588         var buf = [
14589             '<li class="x-tree-node">',
14590             
14591                 
14592                 '<div class="x-tree-node-el ', a.cls,'">',
14593                     // extran...
14594                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14595                 
14596                 
14597                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14598                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14599                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14600                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14601                            (a.iconCls ? ' '+a.iconCls : ''),
14602                            '" unselectable="on" />',
14603                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14604                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14605                              
14606                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14607                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14608                             '<span unselectable="on" qtip="' + tx + '">',
14609                              tx,
14610                              '</span></a>' ,
14611                     '</div>',
14612                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14613                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14614                  ];
14615         for(var i = 1, len = cols.length; i < len; i++){
14616             c = cols[i];
14617             colcls = 'x-t-' + tid + '-c' +i;
14618             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14619             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14620                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14621                       "</div>");
14622          }
14623          
14624          buf.push(
14625             '</a>',
14626             '<div class="x-clear"></div></div>',
14627             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14628             "</li>");
14629         
14630         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14631             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14632                                 n.nextSibling.ui.getEl(), buf.join(""));
14633         }else{
14634             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14635         }
14636         var el = this.wrap.firstChild;
14637         this.elRow = el;
14638         this.elNode = el.firstChild;
14639         this.ranchor = el.childNodes[1];
14640         this.ctNode = this.wrap.childNodes[1];
14641         var cs = el.firstChild.childNodes;
14642         this.indentNode = cs[0];
14643         this.ecNode = cs[1];
14644         this.iconNode = cs[2];
14645         var index = 3;
14646         if(cb){
14647             this.checkbox = cs[3];
14648             index++;
14649         }
14650         this.anchor = cs[index];
14651         
14652         this.textNode = cs[index].firstChild;
14653         
14654         //el.on("click", this.onClick, this);
14655         //el.on("dblclick", this.onDblClick, this);
14656         
14657         
14658        // console.log(this);
14659     },
14660     initEvents : function(){
14661         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14662         
14663             
14664         var a = this.ranchor;
14665
14666         var el = Roo.get(a);
14667
14668         if(Roo.isOpera){ // opera render bug ignores the CSS
14669             el.setStyle("text-decoration", "none");
14670         }
14671
14672         el.on("click", this.onClick, this);
14673         el.on("dblclick", this.onDblClick, this);
14674         el.on("contextmenu", this.onContextMenu, this);
14675         
14676     },
14677     
14678     /*onSelectedChange : function(state){
14679         if(state){
14680             this.focus();
14681             this.addClass("x-tree-selected");
14682         }else{
14683             //this.blur();
14684             this.removeClass("x-tree-selected");
14685         }
14686     },*/
14687     addClass : function(cls){
14688         if(this.elRow){
14689             Roo.fly(this.elRow).addClass(cls);
14690         }
14691         
14692     },
14693     
14694     
14695     removeClass : function(cls){
14696         if(this.elRow){
14697             Roo.fly(this.elRow).removeClass(cls);
14698         }
14699     }
14700
14701     
14702     
14703 });//<Script type="text/javascript">
14704
14705 /*
14706  * Based on:
14707  * Ext JS Library 1.1.1
14708  * Copyright(c) 2006-2007, Ext JS, LLC.
14709  *
14710  * Originally Released Under LGPL - original licence link has changed is not relivant.
14711  *
14712  * Fork - LGPL
14713  * <script type="text/javascript">
14714  */
14715  
14716
14717 /**
14718  * @class Roo.tree.ColumnTree
14719  * @extends Roo.data.TreePanel
14720  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14721  * @cfg {int} borderWidth  compined right/left border allowance
14722  * @constructor
14723  * @param {String/HTMLElement/Element} el The container element
14724  * @param {Object} config
14725  */
14726 Roo.tree.ColumnTree =  function(el, config)
14727 {
14728    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14729    this.addEvents({
14730         /**
14731         * @event resize
14732         * Fire this event on a container when it resizes
14733         * @param {int} w Width
14734         * @param {int} h Height
14735         */
14736        "resize" : true
14737     });
14738     this.on('resize', this.onResize, this);
14739 };
14740
14741 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14742     //lines:false,
14743     
14744     
14745     borderWidth: Roo.isBorderBox ? 0 : 2, 
14746     headEls : false,
14747     
14748     render : function(){
14749         // add the header.....
14750        
14751         Roo.tree.ColumnTree.superclass.render.apply(this);
14752         
14753         this.el.addClass('x-column-tree');
14754         
14755         this.headers = this.el.createChild(
14756             {cls:'x-tree-headers'},this.innerCt.dom);
14757    
14758         var cols = this.columns, c;
14759         var totalWidth = 0;
14760         this.headEls = [];
14761         var  len = cols.length;
14762         for(var i = 0; i < len; i++){
14763              c = cols[i];
14764              totalWidth += c.width;
14765             this.headEls.push(this.headers.createChild({
14766                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14767                  cn: {
14768                      cls:'x-tree-hd-text',
14769                      html: c.header
14770                  },
14771                  style:'width:'+(c.width-this.borderWidth)+'px;'
14772              }));
14773         }
14774         this.headers.createChild({cls:'x-clear'});
14775         // prevent floats from wrapping when clipped
14776         this.headers.setWidth(totalWidth);
14777         //this.innerCt.setWidth(totalWidth);
14778         this.innerCt.setStyle({ overflow: 'auto' });
14779         this.onResize(this.width, this.height);
14780              
14781         
14782     },
14783     onResize : function(w,h)
14784     {
14785         this.height = h;
14786         this.width = w;
14787         // resize cols..
14788         this.innerCt.setWidth(this.width);
14789         this.innerCt.setHeight(this.height-20);
14790         
14791         // headers...
14792         var cols = this.columns, c;
14793         var totalWidth = 0;
14794         var expEl = false;
14795         var len = cols.length;
14796         for(var i = 0; i < len; i++){
14797             c = cols[i];
14798             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14799                 // it's the expander..
14800                 expEl  = this.headEls[i];
14801                 continue;
14802             }
14803             totalWidth += c.width;
14804             
14805         }
14806         if (expEl) {
14807             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14808         }
14809         this.headers.setWidth(w-20);
14810
14811         
14812         
14813         
14814     }
14815 });
14816 /*
14817  * Based on:
14818  * Ext JS Library 1.1.1
14819  * Copyright(c) 2006-2007, Ext JS, LLC.
14820  *
14821  * Originally Released Under LGPL - original licence link has changed is not relivant.
14822  *
14823  * Fork - LGPL
14824  * <script type="text/javascript">
14825  */
14826  
14827 /**
14828  * @class Roo.menu.Menu
14829  * @extends Roo.util.Observable
14830  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14831  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14832  * @constructor
14833  * Creates a new Menu
14834  * @param {Object} config Configuration options
14835  */
14836 Roo.menu.Menu = function(config){
14837     
14838     Roo.menu.Menu.superclass.constructor.call(this, config);
14839     
14840     this.id = this.id || Roo.id();
14841     this.addEvents({
14842         /**
14843          * @event beforeshow
14844          * Fires before this menu is displayed
14845          * @param {Roo.menu.Menu} this
14846          */
14847         beforeshow : true,
14848         /**
14849          * @event beforehide
14850          * Fires before this menu is hidden
14851          * @param {Roo.menu.Menu} this
14852          */
14853         beforehide : true,
14854         /**
14855          * @event show
14856          * Fires after this menu is displayed
14857          * @param {Roo.menu.Menu} this
14858          */
14859         show : true,
14860         /**
14861          * @event hide
14862          * Fires after this menu is hidden
14863          * @param {Roo.menu.Menu} this
14864          */
14865         hide : true,
14866         /**
14867          * @event click
14868          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14869          * @param {Roo.menu.Menu} this
14870          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14871          * @param {Roo.EventObject} e
14872          */
14873         click : true,
14874         /**
14875          * @event mouseover
14876          * Fires when the mouse is hovering over this menu
14877          * @param {Roo.menu.Menu} this
14878          * @param {Roo.EventObject} e
14879          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14880          */
14881         mouseover : true,
14882         /**
14883          * @event mouseout
14884          * Fires when the mouse exits this menu
14885          * @param {Roo.menu.Menu} this
14886          * @param {Roo.EventObject} e
14887          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14888          */
14889         mouseout : true,
14890         /**
14891          * @event itemclick
14892          * Fires when a menu item contained in this menu is clicked
14893          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14894          * @param {Roo.EventObject} e
14895          */
14896         itemclick: true
14897     });
14898     if (this.registerMenu) {
14899         Roo.menu.MenuMgr.register(this);
14900     }
14901     
14902     var mis = this.items;
14903     this.items = new Roo.util.MixedCollection();
14904     if(mis){
14905         this.add.apply(this, mis);
14906     }
14907 };
14908
14909 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14910     /**
14911      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14912      */
14913     minWidth : 120,
14914     /**
14915      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14916      * for bottom-right shadow (defaults to "sides")
14917      */
14918     shadow : "sides",
14919     /**
14920      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14921      * this menu (defaults to "tl-tr?")
14922      */
14923     subMenuAlign : "tl-tr?",
14924     /**
14925      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14926      * relative to its element of origin (defaults to "tl-bl?")
14927      */
14928     defaultAlign : "tl-bl?",
14929     /**
14930      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14931      */
14932     allowOtherMenus : false,
14933     /**
14934      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14935      */
14936     registerMenu : true,
14937
14938     hidden:true,
14939
14940     // private
14941     render : function(){
14942         if(this.el){
14943             return;
14944         }
14945         var el = this.el = new Roo.Layer({
14946             cls: "x-menu",
14947             shadow:this.shadow,
14948             constrain: false,
14949             parentEl: this.parentEl || document.body,
14950             zindex:15000
14951         });
14952
14953         this.keyNav = new Roo.menu.MenuNav(this);
14954
14955         if(this.plain){
14956             el.addClass("x-menu-plain");
14957         }
14958         if(this.cls){
14959             el.addClass(this.cls);
14960         }
14961         // generic focus element
14962         this.focusEl = el.createChild({
14963             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14964         });
14965         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14966         //disabling touch- as it's causing issues ..
14967         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14968         ul.on('click'   , this.onClick, this);
14969         
14970         
14971         ul.on("mouseover", this.onMouseOver, this);
14972         ul.on("mouseout", this.onMouseOut, this);
14973         this.items.each(function(item){
14974             if (item.hidden) {
14975                 return;
14976             }
14977             
14978             var li = document.createElement("li");
14979             li.className = "x-menu-list-item";
14980             ul.dom.appendChild(li);
14981             item.render(li, this);
14982         }, this);
14983         this.ul = ul;
14984         this.autoWidth();
14985     },
14986
14987     // private
14988     autoWidth : function(){
14989         var el = this.el, ul = this.ul;
14990         if(!el){
14991             return;
14992         }
14993         var w = this.width;
14994         if(w){
14995             el.setWidth(w);
14996         }else if(Roo.isIE){
14997             el.setWidth(this.minWidth);
14998             var t = el.dom.offsetWidth; // force recalc
14999             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
15000         }
15001     },
15002
15003     // private
15004     delayAutoWidth : function(){
15005         if(this.rendered){
15006             if(!this.awTask){
15007                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
15008             }
15009             this.awTask.delay(20);
15010         }
15011     },
15012
15013     // private
15014     findTargetItem : function(e){
15015         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
15016         if(t && t.menuItemId){
15017             return this.items.get(t.menuItemId);
15018         }
15019     },
15020
15021     // private
15022     onClick : function(e){
15023         Roo.log("menu.onClick");
15024         var t = this.findTargetItem(e);
15025         if(!t){
15026             return;
15027         }
15028         Roo.log(e);
15029         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
15030             if(t == this.activeItem && t.shouldDeactivate(e)){
15031                 this.activeItem.deactivate();
15032                 delete this.activeItem;
15033                 return;
15034             }
15035             if(t.canActivate){
15036                 this.setActiveItem(t, true);
15037             }
15038             return;
15039             
15040             
15041         }
15042         
15043         t.onClick(e);
15044         this.fireEvent("click", this, t, e);
15045     },
15046
15047     // private
15048     setActiveItem : function(item, autoExpand){
15049         if(item != this.activeItem){
15050             if(this.activeItem){
15051                 this.activeItem.deactivate();
15052             }
15053             this.activeItem = item;
15054             item.activate(autoExpand);
15055         }else if(autoExpand){
15056             item.expandMenu();
15057         }
15058     },
15059
15060     // private
15061     tryActivate : function(start, step){
15062         var items = this.items;
15063         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15064             var item = items.get(i);
15065             if(!item.disabled && item.canActivate){
15066                 this.setActiveItem(item, false);
15067                 return item;
15068             }
15069         }
15070         return false;
15071     },
15072
15073     // private
15074     onMouseOver : function(e){
15075         var t;
15076         if(t = this.findTargetItem(e)){
15077             if(t.canActivate && !t.disabled){
15078                 this.setActiveItem(t, true);
15079             }
15080         }
15081         this.fireEvent("mouseover", this, e, t);
15082     },
15083
15084     // private
15085     onMouseOut : function(e){
15086         var t;
15087         if(t = this.findTargetItem(e)){
15088             if(t == this.activeItem && t.shouldDeactivate(e)){
15089                 this.activeItem.deactivate();
15090                 delete this.activeItem;
15091             }
15092         }
15093         this.fireEvent("mouseout", this, e, t);
15094     },
15095
15096     /**
15097      * Read-only.  Returns true if the menu is currently displayed, else false.
15098      * @type Boolean
15099      */
15100     isVisible : function(){
15101         return this.el && !this.hidden;
15102     },
15103
15104     /**
15105      * Displays this menu relative to another element
15106      * @param {String/HTMLElement/Roo.Element} element The element to align to
15107      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15108      * the element (defaults to this.defaultAlign)
15109      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15110      */
15111     show : function(el, pos, parentMenu){
15112         this.parentMenu = parentMenu;
15113         if(!this.el){
15114             this.render();
15115         }
15116         this.fireEvent("beforeshow", this);
15117         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15118     },
15119
15120     /**
15121      * Displays this menu at a specific xy position
15122      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15123      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15124      */
15125     showAt : function(xy, parentMenu, /* private: */_e){
15126         this.parentMenu = parentMenu;
15127         if(!this.el){
15128             this.render();
15129         }
15130         if(_e !== false){
15131             this.fireEvent("beforeshow", this);
15132             xy = this.el.adjustForConstraints(xy);
15133         }
15134         this.el.setXY(xy);
15135         this.el.show();
15136         this.hidden = false;
15137         this.focus();
15138         this.fireEvent("show", this);
15139     },
15140
15141     focus : function(){
15142         if(!this.hidden){
15143             this.doFocus.defer(50, this);
15144         }
15145     },
15146
15147     doFocus : function(){
15148         if(!this.hidden){
15149             this.focusEl.focus();
15150         }
15151     },
15152
15153     /**
15154      * Hides this menu and optionally all parent menus
15155      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15156      */
15157     hide : function(deep){
15158         if(this.el && this.isVisible()){
15159             this.fireEvent("beforehide", this);
15160             if(this.activeItem){
15161                 this.activeItem.deactivate();
15162                 this.activeItem = null;
15163             }
15164             this.el.hide();
15165             this.hidden = true;
15166             this.fireEvent("hide", this);
15167         }
15168         if(deep === true && this.parentMenu){
15169             this.parentMenu.hide(true);
15170         }
15171     },
15172
15173     /**
15174      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15175      * Any of the following are valid:
15176      * <ul>
15177      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15178      * <li>An HTMLElement object which will be converted to a menu item</li>
15179      * <li>A menu item config object that will be created as a new menu item</li>
15180      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15181      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15182      * </ul>
15183      * Usage:
15184      * <pre><code>
15185 // Create the menu
15186 var menu = new Roo.menu.Menu();
15187
15188 // Create a menu item to add by reference
15189 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15190
15191 // Add a bunch of items at once using different methods.
15192 // Only the last item added will be returned.
15193 var item = menu.add(
15194     menuItem,                // add existing item by ref
15195     'Dynamic Item',          // new TextItem
15196     '-',                     // new separator
15197     { text: 'Config Item' }  // new item by config
15198 );
15199 </code></pre>
15200      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15201      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15202      */
15203     add : function(){
15204         var a = arguments, l = a.length, item;
15205         for(var i = 0; i < l; i++){
15206             var el = a[i];
15207             if ((typeof(el) == "object") && el.xtype && el.xns) {
15208                 el = Roo.factory(el, Roo.menu);
15209             }
15210             
15211             if(el.render){ // some kind of Item
15212                 item = this.addItem(el);
15213             }else if(typeof el == "string"){ // string
15214                 if(el == "separator" || el == "-"){
15215                     item = this.addSeparator();
15216                 }else{
15217                     item = this.addText(el);
15218                 }
15219             }else if(el.tagName || el.el){ // element
15220                 item = this.addElement(el);
15221             }else if(typeof el == "object"){ // must be menu item config?
15222                 item = this.addMenuItem(el);
15223             }
15224         }
15225         return item;
15226     },
15227
15228     /**
15229      * Returns this menu's underlying {@link Roo.Element} object
15230      * @return {Roo.Element} The element
15231      */
15232     getEl : function(){
15233         if(!this.el){
15234             this.render();
15235         }
15236         return this.el;
15237     },
15238
15239     /**
15240      * Adds a separator bar to the menu
15241      * @return {Roo.menu.Item} The menu item that was added
15242      */
15243     addSeparator : function(){
15244         return this.addItem(new Roo.menu.Separator());
15245     },
15246
15247     /**
15248      * Adds an {@link Roo.Element} object to the menu
15249      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15250      * @return {Roo.menu.Item} The menu item that was added
15251      */
15252     addElement : function(el){
15253         return this.addItem(new Roo.menu.BaseItem(el));
15254     },
15255
15256     /**
15257      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15258      * @param {Roo.menu.Item} item The menu item to add
15259      * @return {Roo.menu.Item} The menu item that was added
15260      */
15261     addItem : function(item){
15262         this.items.add(item);
15263         if(this.ul){
15264             var li = document.createElement("li");
15265             li.className = "x-menu-list-item";
15266             this.ul.dom.appendChild(li);
15267             item.render(li, this);
15268             this.delayAutoWidth();
15269         }
15270         return item;
15271     },
15272
15273     /**
15274      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15275      * @param {Object} config A MenuItem config object
15276      * @return {Roo.menu.Item} The menu item that was added
15277      */
15278     addMenuItem : function(config){
15279         if(!(config instanceof Roo.menu.Item)){
15280             if(typeof config.checked == "boolean"){ // must be check menu item config?
15281                 config = new Roo.menu.CheckItem(config);
15282             }else{
15283                 config = new Roo.menu.Item(config);
15284             }
15285         }
15286         return this.addItem(config);
15287     },
15288
15289     /**
15290      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15291      * @param {String} text The text to display in the menu item
15292      * @return {Roo.menu.Item} The menu item that was added
15293      */
15294     addText : function(text){
15295         return this.addItem(new Roo.menu.TextItem({ text : text }));
15296     },
15297
15298     /**
15299      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15300      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15301      * @param {Roo.menu.Item} item The menu item to add
15302      * @return {Roo.menu.Item} The menu item that was added
15303      */
15304     insert : function(index, item){
15305         this.items.insert(index, item);
15306         if(this.ul){
15307             var li = document.createElement("li");
15308             li.className = "x-menu-list-item";
15309             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15310             item.render(li, this);
15311             this.delayAutoWidth();
15312         }
15313         return item;
15314     },
15315
15316     /**
15317      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15318      * @param {Roo.menu.Item} item The menu item to remove
15319      */
15320     remove : function(item){
15321         this.items.removeKey(item.id);
15322         item.destroy();
15323     },
15324
15325     /**
15326      * Removes and destroys all items in the menu
15327      */
15328     removeAll : function(){
15329         var f;
15330         while(f = this.items.first()){
15331             this.remove(f);
15332         }
15333     }
15334 });
15335
15336 // MenuNav is a private utility class used internally by the Menu
15337 Roo.menu.MenuNav = function(menu){
15338     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15339     this.scope = this.menu = menu;
15340 };
15341
15342 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15343     doRelay : function(e, h){
15344         var k = e.getKey();
15345         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15346             this.menu.tryActivate(0, 1);
15347             return false;
15348         }
15349         return h.call(this.scope || this, e, this.menu);
15350     },
15351
15352     up : function(e, m){
15353         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15354             m.tryActivate(m.items.length-1, -1);
15355         }
15356     },
15357
15358     down : function(e, m){
15359         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15360             m.tryActivate(0, 1);
15361         }
15362     },
15363
15364     right : function(e, m){
15365         if(m.activeItem){
15366             m.activeItem.expandMenu(true);
15367         }
15368     },
15369
15370     left : function(e, m){
15371         m.hide();
15372         if(m.parentMenu && m.parentMenu.activeItem){
15373             m.parentMenu.activeItem.activate();
15374         }
15375     },
15376
15377     enter : function(e, m){
15378         if(m.activeItem){
15379             e.stopPropagation();
15380             m.activeItem.onClick(e);
15381             m.fireEvent("click", this, m.activeItem);
15382             return true;
15383         }
15384     }
15385 });/*
15386  * Based on:
15387  * Ext JS Library 1.1.1
15388  * Copyright(c) 2006-2007, Ext JS, LLC.
15389  *
15390  * Originally Released Under LGPL - original licence link has changed is not relivant.
15391  *
15392  * Fork - LGPL
15393  * <script type="text/javascript">
15394  */
15395  
15396 /**
15397  * @class Roo.menu.MenuMgr
15398  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15399  * @singleton
15400  */
15401 Roo.menu.MenuMgr = function(){
15402    var menus, active, groups = {}, attached = false, lastShow = new Date();
15403
15404    // private - called when first menu is created
15405    function init(){
15406        menus = {};
15407        active = new Roo.util.MixedCollection();
15408        Roo.get(document).addKeyListener(27, function(){
15409            if(active.length > 0){
15410                hideAll();
15411            }
15412        });
15413    }
15414
15415    // private
15416    function hideAll(){
15417        if(active && active.length > 0){
15418            var c = active.clone();
15419            c.each(function(m){
15420                m.hide();
15421            });
15422        }
15423    }
15424
15425    // private
15426    function onHide(m){
15427        active.remove(m);
15428        if(active.length < 1){
15429            Roo.get(document).un("mousedown", onMouseDown);
15430            attached = false;
15431        }
15432    }
15433
15434    // private
15435    function onShow(m){
15436        var last = active.last();
15437        lastShow = new Date();
15438        active.add(m);
15439        if(!attached){
15440            Roo.get(document).on("mousedown", onMouseDown);
15441            attached = true;
15442        }
15443        if(m.parentMenu){
15444           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15445           m.parentMenu.activeChild = m;
15446        }else if(last && last.isVisible()){
15447           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15448        }
15449    }
15450
15451    // private
15452    function onBeforeHide(m){
15453        if(m.activeChild){
15454            m.activeChild.hide();
15455        }
15456        if(m.autoHideTimer){
15457            clearTimeout(m.autoHideTimer);
15458            delete m.autoHideTimer;
15459        }
15460    }
15461
15462    // private
15463    function onBeforeShow(m){
15464        var pm = m.parentMenu;
15465        if(!pm && !m.allowOtherMenus){
15466            hideAll();
15467        }else if(pm && pm.activeChild && active != m){
15468            pm.activeChild.hide();
15469        }
15470    }
15471
15472    // private
15473    function onMouseDown(e){
15474        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15475            hideAll();
15476        }
15477    }
15478
15479    // private
15480    function onBeforeCheck(mi, state){
15481        if(state){
15482            var g = groups[mi.group];
15483            for(var i = 0, l = g.length; i < l; i++){
15484                if(g[i] != mi){
15485                    g[i].setChecked(false);
15486                }
15487            }
15488        }
15489    }
15490
15491    return {
15492
15493        /**
15494         * Hides all menus that are currently visible
15495         */
15496        hideAll : function(){
15497             hideAll();  
15498        },
15499
15500        // private
15501        register : function(menu){
15502            if(!menus){
15503                init();
15504            }
15505            menus[menu.id] = menu;
15506            menu.on("beforehide", onBeforeHide);
15507            menu.on("hide", onHide);
15508            menu.on("beforeshow", onBeforeShow);
15509            menu.on("show", onShow);
15510            var g = menu.group;
15511            if(g && menu.events["checkchange"]){
15512                if(!groups[g]){
15513                    groups[g] = [];
15514                }
15515                groups[g].push(menu);
15516                menu.on("checkchange", onCheck);
15517            }
15518        },
15519
15520         /**
15521          * Returns a {@link Roo.menu.Menu} object
15522          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15523          * be used to generate and return a new Menu instance.
15524          */
15525        get : function(menu){
15526            if(typeof menu == "string"){ // menu id
15527                return menus[menu];
15528            }else if(menu.events){  // menu instance
15529                return menu;
15530            }else if(typeof menu.length == 'number'){ // array of menu items?
15531                return new Roo.menu.Menu({items:menu});
15532            }else{ // otherwise, must be a config
15533                return new Roo.menu.Menu(menu);
15534            }
15535        },
15536
15537        // private
15538        unregister : function(menu){
15539            delete menus[menu.id];
15540            menu.un("beforehide", onBeforeHide);
15541            menu.un("hide", onHide);
15542            menu.un("beforeshow", onBeforeShow);
15543            menu.un("show", onShow);
15544            var g = menu.group;
15545            if(g && menu.events["checkchange"]){
15546                groups[g].remove(menu);
15547                menu.un("checkchange", onCheck);
15548            }
15549        },
15550
15551        // private
15552        registerCheckable : function(menuItem){
15553            var g = menuItem.group;
15554            if(g){
15555                if(!groups[g]){
15556                    groups[g] = [];
15557                }
15558                groups[g].push(menuItem);
15559                menuItem.on("beforecheckchange", onBeforeCheck);
15560            }
15561        },
15562
15563        // private
15564        unregisterCheckable : function(menuItem){
15565            var g = menuItem.group;
15566            if(g){
15567                groups[g].remove(menuItem);
15568                menuItem.un("beforecheckchange", onBeforeCheck);
15569            }
15570        }
15571    };
15572 }();/*
15573  * Based on:
15574  * Ext JS Library 1.1.1
15575  * Copyright(c) 2006-2007, Ext JS, LLC.
15576  *
15577  * Originally Released Under LGPL - original licence link has changed is not relivant.
15578  *
15579  * Fork - LGPL
15580  * <script type="text/javascript">
15581  */
15582  
15583
15584 /**
15585  * @class Roo.menu.BaseItem
15586  * @extends Roo.Component
15587  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15588  * management and base configuration options shared by all menu components.
15589  * @constructor
15590  * Creates a new BaseItem
15591  * @param {Object} config Configuration options
15592  */
15593 Roo.menu.BaseItem = function(config){
15594     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15595
15596     this.addEvents({
15597         /**
15598          * @event click
15599          * Fires when this item is clicked
15600          * @param {Roo.menu.BaseItem} this
15601          * @param {Roo.EventObject} e
15602          */
15603         click: true,
15604         /**
15605          * @event activate
15606          * Fires when this item is activated
15607          * @param {Roo.menu.BaseItem} this
15608          */
15609         activate : true,
15610         /**
15611          * @event deactivate
15612          * Fires when this item is deactivated
15613          * @param {Roo.menu.BaseItem} this
15614          */
15615         deactivate : true
15616     });
15617
15618     if(this.handler){
15619         this.on("click", this.handler, this.scope, true);
15620     }
15621 };
15622
15623 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15624     /**
15625      * @cfg {Function} handler
15626      * A function that will handle the click event of this menu item (defaults to undefined)
15627      */
15628     /**
15629      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15630      */
15631     canActivate : false,
15632     
15633      /**
15634      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15635      */
15636     hidden: false,
15637     
15638     /**
15639      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15640      */
15641     activeClass : "x-menu-item-active",
15642     /**
15643      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15644      */
15645     hideOnClick : true,
15646     /**
15647      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15648      */
15649     hideDelay : 100,
15650
15651     // private
15652     ctype: "Roo.menu.BaseItem",
15653
15654     // private
15655     actionMode : "container",
15656
15657     // private
15658     render : function(container, parentMenu){
15659         this.parentMenu = parentMenu;
15660         Roo.menu.BaseItem.superclass.render.call(this, container);
15661         this.container.menuItemId = this.id;
15662     },
15663
15664     // private
15665     onRender : function(container, position){
15666         this.el = Roo.get(this.el);
15667         container.dom.appendChild(this.el.dom);
15668     },
15669
15670     // private
15671     onClick : function(e){
15672         if(!this.disabled && this.fireEvent("click", this, e) !== false
15673                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15674             this.handleClick(e);
15675         }else{
15676             e.stopEvent();
15677         }
15678     },
15679
15680     // private
15681     activate : function(){
15682         if(this.disabled){
15683             return false;
15684         }
15685         var li = this.container;
15686         li.addClass(this.activeClass);
15687         this.region = li.getRegion().adjust(2, 2, -2, -2);
15688         this.fireEvent("activate", this);
15689         return true;
15690     },
15691
15692     // private
15693     deactivate : function(){
15694         this.container.removeClass(this.activeClass);
15695         this.fireEvent("deactivate", this);
15696     },
15697
15698     // private
15699     shouldDeactivate : function(e){
15700         return !this.region || !this.region.contains(e.getPoint());
15701     },
15702
15703     // private
15704     handleClick : function(e){
15705         if(this.hideOnClick){
15706             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15707         }
15708     },
15709
15710     // private
15711     expandMenu : function(autoActivate){
15712         // do nothing
15713     },
15714
15715     // private
15716     hideMenu : function(){
15717         // do nothing
15718     }
15719 });/*
15720  * Based on:
15721  * Ext JS Library 1.1.1
15722  * Copyright(c) 2006-2007, Ext JS, LLC.
15723  *
15724  * Originally Released Under LGPL - original licence link has changed is not relivant.
15725  *
15726  * Fork - LGPL
15727  * <script type="text/javascript">
15728  */
15729  
15730 /**
15731  * @class Roo.menu.Adapter
15732  * @extends Roo.menu.BaseItem
15733  * 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.
15734  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15735  * @constructor
15736  * Creates a new Adapter
15737  * @param {Object} config Configuration options
15738  */
15739 Roo.menu.Adapter = function(component, config){
15740     Roo.menu.Adapter.superclass.constructor.call(this, config);
15741     this.component = component;
15742 };
15743 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15744     // private
15745     canActivate : true,
15746
15747     // private
15748     onRender : function(container, position){
15749         this.component.render(container);
15750         this.el = this.component.getEl();
15751     },
15752
15753     // private
15754     activate : function(){
15755         if(this.disabled){
15756             return false;
15757         }
15758         this.component.focus();
15759         this.fireEvent("activate", this);
15760         return true;
15761     },
15762
15763     // private
15764     deactivate : function(){
15765         this.fireEvent("deactivate", this);
15766     },
15767
15768     // private
15769     disable : function(){
15770         this.component.disable();
15771         Roo.menu.Adapter.superclass.disable.call(this);
15772     },
15773
15774     // private
15775     enable : function(){
15776         this.component.enable();
15777         Roo.menu.Adapter.superclass.enable.call(this);
15778     }
15779 });/*
15780  * Based on:
15781  * Ext JS Library 1.1.1
15782  * Copyright(c) 2006-2007, Ext JS, LLC.
15783  *
15784  * Originally Released Under LGPL - original licence link has changed is not relivant.
15785  *
15786  * Fork - LGPL
15787  * <script type="text/javascript">
15788  */
15789
15790 /**
15791  * @class Roo.menu.TextItem
15792  * @extends Roo.menu.BaseItem
15793  * Adds a static text string to a menu, usually used as either a heading or group separator.
15794  * Note: old style constructor with text is still supported.
15795  * 
15796  * @constructor
15797  * Creates a new TextItem
15798  * @param {Object} cfg Configuration
15799  */
15800 Roo.menu.TextItem = function(cfg){
15801     if (typeof(cfg) == 'string') {
15802         this.text = cfg;
15803     } else {
15804         Roo.apply(this,cfg);
15805     }
15806     
15807     Roo.menu.TextItem.superclass.constructor.call(this);
15808 };
15809
15810 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15811     /**
15812      * @cfg {Boolean} text Text to show on item.
15813      */
15814     text : '',
15815     
15816     /**
15817      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15818      */
15819     hideOnClick : false,
15820     /**
15821      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15822      */
15823     itemCls : "x-menu-text",
15824
15825     // private
15826     onRender : function(){
15827         var s = document.createElement("span");
15828         s.className = this.itemCls;
15829         s.innerHTML = this.text;
15830         this.el = s;
15831         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15832     }
15833 });/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844 /**
15845  * @class Roo.menu.Separator
15846  * @extends Roo.menu.BaseItem
15847  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15848  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15849  * @constructor
15850  * @param {Object} config Configuration options
15851  */
15852 Roo.menu.Separator = function(config){
15853     Roo.menu.Separator.superclass.constructor.call(this, config);
15854 };
15855
15856 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15857     /**
15858      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15859      */
15860     itemCls : "x-menu-sep",
15861     /**
15862      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15863      */
15864     hideOnClick : false,
15865
15866     // private
15867     onRender : function(li){
15868         var s = document.createElement("span");
15869         s.className = this.itemCls;
15870         s.innerHTML = "&#160;";
15871         this.el = s;
15872         li.addClass("x-menu-sep-li");
15873         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15874     }
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  * @class Roo.menu.Item
15887  * @extends Roo.menu.BaseItem
15888  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15889  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15890  * activation and click handling.
15891  * @constructor
15892  * Creates a new Item
15893  * @param {Object} config Configuration options
15894  */
15895 Roo.menu.Item = function(config){
15896     Roo.menu.Item.superclass.constructor.call(this, config);
15897     if(this.menu){
15898         this.menu = Roo.menu.MenuMgr.get(this.menu);
15899     }
15900 };
15901 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15902     
15903     /**
15904      * @cfg {String} text
15905      * The text to show on the menu item.
15906      */
15907     text: '',
15908      /**
15909      * @cfg {String} HTML to render in menu
15910      * The text to show on the menu item (HTML version).
15911      */
15912     html: '',
15913     /**
15914      * @cfg {String} icon
15915      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15916      */
15917     icon: undefined,
15918     /**
15919      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15920      */
15921     itemCls : "x-menu-item",
15922     /**
15923      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15924      */
15925     canActivate : true,
15926     /**
15927      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15928      */
15929     showDelay: 200,
15930     // doc'd in BaseItem
15931     hideDelay: 200,
15932
15933     // private
15934     ctype: "Roo.menu.Item",
15935     
15936     // private
15937     onRender : function(container, position){
15938         var el = document.createElement("a");
15939         el.hideFocus = true;
15940         el.unselectable = "on";
15941         el.href = this.href || "#";
15942         if(this.hrefTarget){
15943             el.target = this.hrefTarget;
15944         }
15945         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15946         
15947         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15948         
15949         el.innerHTML = String.format(
15950                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15951                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15952         this.el = el;
15953         Roo.menu.Item.superclass.onRender.call(this, container, position);
15954     },
15955
15956     /**
15957      * Sets the text to display in this menu item
15958      * @param {String} text The text to display
15959      * @param {Boolean} isHTML true to indicate text is pure html.
15960      */
15961     setText : function(text, isHTML){
15962         if (isHTML) {
15963             this.html = text;
15964         } else {
15965             this.text = text;
15966             this.html = '';
15967         }
15968         if(this.rendered){
15969             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15970      
15971             this.el.update(String.format(
15972                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15973                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15974             this.parentMenu.autoWidth();
15975         }
15976     },
15977
15978     // private
15979     handleClick : function(e){
15980         if(!this.href){ // if no link defined, stop the event automatically
15981             e.stopEvent();
15982         }
15983         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15984     },
15985
15986     // private
15987     activate : function(autoExpand){
15988         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15989             this.focus();
15990             if(autoExpand){
15991                 this.expandMenu();
15992             }
15993         }
15994         return true;
15995     },
15996
15997     // private
15998     shouldDeactivate : function(e){
15999         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
16000             if(this.menu && this.menu.isVisible()){
16001                 return !this.menu.getEl().getRegion().contains(e.getPoint());
16002             }
16003             return true;
16004         }
16005         return false;
16006     },
16007
16008     // private
16009     deactivate : function(){
16010         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
16011         this.hideMenu();
16012     },
16013
16014     // private
16015     expandMenu : function(autoActivate){
16016         if(!this.disabled && this.menu){
16017             clearTimeout(this.hideTimer);
16018             delete this.hideTimer;
16019             if(!this.menu.isVisible() && !this.showTimer){
16020                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16021             }else if (this.menu.isVisible() && autoActivate){
16022                 this.menu.tryActivate(0, 1);
16023             }
16024         }
16025     },
16026
16027     // private
16028     deferExpand : function(autoActivate){
16029         delete this.showTimer;
16030         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16031         if(autoActivate){
16032             this.menu.tryActivate(0, 1);
16033         }
16034     },
16035
16036     // private
16037     hideMenu : function(){
16038         clearTimeout(this.showTimer);
16039         delete this.showTimer;
16040         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16041             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16042         }
16043     },
16044
16045     // private
16046     deferHide : function(){
16047         delete this.hideTimer;
16048         this.menu.hide();
16049     }
16050 });/*
16051  * Based on:
16052  * Ext JS Library 1.1.1
16053  * Copyright(c) 2006-2007, Ext JS, LLC.
16054  *
16055  * Originally Released Under LGPL - original licence link has changed is not relivant.
16056  *
16057  * Fork - LGPL
16058  * <script type="text/javascript">
16059  */
16060  
16061 /**
16062  * @class Roo.menu.CheckItem
16063  * @extends Roo.menu.Item
16064  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16065  * @constructor
16066  * Creates a new CheckItem
16067  * @param {Object} config Configuration options
16068  */
16069 Roo.menu.CheckItem = function(config){
16070     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16071     this.addEvents({
16072         /**
16073          * @event beforecheckchange
16074          * Fires before the checked value is set, providing an opportunity to cancel if needed
16075          * @param {Roo.menu.CheckItem} this
16076          * @param {Boolean} checked The new checked value that will be set
16077          */
16078         "beforecheckchange" : true,
16079         /**
16080          * @event checkchange
16081          * Fires after the checked value has been set
16082          * @param {Roo.menu.CheckItem} this
16083          * @param {Boolean} checked The checked value that was set
16084          */
16085         "checkchange" : true
16086     });
16087     if(this.checkHandler){
16088         this.on('checkchange', this.checkHandler, this.scope);
16089     }
16090 };
16091 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16092     /**
16093      * @cfg {String} group
16094      * All check items with the same group name will automatically be grouped into a single-select
16095      * radio button group (defaults to '')
16096      */
16097     /**
16098      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16099      */
16100     itemCls : "x-menu-item x-menu-check-item",
16101     /**
16102      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16103      */
16104     groupClass : "x-menu-group-item",
16105
16106     /**
16107      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16108      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16109      * initialized with checked = true will be rendered as checked.
16110      */
16111     checked: false,
16112
16113     // private
16114     ctype: "Roo.menu.CheckItem",
16115
16116     // private
16117     onRender : function(c){
16118         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16119         if(this.group){
16120             this.el.addClass(this.groupClass);
16121         }
16122         Roo.menu.MenuMgr.registerCheckable(this);
16123         if(this.checked){
16124             this.checked = false;
16125             this.setChecked(true, true);
16126         }
16127     },
16128
16129     // private
16130     destroy : function(){
16131         if(this.rendered){
16132             Roo.menu.MenuMgr.unregisterCheckable(this);
16133         }
16134         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16135     },
16136
16137     /**
16138      * Set the checked state of this item
16139      * @param {Boolean} checked The new checked value
16140      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16141      */
16142     setChecked : function(state, suppressEvent){
16143         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16144             if(this.container){
16145                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16146             }
16147             this.checked = state;
16148             if(suppressEvent !== true){
16149                 this.fireEvent("checkchange", this, state);
16150             }
16151         }
16152     },
16153
16154     // private
16155     handleClick : function(e){
16156        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16157            this.setChecked(!this.checked);
16158        }
16159        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16160     }
16161 });/*
16162  * Based on:
16163  * Ext JS Library 1.1.1
16164  * Copyright(c) 2006-2007, Ext JS, LLC.
16165  *
16166  * Originally Released Under LGPL - original licence link has changed is not relivant.
16167  *
16168  * Fork - LGPL
16169  * <script type="text/javascript">
16170  */
16171  
16172 /**
16173  * @class Roo.menu.DateItem
16174  * @extends Roo.menu.Adapter
16175  * A menu item that wraps the {@link Roo.DatPicker} component.
16176  * @constructor
16177  * Creates a new DateItem
16178  * @param {Object} config Configuration options
16179  */
16180 Roo.menu.DateItem = function(config){
16181     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16182     /** The Roo.DatePicker object @type Roo.DatePicker */
16183     this.picker = this.component;
16184     this.addEvents({select: true});
16185     
16186     this.picker.on("render", function(picker){
16187         picker.getEl().swallowEvent("click");
16188         picker.container.addClass("x-menu-date-item");
16189     });
16190
16191     this.picker.on("select", this.onSelect, this);
16192 };
16193
16194 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16195     // private
16196     onSelect : function(picker, date){
16197         this.fireEvent("select", this, date, picker);
16198         Roo.menu.DateItem.superclass.handleClick.call(this);
16199     }
16200 });/*
16201  * Based on:
16202  * Ext JS Library 1.1.1
16203  * Copyright(c) 2006-2007, Ext JS, LLC.
16204  *
16205  * Originally Released Under LGPL - original licence link has changed is not relivant.
16206  *
16207  * Fork - LGPL
16208  * <script type="text/javascript">
16209  */
16210  
16211 /**
16212  * @class Roo.menu.ColorItem
16213  * @extends Roo.menu.Adapter
16214  * A menu item that wraps the {@link Roo.ColorPalette} component.
16215  * @constructor
16216  * Creates a new ColorItem
16217  * @param {Object} config Configuration options
16218  */
16219 Roo.menu.ColorItem = function(config){
16220     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16221     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16222     this.palette = this.component;
16223     this.relayEvents(this.palette, ["select"]);
16224     if(this.selectHandler){
16225         this.on('select', this.selectHandler, this.scope);
16226     }
16227 };
16228 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16229  * Based on:
16230  * Ext JS Library 1.1.1
16231  * Copyright(c) 2006-2007, Ext JS, LLC.
16232  *
16233  * Originally Released Under LGPL - original licence link has changed is not relivant.
16234  *
16235  * Fork - LGPL
16236  * <script type="text/javascript">
16237  */
16238  
16239
16240 /**
16241  * @class Roo.menu.DateMenu
16242  * @extends Roo.menu.Menu
16243  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16244  * @constructor
16245  * Creates a new DateMenu
16246  * @param {Object} config Configuration options
16247  */
16248 Roo.menu.DateMenu = function(config){
16249     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16250     this.plain = true;
16251     var di = new Roo.menu.DateItem(config);
16252     this.add(di);
16253     /**
16254      * The {@link Roo.DatePicker} instance for this DateMenu
16255      * @type DatePicker
16256      */
16257     this.picker = di.picker;
16258     /**
16259      * @event select
16260      * @param {DatePicker} picker
16261      * @param {Date} date
16262      */
16263     this.relayEvents(di, ["select"]);
16264     this.on('beforeshow', function(){
16265         if(this.picker){
16266             this.picker.hideMonthPicker(false);
16267         }
16268     }, this);
16269 };
16270 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16271     cls:'x-date-menu'
16272 });/*
16273  * Based on:
16274  * Ext JS Library 1.1.1
16275  * Copyright(c) 2006-2007, Ext JS, LLC.
16276  *
16277  * Originally Released Under LGPL - original licence link has changed is not relivant.
16278  *
16279  * Fork - LGPL
16280  * <script type="text/javascript">
16281  */
16282  
16283
16284 /**
16285  * @class Roo.menu.ColorMenu
16286  * @extends Roo.menu.Menu
16287  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16288  * @constructor
16289  * Creates a new ColorMenu
16290  * @param {Object} config Configuration options
16291  */
16292 Roo.menu.ColorMenu = function(config){
16293     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16294     this.plain = true;
16295     var ci = new Roo.menu.ColorItem(config);
16296     this.add(ci);
16297     /**
16298      * The {@link Roo.ColorPalette} instance for this ColorMenu
16299      * @type ColorPalette
16300      */
16301     this.palette = ci.palette;
16302     /**
16303      * @event select
16304      * @param {ColorPalette} palette
16305      * @param {String} color
16306      */
16307     this.relayEvents(ci, ["select"]);
16308 };
16309 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16310  * Based on:
16311  * Ext JS Library 1.1.1
16312  * Copyright(c) 2006-2007, Ext JS, LLC.
16313  *
16314  * Originally Released Under LGPL - original licence link has changed is not relivant.
16315  *
16316  * Fork - LGPL
16317  * <script type="text/javascript">
16318  */
16319  
16320 /**
16321  * @class Roo.form.TextItem
16322  * @extends Roo.BoxComponent
16323  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16324  * @constructor
16325  * Creates a new TextItem
16326  * @param {Object} config Configuration options
16327  */
16328 Roo.form.TextItem = function(config){
16329     Roo.form.TextItem.superclass.constructor.call(this, config);
16330 };
16331
16332 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16333     
16334     /**
16335      * @cfg {String} tag the tag for this item (default div)
16336      */
16337     tag : 'div',
16338     /**
16339      * @cfg {String} html the content for this item
16340      */
16341     html : '',
16342     
16343     getAutoCreate : function()
16344     {
16345         var cfg = {
16346             id: this.id,
16347             tag: this.tag,
16348             html: this.html,
16349             cls: 'x-form-item'
16350         };
16351         
16352         return cfg;
16353         
16354     },
16355     
16356     onRender : function(ct, position)
16357     {
16358         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16359         
16360         if(!this.el){
16361             var cfg = this.getAutoCreate();
16362             if(!cfg.name){
16363                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16364             }
16365             if (!cfg.name.length) {
16366                 delete cfg.name;
16367             }
16368             this.el = ct.createChild(cfg, position);
16369         }
16370     }
16371     
16372 });/*
16373  * Based on:
16374  * Ext JS Library 1.1.1
16375  * Copyright(c) 2006-2007, Ext JS, LLC.
16376  *
16377  * Originally Released Under LGPL - original licence link has changed is not relivant.
16378  *
16379  * Fork - LGPL
16380  * <script type="text/javascript">
16381  */
16382  
16383 /**
16384  * @class Roo.form.Field
16385  * @extends Roo.BoxComponent
16386  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16387  * @constructor
16388  * Creates a new Field
16389  * @param {Object} config Configuration options
16390  */
16391 Roo.form.Field = function(config){
16392     Roo.form.Field.superclass.constructor.call(this, config);
16393 };
16394
16395 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16396     /**
16397      * @cfg {String} fieldLabel Label to use when rendering a form.
16398      */
16399        /**
16400      * @cfg {String} qtip Mouse over tip
16401      */
16402      
16403     /**
16404      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16405      */
16406     invalidClass : "x-form-invalid",
16407     /**
16408      * @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")
16409      */
16410     invalidText : "The value in this field is invalid",
16411     /**
16412      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16413      */
16414     focusClass : "x-form-focus",
16415     /**
16416      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16417       automatic validation (defaults to "keyup").
16418      */
16419     validationEvent : "keyup",
16420     /**
16421      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16422      */
16423     validateOnBlur : true,
16424     /**
16425      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16426      */
16427     validationDelay : 250,
16428     /**
16429      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16430      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16431      */
16432     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16433     /**
16434      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16435      */
16436     fieldClass : "x-form-field",
16437     /**
16438      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16439      *<pre>
16440 Value         Description
16441 -----------   ----------------------------------------------------------------------
16442 qtip          Display a quick tip when the user hovers over the field
16443 title         Display a default browser title attribute popup
16444 under         Add a block div beneath the field containing the error text
16445 side          Add an error icon to the right of the field with a popup on hover
16446 [element id]  Add the error text directly to the innerHTML of the specified element
16447 </pre>
16448      */
16449     msgTarget : 'qtip',
16450     /**
16451      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16452      */
16453     msgFx : 'normal',
16454
16455     /**
16456      * @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.
16457      */
16458     readOnly : false,
16459
16460     /**
16461      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16462      */
16463     disabled : false,
16464
16465     /**
16466      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16467      */
16468     inputType : undefined,
16469     
16470     /**
16471      * @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).
16472          */
16473         tabIndex : undefined,
16474         
16475     // private
16476     isFormField : true,
16477
16478     // private
16479     hasFocus : false,
16480     /**
16481      * @property {Roo.Element} fieldEl
16482      * Element Containing the rendered Field (with label etc.)
16483      */
16484     /**
16485      * @cfg {Mixed} value A value to initialize this field with.
16486      */
16487     value : undefined,
16488
16489     /**
16490      * @cfg {String} name The field's HTML name attribute.
16491      */
16492     /**
16493      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16494      */
16495     // private
16496     loadedValue : false,
16497      
16498      
16499         // private ??
16500         initComponent : function(){
16501         Roo.form.Field.superclass.initComponent.call(this);
16502         this.addEvents({
16503             /**
16504              * @event focus
16505              * Fires when this field receives input focus.
16506              * @param {Roo.form.Field} this
16507              */
16508             focus : true,
16509             /**
16510              * @event blur
16511              * Fires when this field loses input focus.
16512              * @param {Roo.form.Field} this
16513              */
16514             blur : true,
16515             /**
16516              * @event specialkey
16517              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16518              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16519              * @param {Roo.form.Field} this
16520              * @param {Roo.EventObject} e The event object
16521              */
16522             specialkey : true,
16523             /**
16524              * @event change
16525              * Fires just before the field blurs if the field value has changed.
16526              * @param {Roo.form.Field} this
16527              * @param {Mixed} newValue The new value
16528              * @param {Mixed} oldValue The original value
16529              */
16530             change : true,
16531             /**
16532              * @event invalid
16533              * Fires after the field has been marked as invalid.
16534              * @param {Roo.form.Field} this
16535              * @param {String} msg The validation message
16536              */
16537             invalid : true,
16538             /**
16539              * @event valid
16540              * Fires after the field has been validated with no errors.
16541              * @param {Roo.form.Field} this
16542              */
16543             valid : true,
16544              /**
16545              * @event keyup
16546              * Fires after the key up
16547              * @param {Roo.form.Field} this
16548              * @param {Roo.EventObject}  e The event Object
16549              */
16550             keyup : true
16551         });
16552     },
16553
16554     /**
16555      * Returns the name attribute of the field if available
16556      * @return {String} name The field name
16557      */
16558     getName: function(){
16559          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16560     },
16561
16562     // private
16563     onRender : function(ct, position){
16564         Roo.form.Field.superclass.onRender.call(this, ct, position);
16565         if(!this.el){
16566             var cfg = this.getAutoCreate();
16567             if(!cfg.name){
16568                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16569             }
16570             if (!cfg.name.length) {
16571                 delete cfg.name;
16572             }
16573             if(this.inputType){
16574                 cfg.type = this.inputType;
16575             }
16576             this.el = ct.createChild(cfg, position);
16577         }
16578         var type = this.el.dom.type;
16579         if(type){
16580             if(type == 'password'){
16581                 type = 'text';
16582             }
16583             this.el.addClass('x-form-'+type);
16584         }
16585         if(this.readOnly){
16586             this.el.dom.readOnly = true;
16587         }
16588         if(this.tabIndex !== undefined){
16589             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16590         }
16591
16592         this.el.addClass([this.fieldClass, this.cls]);
16593         this.initValue();
16594     },
16595
16596     /**
16597      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16598      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16599      * @return {Roo.form.Field} this
16600      */
16601     applyTo : function(target){
16602         this.allowDomMove = false;
16603         this.el = Roo.get(target);
16604         this.render(this.el.dom.parentNode);
16605         return this;
16606     },
16607
16608     // private
16609     initValue : function(){
16610         if(this.value !== undefined){
16611             this.setValue(this.value);
16612         }else if(this.el.dom.value.length > 0){
16613             this.setValue(this.el.dom.value);
16614         }
16615     },
16616
16617     /**
16618      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16619      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16620      */
16621     isDirty : function() {
16622         if(this.disabled) {
16623             return false;
16624         }
16625         return String(this.getValue()) !== String(this.originalValue);
16626     },
16627
16628     /**
16629      * stores the current value in loadedValue
16630      */
16631     resetHasChanged : function()
16632     {
16633         this.loadedValue = String(this.getValue());
16634     },
16635     /**
16636      * checks the current value against the 'loaded' value.
16637      * Note - will return false if 'resetHasChanged' has not been called first.
16638      */
16639     hasChanged : function()
16640     {
16641         if(this.disabled || this.readOnly) {
16642             return false;
16643         }
16644         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16645     },
16646     
16647     
16648     
16649     // private
16650     afterRender : function(){
16651         Roo.form.Field.superclass.afterRender.call(this);
16652         this.initEvents();
16653     },
16654
16655     // private
16656     fireKey : function(e){
16657         //Roo.log('field ' + e.getKey());
16658         if(e.isNavKeyPress()){
16659             this.fireEvent("specialkey", this, e);
16660         }
16661     },
16662
16663     /**
16664      * Resets the current field value to the originally loaded value and clears any validation messages
16665      */
16666     reset : function(){
16667         this.setValue(this.resetValue);
16668         this.originalValue = this.getValue();
16669         this.clearInvalid();
16670     },
16671
16672     // private
16673     initEvents : function(){
16674         // safari killled keypress - so keydown is now used..
16675         this.el.on("keydown" , this.fireKey,  this);
16676         this.el.on("focus", this.onFocus,  this);
16677         this.el.on("blur", this.onBlur,  this);
16678         this.el.relayEvent('keyup', this);
16679
16680         // reference to original value for reset
16681         this.originalValue = this.getValue();
16682         this.resetValue =  this.getValue();
16683     },
16684
16685     // private
16686     onFocus : function(){
16687         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16688             this.el.addClass(this.focusClass);
16689         }
16690         if(!this.hasFocus){
16691             this.hasFocus = true;
16692             this.startValue = this.getValue();
16693             this.fireEvent("focus", this);
16694         }
16695     },
16696
16697     beforeBlur : Roo.emptyFn,
16698
16699     // private
16700     onBlur : function(){
16701         this.beforeBlur();
16702         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16703             this.el.removeClass(this.focusClass);
16704         }
16705         this.hasFocus = false;
16706         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16707             this.validate();
16708         }
16709         var v = this.getValue();
16710         if(String(v) !== String(this.startValue)){
16711             this.fireEvent('change', this, v, this.startValue);
16712         }
16713         this.fireEvent("blur", this);
16714     },
16715
16716     /**
16717      * Returns whether or not the field value is currently valid
16718      * @param {Boolean} preventMark True to disable marking the field invalid
16719      * @return {Boolean} True if the value is valid, else false
16720      */
16721     isValid : function(preventMark){
16722         if(this.disabled){
16723             return true;
16724         }
16725         var restore = this.preventMark;
16726         this.preventMark = preventMark === true;
16727         var v = this.validateValue(this.processValue(this.getRawValue()));
16728         this.preventMark = restore;
16729         return v;
16730     },
16731
16732     /**
16733      * Validates the field value
16734      * @return {Boolean} True if the value is valid, else false
16735      */
16736     validate : function(){
16737         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16738             this.clearInvalid();
16739             return true;
16740         }
16741         return false;
16742     },
16743
16744     processValue : function(value){
16745         return value;
16746     },
16747
16748     // private
16749     // Subclasses should provide the validation implementation by overriding this
16750     validateValue : function(value){
16751         return true;
16752     },
16753
16754     /**
16755      * Mark this field as invalid
16756      * @param {String} msg The validation message
16757      */
16758     markInvalid : function(msg){
16759         if(!this.rendered || this.preventMark){ // not rendered
16760             return;
16761         }
16762         
16763         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16764         
16765         obj.el.addClass(this.invalidClass);
16766         msg = msg || this.invalidText;
16767         switch(this.msgTarget){
16768             case 'qtip':
16769                 obj.el.dom.qtip = msg;
16770                 obj.el.dom.qclass = 'x-form-invalid-tip';
16771                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16772                     Roo.QuickTips.enable();
16773                 }
16774                 break;
16775             case 'title':
16776                 this.el.dom.title = msg;
16777                 break;
16778             case 'under':
16779                 if(!this.errorEl){
16780                     var elp = this.el.findParent('.x-form-element', 5, true);
16781                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16782                     this.errorEl.setWidth(elp.getWidth(true)-20);
16783                 }
16784                 this.errorEl.update(msg);
16785                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16786                 break;
16787             case 'side':
16788                 if(!this.errorIcon){
16789                     var elp = this.el.findParent('.x-form-element', 5, true);
16790                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16791                 }
16792                 this.alignErrorIcon();
16793                 this.errorIcon.dom.qtip = msg;
16794                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16795                 this.errorIcon.show();
16796                 this.on('resize', this.alignErrorIcon, this);
16797                 break;
16798             default:
16799                 var t = Roo.getDom(this.msgTarget);
16800                 t.innerHTML = msg;
16801                 t.style.display = this.msgDisplay;
16802                 break;
16803         }
16804         this.fireEvent('invalid', this, msg);
16805     },
16806
16807     // private
16808     alignErrorIcon : function(){
16809         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16810     },
16811
16812     /**
16813      * Clear any invalid styles/messages for this field
16814      */
16815     clearInvalid : function(){
16816         if(!this.rendered || this.preventMark){ // not rendered
16817             return;
16818         }
16819         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16820         
16821         obj.el.removeClass(this.invalidClass);
16822         switch(this.msgTarget){
16823             case 'qtip':
16824                 obj.el.dom.qtip = '';
16825                 break;
16826             case 'title':
16827                 this.el.dom.title = '';
16828                 break;
16829             case 'under':
16830                 if(this.errorEl){
16831                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16832                 }
16833                 break;
16834             case 'side':
16835                 if(this.errorIcon){
16836                     this.errorIcon.dom.qtip = '';
16837                     this.errorIcon.hide();
16838                     this.un('resize', this.alignErrorIcon, this);
16839                 }
16840                 break;
16841             default:
16842                 var t = Roo.getDom(this.msgTarget);
16843                 t.innerHTML = '';
16844                 t.style.display = 'none';
16845                 break;
16846         }
16847         this.fireEvent('valid', this);
16848     },
16849
16850     /**
16851      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16852      * @return {Mixed} value The field value
16853      */
16854     getRawValue : function(){
16855         var v = this.el.getValue();
16856         
16857         return v;
16858     },
16859
16860     /**
16861      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16862      * @return {Mixed} value The field value
16863      */
16864     getValue : function(){
16865         var v = this.el.getValue();
16866          
16867         return v;
16868     },
16869
16870     /**
16871      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16872      * @param {Mixed} value The value to set
16873      */
16874     setRawValue : function(v){
16875         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16876     },
16877
16878     /**
16879      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16880      * @param {Mixed} value The value to set
16881      */
16882     setValue : function(v){
16883         this.value = v;
16884         if(this.rendered){
16885             this.el.dom.value = (v === null || v === undefined ? '' : v);
16886              this.validate();
16887         }
16888     },
16889
16890     adjustSize : function(w, h){
16891         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16892         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16893         return s;
16894     },
16895
16896     adjustWidth : function(tag, w){
16897         tag = tag.toLowerCase();
16898         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16899             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16900                 if(tag == 'input'){
16901                     return w + 2;
16902                 }
16903                 if(tag == 'textarea'){
16904                     return w-2;
16905                 }
16906             }else if(Roo.isOpera){
16907                 if(tag == 'input'){
16908                     return w + 2;
16909                 }
16910                 if(tag == 'textarea'){
16911                     return w-2;
16912                 }
16913             }
16914         }
16915         return w;
16916     }
16917 });
16918
16919
16920 // anything other than normal should be considered experimental
16921 Roo.form.Field.msgFx = {
16922     normal : {
16923         show: function(msgEl, f){
16924             msgEl.setDisplayed('block');
16925         },
16926
16927         hide : function(msgEl, f){
16928             msgEl.setDisplayed(false).update('');
16929         }
16930     },
16931
16932     slide : {
16933         show: function(msgEl, f){
16934             msgEl.slideIn('t', {stopFx:true});
16935         },
16936
16937         hide : function(msgEl, f){
16938             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16939         }
16940     },
16941
16942     slideRight : {
16943         show: function(msgEl, f){
16944             msgEl.fixDisplay();
16945             msgEl.alignTo(f.el, 'tl-tr');
16946             msgEl.slideIn('l', {stopFx:true});
16947         },
16948
16949         hide : function(msgEl, f){
16950             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16951         }
16952     }
16953 };/*
16954  * Based on:
16955  * Ext JS Library 1.1.1
16956  * Copyright(c) 2006-2007, Ext JS, LLC.
16957  *
16958  * Originally Released Under LGPL - original licence link has changed is not relivant.
16959  *
16960  * Fork - LGPL
16961  * <script type="text/javascript">
16962  */
16963  
16964
16965 /**
16966  * @class Roo.form.TextField
16967  * @extends Roo.form.Field
16968  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16969  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16970  * @constructor
16971  * Creates a new TextField
16972  * @param {Object} config Configuration options
16973  */
16974 Roo.form.TextField = function(config){
16975     Roo.form.TextField.superclass.constructor.call(this, config);
16976     this.addEvents({
16977         /**
16978          * @event autosize
16979          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16980          * according to the default logic, but this event provides a hook for the developer to apply additional
16981          * logic at runtime to resize the field if needed.
16982              * @param {Roo.form.Field} this This text field
16983              * @param {Number} width The new field width
16984              */
16985         autosize : true
16986     });
16987 };
16988
16989 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16990     /**
16991      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16992      */
16993     grow : false,
16994     /**
16995      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16996      */
16997     growMin : 30,
16998     /**
16999      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
17000      */
17001     growMax : 800,
17002     /**
17003      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
17004      */
17005     vtype : null,
17006     /**
17007      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
17008      */
17009     maskRe : null,
17010     /**
17011      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
17012      */
17013     disableKeyFilter : false,
17014     /**
17015      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
17016      */
17017     allowBlank : true,
17018     /**
17019      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
17020      */
17021     minLength : 0,
17022     /**
17023      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17024      */
17025     maxLength : Number.MAX_VALUE,
17026     /**
17027      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17028      */
17029     minLengthText : "The minimum length for this field is {0}",
17030     /**
17031      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17032      */
17033     maxLengthText : "The maximum length for this field is {0}",
17034     /**
17035      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17036      */
17037     selectOnFocus : false,
17038     /**
17039      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17040      */    
17041     allowLeadingSpace : false,
17042     /**
17043      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17044      */
17045     blankText : "This field is required",
17046     /**
17047      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17048      * If available, this function will be called only after the basic validators all return true, and will be passed the
17049      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17050      */
17051     validator : null,
17052     /**
17053      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17054      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17055      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17056      */
17057     regex : null,
17058     /**
17059      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17060      */
17061     regexText : "",
17062     /**
17063      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17064      */
17065     emptyText : null,
17066    
17067
17068     // private
17069     initEvents : function()
17070     {
17071         if (this.emptyText) {
17072             this.el.attr('placeholder', this.emptyText);
17073         }
17074         
17075         Roo.form.TextField.superclass.initEvents.call(this);
17076         if(this.validationEvent == 'keyup'){
17077             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17078             this.el.on('keyup', this.filterValidation, this);
17079         }
17080         else if(this.validationEvent !== false){
17081             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17082         }
17083         
17084         if(this.selectOnFocus){
17085             this.on("focus", this.preFocus, this);
17086         }
17087         if (!this.allowLeadingSpace) {
17088             this.on('blur', this.cleanLeadingSpace, this);
17089         }
17090         
17091         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17092             this.el.on("keypress", this.filterKeys, this);
17093         }
17094         if(this.grow){
17095             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17096             this.el.on("click", this.autoSize,  this);
17097         }
17098         if(this.el.is('input[type=password]') && Roo.isSafari){
17099             this.el.on('keydown', this.SafariOnKeyDown, this);
17100         }
17101     },
17102
17103     processValue : function(value){
17104         if(this.stripCharsRe){
17105             var newValue = value.replace(this.stripCharsRe, '');
17106             if(newValue !== value){
17107                 this.setRawValue(newValue);
17108                 return newValue;
17109             }
17110         }
17111         return value;
17112     },
17113
17114     filterValidation : function(e){
17115         if(!e.isNavKeyPress()){
17116             this.validationTask.delay(this.validationDelay);
17117         }
17118     },
17119
17120     // private
17121     onKeyUp : function(e){
17122         if(!e.isNavKeyPress()){
17123             this.autoSize();
17124         }
17125     },
17126     // private - clean the leading white space
17127     cleanLeadingSpace : function(e)
17128     {
17129         if ( this.inputType == 'file') {
17130             return;
17131         }
17132         
17133         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17134     },
17135     /**
17136      * Resets the current field value to the originally-loaded value and clears any validation messages.
17137      *  
17138      */
17139     reset : function(){
17140         Roo.form.TextField.superclass.reset.call(this);
17141        
17142     }, 
17143     // private
17144     preFocus : function(){
17145         
17146         if(this.selectOnFocus){
17147             this.el.dom.select();
17148         }
17149     },
17150
17151     
17152     // private
17153     filterKeys : function(e){
17154         var k = e.getKey();
17155         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17156             return;
17157         }
17158         var c = e.getCharCode(), cc = String.fromCharCode(c);
17159         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17160             return;
17161         }
17162         if(!this.maskRe.test(cc)){
17163             e.stopEvent();
17164         }
17165     },
17166
17167     setValue : function(v){
17168         
17169         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17170         
17171         this.autoSize();
17172     },
17173
17174     /**
17175      * Validates a value according to the field's validation rules and marks the field as invalid
17176      * if the validation fails
17177      * @param {Mixed} value The value to validate
17178      * @return {Boolean} True if the value is valid, else false
17179      */
17180     validateValue : function(value){
17181         if(value.length < 1)  { // if it's blank
17182              if(this.allowBlank){
17183                 this.clearInvalid();
17184                 return true;
17185              }else{
17186                 this.markInvalid(this.blankText);
17187                 return false;
17188              }
17189         }
17190         if(value.length < this.minLength){
17191             this.markInvalid(String.format(this.minLengthText, this.minLength));
17192             return false;
17193         }
17194         if(value.length > this.maxLength){
17195             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17196             return false;
17197         }
17198         if(this.vtype){
17199             var vt = Roo.form.VTypes;
17200             if(!vt[this.vtype](value, this)){
17201                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17202                 return false;
17203             }
17204         }
17205         if(typeof this.validator == "function"){
17206             var msg = this.validator(value);
17207             if(msg !== true){
17208                 this.markInvalid(msg);
17209                 return false;
17210             }
17211         }
17212         if(this.regex && !this.regex.test(value)){
17213             this.markInvalid(this.regexText);
17214             return false;
17215         }
17216         return true;
17217     },
17218
17219     /**
17220      * Selects text in this field
17221      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17222      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17223      */
17224     selectText : function(start, end){
17225         var v = this.getRawValue();
17226         if(v.length > 0){
17227             start = start === undefined ? 0 : start;
17228             end = end === undefined ? v.length : end;
17229             var d = this.el.dom;
17230             if(d.setSelectionRange){
17231                 d.setSelectionRange(start, end);
17232             }else if(d.createTextRange){
17233                 var range = d.createTextRange();
17234                 range.moveStart("character", start);
17235                 range.moveEnd("character", v.length-end);
17236                 range.select();
17237             }
17238         }
17239     },
17240
17241     /**
17242      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17243      * This only takes effect if grow = true, and fires the autosize event.
17244      */
17245     autoSize : function(){
17246         if(!this.grow || !this.rendered){
17247             return;
17248         }
17249         if(!this.metrics){
17250             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17251         }
17252         var el = this.el;
17253         var v = el.dom.value;
17254         var d = document.createElement('div');
17255         d.appendChild(document.createTextNode(v));
17256         v = d.innerHTML;
17257         d = null;
17258         v += "&#160;";
17259         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17260         this.el.setWidth(w);
17261         this.fireEvent("autosize", this, w);
17262     },
17263     
17264     // private
17265     SafariOnKeyDown : function(event)
17266     {
17267         // this is a workaround for a password hang bug on chrome/ webkit.
17268         
17269         var isSelectAll = false;
17270         
17271         if(this.el.dom.selectionEnd > 0){
17272             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17273         }
17274         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17275             event.preventDefault();
17276             this.setValue('');
17277             return;
17278         }
17279         
17280         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17281             
17282             event.preventDefault();
17283             // this is very hacky as keydown always get's upper case.
17284             
17285             var cc = String.fromCharCode(event.getCharCode());
17286             
17287             
17288             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17289             
17290         }
17291         
17292         
17293     }
17294 });/*
17295  * Based on:
17296  * Ext JS Library 1.1.1
17297  * Copyright(c) 2006-2007, Ext JS, LLC.
17298  *
17299  * Originally Released Under LGPL - original licence link has changed is not relivant.
17300  *
17301  * Fork - LGPL
17302  * <script type="text/javascript">
17303  */
17304  
17305 /**
17306  * @class Roo.form.Hidden
17307  * @extends Roo.form.TextField
17308  * Simple Hidden element used on forms 
17309  * 
17310  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17311  * 
17312  * @constructor
17313  * Creates a new Hidden form element.
17314  * @param {Object} config Configuration options
17315  */
17316
17317
17318
17319 // easy hidden field...
17320 Roo.form.Hidden = function(config){
17321     Roo.form.Hidden.superclass.constructor.call(this, config);
17322 };
17323   
17324 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17325     fieldLabel:      '',
17326     inputType:      'hidden',
17327     width:          50,
17328     allowBlank:     true,
17329     labelSeparator: '',
17330     hidden:         true,
17331     itemCls :       'x-form-item-display-none'
17332
17333
17334 });
17335
17336
17337 /*
17338  * Based on:
17339  * Ext JS Library 1.1.1
17340  * Copyright(c) 2006-2007, Ext JS, LLC.
17341  *
17342  * Originally Released Under LGPL - original licence link has changed is not relivant.
17343  *
17344  * Fork - LGPL
17345  * <script type="text/javascript">
17346  */
17347  
17348 /**
17349  * @class Roo.form.TriggerField
17350  * @extends Roo.form.TextField
17351  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17352  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17353  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17354  * for which you can provide a custom implementation.  For example:
17355  * <pre><code>
17356 var trigger = new Roo.form.TriggerField();
17357 trigger.onTriggerClick = myTriggerFn;
17358 trigger.applyTo('my-field');
17359 </code></pre>
17360  *
17361  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17362  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17363  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17364  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17365  * @constructor
17366  * Create a new TriggerField.
17367  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17368  * to the base TextField)
17369  */
17370 Roo.form.TriggerField = function(config){
17371     this.mimicing = false;
17372     Roo.form.TriggerField.superclass.constructor.call(this, config);
17373 };
17374
17375 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17376     /**
17377      * @cfg {String} triggerClass A CSS class to apply to the trigger
17378      */
17379     /**
17380      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17381      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17382      */
17383     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17384     /**
17385      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17386      */
17387     hideTrigger:false,
17388
17389     /** @cfg {Boolean} grow @hide */
17390     /** @cfg {Number} growMin @hide */
17391     /** @cfg {Number} growMax @hide */
17392
17393     /**
17394      * @hide 
17395      * @method
17396      */
17397     autoSize: Roo.emptyFn,
17398     // private
17399     monitorTab : true,
17400     // private
17401     deferHeight : true,
17402
17403     
17404     actionMode : 'wrap',
17405     // private
17406     onResize : function(w, h){
17407         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17408         if(typeof w == 'number'){
17409             var x = w - this.trigger.getWidth();
17410             this.el.setWidth(this.adjustWidth('input', x));
17411             this.trigger.setStyle('left', x+'px');
17412         }
17413     },
17414
17415     // private
17416     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17417
17418     // private
17419     getResizeEl : function(){
17420         return this.wrap;
17421     },
17422
17423     // private
17424     getPositionEl : function(){
17425         return this.wrap;
17426     },
17427
17428     // private
17429     alignErrorIcon : function(){
17430         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17431     },
17432
17433     // private
17434     onRender : function(ct, position){
17435         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17436         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17437         this.trigger = this.wrap.createChild(this.triggerConfig ||
17438                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17439         if(this.hideTrigger){
17440             this.trigger.setDisplayed(false);
17441         }
17442         this.initTrigger();
17443         if(!this.width){
17444             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17445         }
17446     },
17447
17448     // private
17449     initTrigger : function(){
17450         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17451         this.trigger.addClassOnOver('x-form-trigger-over');
17452         this.trigger.addClassOnClick('x-form-trigger-click');
17453     },
17454
17455     // private
17456     onDestroy : function(){
17457         if(this.trigger){
17458             this.trigger.removeAllListeners();
17459             this.trigger.remove();
17460         }
17461         if(this.wrap){
17462             this.wrap.remove();
17463         }
17464         Roo.form.TriggerField.superclass.onDestroy.call(this);
17465     },
17466
17467     // private
17468     onFocus : function(){
17469         Roo.form.TriggerField.superclass.onFocus.call(this);
17470         if(!this.mimicing){
17471             this.wrap.addClass('x-trigger-wrap-focus');
17472             this.mimicing = true;
17473             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17474             if(this.monitorTab){
17475                 this.el.on("keydown", this.checkTab, this);
17476             }
17477         }
17478     },
17479
17480     // private
17481     checkTab : function(e){
17482         if(e.getKey() == e.TAB){
17483             this.triggerBlur();
17484         }
17485     },
17486
17487     // private
17488     onBlur : function(){
17489         // do nothing
17490     },
17491
17492     // private
17493     mimicBlur : function(e, t){
17494         if(!this.wrap.contains(t) && this.validateBlur()){
17495             this.triggerBlur();
17496         }
17497     },
17498
17499     // private
17500     triggerBlur : function(){
17501         this.mimicing = false;
17502         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17503         if(this.monitorTab){
17504             this.el.un("keydown", this.checkTab, this);
17505         }
17506         this.wrap.removeClass('x-trigger-wrap-focus');
17507         Roo.form.TriggerField.superclass.onBlur.call(this);
17508     },
17509
17510     // private
17511     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17512     validateBlur : function(e, t){
17513         return true;
17514     },
17515
17516     // private
17517     onDisable : function(){
17518         Roo.form.TriggerField.superclass.onDisable.call(this);
17519         if(this.wrap){
17520             this.wrap.addClass('x-item-disabled');
17521         }
17522     },
17523
17524     // private
17525     onEnable : function(){
17526         Roo.form.TriggerField.superclass.onEnable.call(this);
17527         if(this.wrap){
17528             this.wrap.removeClass('x-item-disabled');
17529         }
17530     },
17531
17532     // private
17533     onShow : function(){
17534         var ae = this.getActionEl();
17535         
17536         if(ae){
17537             ae.dom.style.display = '';
17538             ae.dom.style.visibility = 'visible';
17539         }
17540     },
17541
17542     // private
17543     
17544     onHide : function(){
17545         var ae = this.getActionEl();
17546         ae.dom.style.display = 'none';
17547     },
17548
17549     /**
17550      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17551      * by an implementing function.
17552      * @method
17553      * @param {EventObject} e
17554      */
17555     onTriggerClick : Roo.emptyFn
17556 });
17557
17558 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17559 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17560 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17561 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17562     initComponent : function(){
17563         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17564
17565         this.triggerConfig = {
17566             tag:'span', cls:'x-form-twin-triggers', cn:[
17567             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17568             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17569         ]};
17570     },
17571
17572     getTrigger : function(index){
17573         return this.triggers[index];
17574     },
17575
17576     initTrigger : function(){
17577         var ts = this.trigger.select('.x-form-trigger', true);
17578         this.wrap.setStyle('overflow', 'hidden');
17579         var triggerField = this;
17580         ts.each(function(t, all, index){
17581             t.hide = function(){
17582                 var w = triggerField.wrap.getWidth();
17583                 this.dom.style.display = 'none';
17584                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17585             };
17586             t.show = function(){
17587                 var w = triggerField.wrap.getWidth();
17588                 this.dom.style.display = '';
17589                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17590             };
17591             var triggerIndex = 'Trigger'+(index+1);
17592
17593             if(this['hide'+triggerIndex]){
17594                 t.dom.style.display = 'none';
17595             }
17596             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17597             t.addClassOnOver('x-form-trigger-over');
17598             t.addClassOnClick('x-form-trigger-click');
17599         }, this);
17600         this.triggers = ts.elements;
17601     },
17602
17603     onTrigger1Click : Roo.emptyFn,
17604     onTrigger2Click : Roo.emptyFn
17605 });/*
17606  * Based on:
17607  * Ext JS Library 1.1.1
17608  * Copyright(c) 2006-2007, Ext JS, LLC.
17609  *
17610  * Originally Released Under LGPL - original licence link has changed is not relivant.
17611  *
17612  * Fork - LGPL
17613  * <script type="text/javascript">
17614  */
17615  
17616 /**
17617  * @class Roo.form.TextArea
17618  * @extends Roo.form.TextField
17619  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17620  * support for auto-sizing.
17621  * @constructor
17622  * Creates a new TextArea
17623  * @param {Object} config Configuration options
17624  */
17625 Roo.form.TextArea = function(config){
17626     Roo.form.TextArea.superclass.constructor.call(this, config);
17627     // these are provided exchanges for backwards compat
17628     // minHeight/maxHeight were replaced by growMin/growMax to be
17629     // compatible with TextField growing config values
17630     if(this.minHeight !== undefined){
17631         this.growMin = this.minHeight;
17632     }
17633     if(this.maxHeight !== undefined){
17634         this.growMax = this.maxHeight;
17635     }
17636 };
17637
17638 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17639     /**
17640      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17641      */
17642     growMin : 60,
17643     /**
17644      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17645      */
17646     growMax: 1000,
17647     /**
17648      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17649      * in the field (equivalent to setting overflow: hidden, defaults to false)
17650      */
17651     preventScrollbars: false,
17652     /**
17653      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17654      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17655      */
17656
17657     // private
17658     onRender : function(ct, position){
17659         if(!this.el){
17660             this.defaultAutoCreate = {
17661                 tag: "textarea",
17662                 style:"width:300px;height:60px;",
17663                 autocomplete: "new-password"
17664             };
17665         }
17666         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17667         if(this.grow){
17668             this.textSizeEl = Roo.DomHelper.append(document.body, {
17669                 tag: "pre", cls: "x-form-grow-sizer"
17670             });
17671             if(this.preventScrollbars){
17672                 this.el.setStyle("overflow", "hidden");
17673             }
17674             this.el.setHeight(this.growMin);
17675         }
17676     },
17677
17678     onDestroy : function(){
17679         if(this.textSizeEl){
17680             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17681         }
17682         Roo.form.TextArea.superclass.onDestroy.call(this);
17683     },
17684
17685     // private
17686     onKeyUp : function(e){
17687         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17688             this.autoSize();
17689         }
17690     },
17691
17692     /**
17693      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17694      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17695      */
17696     autoSize : function(){
17697         if(!this.grow || !this.textSizeEl){
17698             return;
17699         }
17700         var el = this.el;
17701         var v = el.dom.value;
17702         var ts = this.textSizeEl;
17703
17704         ts.innerHTML = '';
17705         ts.appendChild(document.createTextNode(v));
17706         v = ts.innerHTML;
17707
17708         Roo.fly(ts).setWidth(this.el.getWidth());
17709         if(v.length < 1){
17710             v = "&#160;&#160;";
17711         }else{
17712             if(Roo.isIE){
17713                 v = v.replace(/\n/g, '<p>&#160;</p>');
17714             }
17715             v += "&#160;\n&#160;";
17716         }
17717         ts.innerHTML = v;
17718         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17719         if(h != this.lastHeight){
17720             this.lastHeight = h;
17721             this.el.setHeight(h);
17722             this.fireEvent("autosize", this, h);
17723         }
17724     }
17725 });/*
17726  * Based on:
17727  * Ext JS Library 1.1.1
17728  * Copyright(c) 2006-2007, Ext JS, LLC.
17729  *
17730  * Originally Released Under LGPL - original licence link has changed is not relivant.
17731  *
17732  * Fork - LGPL
17733  * <script type="text/javascript">
17734  */
17735  
17736
17737 /**
17738  * @class Roo.form.NumberField
17739  * @extends Roo.form.TextField
17740  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17741  * @constructor
17742  * Creates a new NumberField
17743  * @param {Object} config Configuration options
17744  */
17745 Roo.form.NumberField = function(config){
17746     Roo.form.NumberField.superclass.constructor.call(this, config);
17747 };
17748
17749 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17750     /**
17751      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17752      */
17753     fieldClass: "x-form-field x-form-num-field",
17754     /**
17755      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17756      */
17757     allowDecimals : true,
17758     /**
17759      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17760      */
17761     decimalSeparator : ".",
17762     /**
17763      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17764      */
17765     decimalPrecision : 2,
17766     /**
17767      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17768      */
17769     allowNegative : true,
17770     /**
17771      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17772      */
17773     minValue : Number.NEGATIVE_INFINITY,
17774     /**
17775      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17776      */
17777     maxValue : Number.MAX_VALUE,
17778     /**
17779      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17780      */
17781     minText : "The minimum value for this field is {0}",
17782     /**
17783      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17784      */
17785     maxText : "The maximum value for this field is {0}",
17786     /**
17787      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17788      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17789      */
17790     nanText : "{0} is not a valid number",
17791
17792     // private
17793     initEvents : function(){
17794         Roo.form.NumberField.superclass.initEvents.call(this);
17795         var allowed = "0123456789";
17796         if(this.allowDecimals){
17797             allowed += this.decimalSeparator;
17798         }
17799         if(this.allowNegative){
17800             allowed += "-";
17801         }
17802         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17803         var keyPress = function(e){
17804             var k = e.getKey();
17805             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17806                 return;
17807             }
17808             var c = e.getCharCode();
17809             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17810                 e.stopEvent();
17811             }
17812         };
17813         this.el.on("keypress", keyPress, this);
17814     },
17815
17816     // private
17817     validateValue : function(value){
17818         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17819             return false;
17820         }
17821         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17822              return true;
17823         }
17824         var num = this.parseValue(value);
17825         if(isNaN(num)){
17826             this.markInvalid(String.format(this.nanText, value));
17827             return false;
17828         }
17829         if(num < this.minValue){
17830             this.markInvalid(String.format(this.minText, this.minValue));
17831             return false;
17832         }
17833         if(num > this.maxValue){
17834             this.markInvalid(String.format(this.maxText, this.maxValue));
17835             return false;
17836         }
17837         return true;
17838     },
17839
17840     getValue : function(){
17841         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17842     },
17843
17844     // private
17845     parseValue : function(value){
17846         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17847         return isNaN(value) ? '' : value;
17848     },
17849
17850     // private
17851     fixPrecision : function(value){
17852         var nan = isNaN(value);
17853         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17854             return nan ? '' : value;
17855         }
17856         return parseFloat(value).toFixed(this.decimalPrecision);
17857     },
17858
17859     setValue : function(v){
17860         v = this.fixPrecision(v);
17861         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17862     },
17863
17864     // private
17865     decimalPrecisionFcn : function(v){
17866         return Math.floor(v);
17867     },
17868
17869     beforeBlur : function(){
17870         var v = this.parseValue(this.getRawValue());
17871         if(v){
17872             this.setValue(v);
17873         }
17874     }
17875 });/*
17876  * Based on:
17877  * Ext JS Library 1.1.1
17878  * Copyright(c) 2006-2007, Ext JS, LLC.
17879  *
17880  * Originally Released Under LGPL - original licence link has changed is not relivant.
17881  *
17882  * Fork - LGPL
17883  * <script type="text/javascript">
17884  */
17885  
17886 /**
17887  * @class Roo.form.DateField
17888  * @extends Roo.form.TriggerField
17889  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17890 * @constructor
17891 * Create a new DateField
17892 * @param {Object} config
17893  */
17894 Roo.form.DateField = function(config)
17895 {
17896     Roo.form.DateField.superclass.constructor.call(this, config);
17897     
17898       this.addEvents({
17899          
17900         /**
17901          * @event select
17902          * Fires when a date is selected
17903              * @param {Roo.form.DateField} combo This combo box
17904              * @param {Date} date The date selected
17905              */
17906         'select' : true
17907          
17908     });
17909     
17910     
17911     if(typeof this.minValue == "string") {
17912         this.minValue = this.parseDate(this.minValue);
17913     }
17914     if(typeof this.maxValue == "string") {
17915         this.maxValue = this.parseDate(this.maxValue);
17916     }
17917     this.ddMatch = null;
17918     if(this.disabledDates){
17919         var dd = this.disabledDates;
17920         var re = "(?:";
17921         for(var i = 0; i < dd.length; i++){
17922             re += dd[i];
17923             if(i != dd.length-1) {
17924                 re += "|";
17925             }
17926         }
17927         this.ddMatch = new RegExp(re + ")");
17928     }
17929 };
17930
17931 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17932     /**
17933      * @cfg {String} format
17934      * The default date format string which can be overriden for localization support.  The format must be
17935      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17936      */
17937     format : "m/d/y",
17938     /**
17939      * @cfg {String} altFormats
17940      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17941      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17942      */
17943     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17944     /**
17945      * @cfg {Array} disabledDays
17946      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17947      */
17948     disabledDays : null,
17949     /**
17950      * @cfg {String} disabledDaysText
17951      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17952      */
17953     disabledDaysText : "Disabled",
17954     /**
17955      * @cfg {Array} disabledDates
17956      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17957      * expression so they are very powerful. Some examples:
17958      * <ul>
17959      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17960      * <li>["03/08", "09/16"] would disable those days for every year</li>
17961      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17962      * <li>["03/../2006"] would disable every day in March 2006</li>
17963      * <li>["^03"] would disable every day in every March</li>
17964      * </ul>
17965      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17966      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17967      */
17968     disabledDates : null,
17969     /**
17970      * @cfg {String} disabledDatesText
17971      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17972      */
17973     disabledDatesText : "Disabled",
17974     /**
17975      * @cfg {Date/String} minValue
17976      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17977      * valid format (defaults to null).
17978      */
17979     minValue : null,
17980     /**
17981      * @cfg {Date/String} maxValue
17982      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17983      * valid format (defaults to null).
17984      */
17985     maxValue : null,
17986     /**
17987      * @cfg {String} minText
17988      * The error text to display when the date in the cell is before minValue (defaults to
17989      * 'The date in this field must be after {minValue}').
17990      */
17991     minText : "The date in this field must be equal to or after {0}",
17992     /**
17993      * @cfg {String} maxText
17994      * The error text to display when the date in the cell is after maxValue (defaults to
17995      * 'The date in this field must be before {maxValue}').
17996      */
17997     maxText : "The date in this field must be equal to or before {0}",
17998     /**
17999      * @cfg {String} invalidText
18000      * The error text to display when the date in the field is invalid (defaults to
18001      * '{value} is not a valid date - it must be in the format {format}').
18002      */
18003     invalidText : "{0} is not a valid date - it must be in the format {1}",
18004     /**
18005      * @cfg {String} triggerClass
18006      * An additional CSS class used to style the trigger button.  The trigger will always get the
18007      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18008      * which displays a calendar icon).
18009      */
18010     triggerClass : 'x-form-date-trigger',
18011     
18012
18013     /**
18014      * @cfg {Boolean} useIso
18015      * if enabled, then the date field will use a hidden field to store the 
18016      * real value as iso formated date. default (false)
18017      */ 
18018     useIso : false,
18019     /**
18020      * @cfg {String/Object} autoCreate
18021      * A DomHelper element spec, or true for a default element spec (defaults to
18022      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18023      */ 
18024     // private
18025     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18026     
18027     // private
18028     hiddenField: false,
18029     
18030     onRender : function(ct, position)
18031     {
18032         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18033         if (this.useIso) {
18034             //this.el.dom.removeAttribute('name'); 
18035             Roo.log("Changing name?");
18036             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18037             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18038                     'before', true);
18039             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18040             // prevent input submission
18041             this.hiddenName = this.name;
18042         }
18043             
18044             
18045     },
18046     
18047     // private
18048     validateValue : function(value)
18049     {
18050         value = this.formatDate(value);
18051         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18052             Roo.log('super failed');
18053             return false;
18054         }
18055         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18056              return true;
18057         }
18058         var svalue = value;
18059         value = this.parseDate(value);
18060         if(!value){
18061             Roo.log('parse date failed' + svalue);
18062             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18063             return false;
18064         }
18065         var time = value.getTime();
18066         if(this.minValue && time < this.minValue.getTime()){
18067             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18068             return false;
18069         }
18070         if(this.maxValue && time > this.maxValue.getTime()){
18071             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18072             return false;
18073         }
18074         if(this.disabledDays){
18075             var day = value.getDay();
18076             for(var i = 0; i < this.disabledDays.length; i++) {
18077                 if(day === this.disabledDays[i]){
18078                     this.markInvalid(this.disabledDaysText);
18079                     return false;
18080                 }
18081             }
18082         }
18083         var fvalue = this.formatDate(value);
18084         if(this.ddMatch && this.ddMatch.test(fvalue)){
18085             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18086             return false;
18087         }
18088         return true;
18089     },
18090
18091     // private
18092     // Provides logic to override the default TriggerField.validateBlur which just returns true
18093     validateBlur : function(){
18094         return !this.menu || !this.menu.isVisible();
18095     },
18096     
18097     getName: function()
18098     {
18099         // returns hidden if it's set..
18100         if (!this.rendered) {return ''};
18101         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18102         
18103     },
18104
18105     /**
18106      * Returns the current date value of the date field.
18107      * @return {Date} The date value
18108      */
18109     getValue : function(){
18110         
18111         return  this.hiddenField ?
18112                 this.hiddenField.value :
18113                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18114     },
18115
18116     /**
18117      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18118      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18119      * (the default format used is "m/d/y").
18120      * <br />Usage:
18121      * <pre><code>
18122 //All of these calls set the same date value (May 4, 2006)
18123
18124 //Pass a date object:
18125 var dt = new Date('5/4/06');
18126 dateField.setValue(dt);
18127
18128 //Pass a date string (default format):
18129 dateField.setValue('5/4/06');
18130
18131 //Pass a date string (custom format):
18132 dateField.format = 'Y-m-d';
18133 dateField.setValue('2006-5-4');
18134 </code></pre>
18135      * @param {String/Date} date The date or valid date string
18136      */
18137     setValue : function(date){
18138         if (this.hiddenField) {
18139             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18140         }
18141         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18142         // make sure the value field is always stored as a date..
18143         this.value = this.parseDate(date);
18144         
18145         
18146     },
18147
18148     // private
18149     parseDate : function(value){
18150         if(!value || value instanceof Date){
18151             return value;
18152         }
18153         var v = Date.parseDate(value, this.format);
18154          if (!v && this.useIso) {
18155             v = Date.parseDate(value, 'Y-m-d');
18156         }
18157         if(!v && this.altFormats){
18158             if(!this.altFormatsArray){
18159                 this.altFormatsArray = this.altFormats.split("|");
18160             }
18161             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18162                 v = Date.parseDate(value, this.altFormatsArray[i]);
18163             }
18164         }
18165         return v;
18166     },
18167
18168     // private
18169     formatDate : function(date, fmt){
18170         return (!date || !(date instanceof Date)) ?
18171                date : date.dateFormat(fmt || this.format);
18172     },
18173
18174     // private
18175     menuListeners : {
18176         select: function(m, d){
18177             
18178             this.setValue(d);
18179             this.fireEvent('select', this, d);
18180         },
18181         show : function(){ // retain focus styling
18182             this.onFocus();
18183         },
18184         hide : function(){
18185             this.focus.defer(10, this);
18186             var ml = this.menuListeners;
18187             this.menu.un("select", ml.select,  this);
18188             this.menu.un("show", ml.show,  this);
18189             this.menu.un("hide", ml.hide,  this);
18190         }
18191     },
18192
18193     // private
18194     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18195     onTriggerClick : function(){
18196         if(this.disabled){
18197             return;
18198         }
18199         if(this.menu == null){
18200             this.menu = new Roo.menu.DateMenu();
18201         }
18202         Roo.apply(this.menu.picker,  {
18203             showClear: this.allowBlank,
18204             minDate : this.minValue,
18205             maxDate : this.maxValue,
18206             disabledDatesRE : this.ddMatch,
18207             disabledDatesText : this.disabledDatesText,
18208             disabledDays : this.disabledDays,
18209             disabledDaysText : this.disabledDaysText,
18210             format : this.useIso ? 'Y-m-d' : this.format,
18211             minText : String.format(this.minText, this.formatDate(this.minValue)),
18212             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18213         });
18214         this.menu.on(Roo.apply({}, this.menuListeners, {
18215             scope:this
18216         }));
18217         this.menu.picker.setValue(this.getValue() || new Date());
18218         this.menu.show(this.el, "tl-bl?");
18219     },
18220
18221     beforeBlur : function(){
18222         var v = this.parseDate(this.getRawValue());
18223         if(v){
18224             this.setValue(v);
18225         }
18226     },
18227
18228     /*@
18229      * overide
18230      * 
18231      */
18232     isDirty : function() {
18233         if(this.disabled) {
18234             return false;
18235         }
18236         
18237         if(typeof(this.startValue) === 'undefined'){
18238             return false;
18239         }
18240         
18241         return String(this.getValue()) !== String(this.startValue);
18242         
18243     },
18244     // @overide
18245     cleanLeadingSpace : function(e)
18246     {
18247        return;
18248     }
18249     
18250 });/*
18251  * Based on:
18252  * Ext JS Library 1.1.1
18253  * Copyright(c) 2006-2007, Ext JS, LLC.
18254  *
18255  * Originally Released Under LGPL - original licence link has changed is not relivant.
18256  *
18257  * Fork - LGPL
18258  * <script type="text/javascript">
18259  */
18260  
18261 /**
18262  * @class Roo.form.MonthField
18263  * @extends Roo.form.TriggerField
18264  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18265 * @constructor
18266 * Create a new MonthField
18267 * @param {Object} config
18268  */
18269 Roo.form.MonthField = function(config){
18270     
18271     Roo.form.MonthField.superclass.constructor.call(this, config);
18272     
18273       this.addEvents({
18274          
18275         /**
18276          * @event select
18277          * Fires when a date is selected
18278              * @param {Roo.form.MonthFieeld} combo This combo box
18279              * @param {Date} date The date selected
18280              */
18281         'select' : true
18282          
18283     });
18284     
18285     
18286     if(typeof this.minValue == "string") {
18287         this.minValue = this.parseDate(this.minValue);
18288     }
18289     if(typeof this.maxValue == "string") {
18290         this.maxValue = this.parseDate(this.maxValue);
18291     }
18292     this.ddMatch = null;
18293     if(this.disabledDates){
18294         var dd = this.disabledDates;
18295         var re = "(?:";
18296         for(var i = 0; i < dd.length; i++){
18297             re += dd[i];
18298             if(i != dd.length-1) {
18299                 re += "|";
18300             }
18301         }
18302         this.ddMatch = new RegExp(re + ")");
18303     }
18304 };
18305
18306 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18307     /**
18308      * @cfg {String} format
18309      * The default date format string which can be overriden for localization support.  The format must be
18310      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18311      */
18312     format : "M Y",
18313     /**
18314      * @cfg {String} altFormats
18315      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18316      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18317      */
18318     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18319     /**
18320      * @cfg {Array} disabledDays
18321      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18322      */
18323     disabledDays : [0,1,2,3,4,5,6],
18324     /**
18325      * @cfg {String} disabledDaysText
18326      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18327      */
18328     disabledDaysText : "Disabled",
18329     /**
18330      * @cfg {Array} disabledDates
18331      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18332      * expression so they are very powerful. Some examples:
18333      * <ul>
18334      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18335      * <li>["03/08", "09/16"] would disable those days for every year</li>
18336      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18337      * <li>["03/../2006"] would disable every day in March 2006</li>
18338      * <li>["^03"] would disable every day in every March</li>
18339      * </ul>
18340      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18341      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18342      */
18343     disabledDates : null,
18344     /**
18345      * @cfg {String} disabledDatesText
18346      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18347      */
18348     disabledDatesText : "Disabled",
18349     /**
18350      * @cfg {Date/String} minValue
18351      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18352      * valid format (defaults to null).
18353      */
18354     minValue : null,
18355     /**
18356      * @cfg {Date/String} maxValue
18357      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18358      * valid format (defaults to null).
18359      */
18360     maxValue : null,
18361     /**
18362      * @cfg {String} minText
18363      * The error text to display when the date in the cell is before minValue (defaults to
18364      * 'The date in this field must be after {minValue}').
18365      */
18366     minText : "The date in this field must be equal to or after {0}",
18367     /**
18368      * @cfg {String} maxTextf
18369      * The error text to display when the date in the cell is after maxValue (defaults to
18370      * 'The date in this field must be before {maxValue}').
18371      */
18372     maxText : "The date in this field must be equal to or before {0}",
18373     /**
18374      * @cfg {String} invalidText
18375      * The error text to display when the date in the field is invalid (defaults to
18376      * '{value} is not a valid date - it must be in the format {format}').
18377      */
18378     invalidText : "{0} is not a valid date - it must be in the format {1}",
18379     /**
18380      * @cfg {String} triggerClass
18381      * An additional CSS class used to style the trigger button.  The trigger will always get the
18382      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18383      * which displays a calendar icon).
18384      */
18385     triggerClass : 'x-form-date-trigger',
18386     
18387
18388     /**
18389      * @cfg {Boolean} useIso
18390      * if enabled, then the date field will use a hidden field to store the 
18391      * real value as iso formated date. default (true)
18392      */ 
18393     useIso : true,
18394     /**
18395      * @cfg {String/Object} autoCreate
18396      * A DomHelper element spec, or true for a default element spec (defaults to
18397      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18398      */ 
18399     // private
18400     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18401     
18402     // private
18403     hiddenField: false,
18404     
18405     hideMonthPicker : false,
18406     
18407     onRender : function(ct, position)
18408     {
18409         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18410         if (this.useIso) {
18411             this.el.dom.removeAttribute('name'); 
18412             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18413                     'before', true);
18414             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18415             // prevent input submission
18416             this.hiddenName = this.name;
18417         }
18418             
18419             
18420     },
18421     
18422     // private
18423     validateValue : function(value)
18424     {
18425         value = this.formatDate(value);
18426         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18427             return false;
18428         }
18429         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18430              return true;
18431         }
18432         var svalue = value;
18433         value = this.parseDate(value);
18434         if(!value){
18435             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18436             return false;
18437         }
18438         var time = value.getTime();
18439         if(this.minValue && time < this.minValue.getTime()){
18440             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18441             return false;
18442         }
18443         if(this.maxValue && time > this.maxValue.getTime()){
18444             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18445             return false;
18446         }
18447         /*if(this.disabledDays){
18448             var day = value.getDay();
18449             for(var i = 0; i < this.disabledDays.length; i++) {
18450                 if(day === this.disabledDays[i]){
18451                     this.markInvalid(this.disabledDaysText);
18452                     return false;
18453                 }
18454             }
18455         }
18456         */
18457         var fvalue = this.formatDate(value);
18458         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18459             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18460             return false;
18461         }
18462         */
18463         return true;
18464     },
18465
18466     // private
18467     // Provides logic to override the default TriggerField.validateBlur which just returns true
18468     validateBlur : function(){
18469         return !this.menu || !this.menu.isVisible();
18470     },
18471
18472     /**
18473      * Returns the current date value of the date field.
18474      * @return {Date} The date value
18475      */
18476     getValue : function(){
18477         
18478         
18479         
18480         return  this.hiddenField ?
18481                 this.hiddenField.value :
18482                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18483     },
18484
18485     /**
18486      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18487      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18488      * (the default format used is "m/d/y").
18489      * <br />Usage:
18490      * <pre><code>
18491 //All of these calls set the same date value (May 4, 2006)
18492
18493 //Pass a date object:
18494 var dt = new Date('5/4/06');
18495 monthField.setValue(dt);
18496
18497 //Pass a date string (default format):
18498 monthField.setValue('5/4/06');
18499
18500 //Pass a date string (custom format):
18501 monthField.format = 'Y-m-d';
18502 monthField.setValue('2006-5-4');
18503 </code></pre>
18504      * @param {String/Date} date The date or valid date string
18505      */
18506     setValue : function(date){
18507         Roo.log('month setValue' + date);
18508         // can only be first of month..
18509         
18510         var val = this.parseDate(date);
18511         
18512         if (this.hiddenField) {
18513             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18514         }
18515         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18516         this.value = this.parseDate(date);
18517     },
18518
18519     // private
18520     parseDate : function(value){
18521         if(!value || value instanceof Date){
18522             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18523             return value;
18524         }
18525         var v = Date.parseDate(value, this.format);
18526         if (!v && this.useIso) {
18527             v = Date.parseDate(value, 'Y-m-d');
18528         }
18529         if (v) {
18530             // 
18531             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18532         }
18533         
18534         
18535         if(!v && this.altFormats){
18536             if(!this.altFormatsArray){
18537                 this.altFormatsArray = this.altFormats.split("|");
18538             }
18539             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18540                 v = Date.parseDate(value, this.altFormatsArray[i]);
18541             }
18542         }
18543         return v;
18544     },
18545
18546     // private
18547     formatDate : function(date, fmt){
18548         return (!date || !(date instanceof Date)) ?
18549                date : date.dateFormat(fmt || this.format);
18550     },
18551
18552     // private
18553     menuListeners : {
18554         select: function(m, d){
18555             this.setValue(d);
18556             this.fireEvent('select', this, d);
18557         },
18558         show : function(){ // retain focus styling
18559             this.onFocus();
18560         },
18561         hide : function(){
18562             this.focus.defer(10, this);
18563             var ml = this.menuListeners;
18564             this.menu.un("select", ml.select,  this);
18565             this.menu.un("show", ml.show,  this);
18566             this.menu.un("hide", ml.hide,  this);
18567         }
18568     },
18569     // private
18570     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18571     onTriggerClick : function(){
18572         if(this.disabled){
18573             return;
18574         }
18575         if(this.menu == null){
18576             this.menu = new Roo.menu.DateMenu();
18577            
18578         }
18579         
18580         Roo.apply(this.menu.picker,  {
18581             
18582             showClear: this.allowBlank,
18583             minDate : this.minValue,
18584             maxDate : this.maxValue,
18585             disabledDatesRE : this.ddMatch,
18586             disabledDatesText : this.disabledDatesText,
18587             
18588             format : this.useIso ? 'Y-m-d' : this.format,
18589             minText : String.format(this.minText, this.formatDate(this.minValue)),
18590             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18591             
18592         });
18593          this.menu.on(Roo.apply({}, this.menuListeners, {
18594             scope:this
18595         }));
18596        
18597         
18598         var m = this.menu;
18599         var p = m.picker;
18600         
18601         // hide month picker get's called when we called by 'before hide';
18602         
18603         var ignorehide = true;
18604         p.hideMonthPicker  = function(disableAnim){
18605             if (ignorehide) {
18606                 return;
18607             }
18608              if(this.monthPicker){
18609                 Roo.log("hideMonthPicker called");
18610                 if(disableAnim === true){
18611                     this.monthPicker.hide();
18612                 }else{
18613                     this.monthPicker.slideOut('t', {duration:.2});
18614                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18615                     p.fireEvent("select", this, this.value);
18616                     m.hide();
18617                 }
18618             }
18619         }
18620         
18621         Roo.log('picker set value');
18622         Roo.log(this.getValue());
18623         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18624         m.show(this.el, 'tl-bl?');
18625         ignorehide  = false;
18626         // this will trigger hideMonthPicker..
18627         
18628         
18629         // hidden the day picker
18630         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18631         
18632         
18633         
18634       
18635         
18636         p.showMonthPicker.defer(100, p);
18637     
18638         
18639        
18640     },
18641
18642     beforeBlur : function(){
18643         var v = this.parseDate(this.getRawValue());
18644         if(v){
18645             this.setValue(v);
18646         }
18647     }
18648
18649     /** @cfg {Boolean} grow @hide */
18650     /** @cfg {Number} growMin @hide */
18651     /** @cfg {Number} growMax @hide */
18652     /**
18653      * @hide
18654      * @method autoSize
18655      */
18656 });/*
18657  * Based on:
18658  * Ext JS Library 1.1.1
18659  * Copyright(c) 2006-2007, Ext JS, LLC.
18660  *
18661  * Originally Released Under LGPL - original licence link has changed is not relivant.
18662  *
18663  * Fork - LGPL
18664  * <script type="text/javascript">
18665  */
18666  
18667
18668 /**
18669  * @class Roo.form.ComboBox
18670  * @extends Roo.form.TriggerField
18671  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18672  * @constructor
18673  * Create a new ComboBox.
18674  * @param {Object} config Configuration options
18675  */
18676 Roo.form.ComboBox = function(config){
18677     Roo.form.ComboBox.superclass.constructor.call(this, config);
18678     this.addEvents({
18679         /**
18680          * @event expand
18681          * Fires when the dropdown list is expanded
18682              * @param {Roo.form.ComboBox} combo This combo box
18683              */
18684         'expand' : true,
18685         /**
18686          * @event collapse
18687          * Fires when the dropdown list is collapsed
18688              * @param {Roo.form.ComboBox} combo This combo box
18689              */
18690         'collapse' : true,
18691         /**
18692          * @event beforeselect
18693          * Fires before a list item is selected. Return false to cancel the selection.
18694              * @param {Roo.form.ComboBox} combo This combo box
18695              * @param {Roo.data.Record} record The data record returned from the underlying store
18696              * @param {Number} index The index of the selected item in the dropdown list
18697              */
18698         'beforeselect' : true,
18699         /**
18700          * @event select
18701          * Fires when a list item is selected
18702              * @param {Roo.form.ComboBox} combo This combo box
18703              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18704              * @param {Number} index The index of the selected item in the dropdown list
18705              */
18706         'select' : true,
18707         /**
18708          * @event beforequery
18709          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18710          * The event object passed has these properties:
18711              * @param {Roo.form.ComboBox} combo This combo box
18712              * @param {String} query The query
18713              * @param {Boolean} forceAll true to force "all" query
18714              * @param {Boolean} cancel true to cancel the query
18715              * @param {Object} e The query event object
18716              */
18717         'beforequery': true,
18718          /**
18719          * @event add
18720          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18721              * @param {Roo.form.ComboBox} combo This combo box
18722              */
18723         'add' : true,
18724         /**
18725          * @event edit
18726          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18727              * @param {Roo.form.ComboBox} combo This combo box
18728              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18729              */
18730         'edit' : true
18731         
18732         
18733     });
18734     if(this.transform){
18735         this.allowDomMove = false;
18736         var s = Roo.getDom(this.transform);
18737         if(!this.hiddenName){
18738             this.hiddenName = s.name;
18739         }
18740         if(!this.store){
18741             this.mode = 'local';
18742             var d = [], opts = s.options;
18743             for(var i = 0, len = opts.length;i < len; i++){
18744                 var o = opts[i];
18745                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18746                 if(o.selected) {
18747                     this.value = value;
18748                 }
18749                 d.push([value, o.text]);
18750             }
18751             this.store = new Roo.data.SimpleStore({
18752                 'id': 0,
18753                 fields: ['value', 'text'],
18754                 data : d
18755             });
18756             this.valueField = 'value';
18757             this.displayField = 'text';
18758         }
18759         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18760         if(!this.lazyRender){
18761             this.target = true;
18762             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18763             s.parentNode.removeChild(s); // remove it
18764             this.render(this.el.parentNode);
18765         }else{
18766             s.parentNode.removeChild(s); // remove it
18767         }
18768
18769     }
18770     if (this.store) {
18771         this.store = Roo.factory(this.store, Roo.data);
18772     }
18773     
18774     this.selectedIndex = -1;
18775     if(this.mode == 'local'){
18776         if(config.queryDelay === undefined){
18777             this.queryDelay = 10;
18778         }
18779         if(config.minChars === undefined){
18780             this.minChars = 0;
18781         }
18782     }
18783 };
18784
18785 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18786     /**
18787      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18788      */
18789     /**
18790      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18791      * rendering into an Roo.Editor, defaults to false)
18792      */
18793     /**
18794      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18795      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18796      */
18797     /**
18798      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18799      */
18800     /**
18801      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18802      * the dropdown list (defaults to undefined, with no header element)
18803      */
18804
18805      /**
18806      * @cfg {String/Roo.Template} tpl The template to use to render the output
18807      */
18808      
18809     // private
18810     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18811     /**
18812      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18813      */
18814     listWidth: undefined,
18815     /**
18816      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18817      * mode = 'remote' or 'text' if mode = 'local')
18818      */
18819     displayField: undefined,
18820     /**
18821      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18822      * mode = 'remote' or 'value' if mode = 'local'). 
18823      * Note: use of a valueField requires the user make a selection
18824      * in order for a value to be mapped.
18825      */
18826     valueField: undefined,
18827     
18828     
18829     /**
18830      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18831      * field's data value (defaults to the underlying DOM element's name)
18832      */
18833     hiddenName: undefined,
18834     /**
18835      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18836      */
18837     listClass: '',
18838     /**
18839      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18840      */
18841     selectedClass: 'x-combo-selected',
18842     /**
18843      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18844      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18845      * which displays a downward arrow icon).
18846      */
18847     triggerClass : 'x-form-arrow-trigger',
18848     /**
18849      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18850      */
18851     shadow:'sides',
18852     /**
18853      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18854      * anchor positions (defaults to 'tl-bl')
18855      */
18856     listAlign: 'tl-bl?',
18857     /**
18858      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18859      */
18860     maxHeight: 300,
18861     /**
18862      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18863      * query specified by the allQuery config option (defaults to 'query')
18864      */
18865     triggerAction: 'query',
18866     /**
18867      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18868      * (defaults to 4, does not apply if editable = false)
18869      */
18870     minChars : 4,
18871     /**
18872      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18873      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18874      */
18875     typeAhead: false,
18876     /**
18877      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18878      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18879      */
18880     queryDelay: 500,
18881     /**
18882      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18883      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18884      */
18885     pageSize: 0,
18886     /**
18887      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18888      * when editable = true (defaults to false)
18889      */
18890     selectOnFocus:false,
18891     /**
18892      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18893      */
18894     queryParam: 'query',
18895     /**
18896      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18897      * when mode = 'remote' (defaults to 'Loading...')
18898      */
18899     loadingText: 'Loading...',
18900     /**
18901      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18902      */
18903     resizable: false,
18904     /**
18905      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18906      */
18907     handleHeight : 8,
18908     /**
18909      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18910      * traditional select (defaults to true)
18911      */
18912     editable: true,
18913     /**
18914      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18915      */
18916     allQuery: '',
18917     /**
18918      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18919      */
18920     mode: 'remote',
18921     /**
18922      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18923      * listWidth has a higher value)
18924      */
18925     minListWidth : 70,
18926     /**
18927      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18928      * allow the user to set arbitrary text into the field (defaults to false)
18929      */
18930     forceSelection:false,
18931     /**
18932      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18933      * if typeAhead = true (defaults to 250)
18934      */
18935     typeAheadDelay : 250,
18936     /**
18937      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18938      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18939      */
18940     valueNotFoundText : undefined,
18941     /**
18942      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18943      */
18944     blockFocus : false,
18945     
18946     /**
18947      * @cfg {Boolean} disableClear Disable showing of clear button.
18948      */
18949     disableClear : false,
18950     /**
18951      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18952      */
18953     alwaysQuery : false,
18954     
18955     //private
18956     addicon : false,
18957     editicon: false,
18958     
18959     // element that contains real text value.. (when hidden is used..)
18960      
18961     // private
18962     onRender : function(ct, position)
18963     {
18964         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18965         
18966         if(this.hiddenName){
18967             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18968                     'before', true);
18969             this.hiddenField.value =
18970                 this.hiddenValue !== undefined ? this.hiddenValue :
18971                 this.value !== undefined ? this.value : '';
18972
18973             // prevent input submission
18974             this.el.dom.removeAttribute('name');
18975              
18976              
18977         }
18978         
18979         if(Roo.isGecko){
18980             this.el.dom.setAttribute('autocomplete', 'off');
18981         }
18982
18983         var cls = 'x-combo-list';
18984
18985         this.list = new Roo.Layer({
18986             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18987         });
18988
18989         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18990         this.list.setWidth(lw);
18991         this.list.swallowEvent('mousewheel');
18992         this.assetHeight = 0;
18993
18994         if(this.title){
18995             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18996             this.assetHeight += this.header.getHeight();
18997         }
18998
18999         this.innerList = this.list.createChild({cls:cls+'-inner'});
19000         this.innerList.on('mouseover', this.onViewOver, this);
19001         this.innerList.on('mousemove', this.onViewMove, this);
19002         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19003         
19004         if(this.allowBlank && !this.pageSize && !this.disableClear){
19005             this.footer = this.list.createChild({cls:cls+'-ft'});
19006             this.pageTb = new Roo.Toolbar(this.footer);
19007            
19008         }
19009         if(this.pageSize){
19010             this.footer = this.list.createChild({cls:cls+'-ft'});
19011             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
19012                     {pageSize: this.pageSize});
19013             
19014         }
19015         
19016         if (this.pageTb && this.allowBlank && !this.disableClear) {
19017             var _this = this;
19018             this.pageTb.add(new Roo.Toolbar.Fill(), {
19019                 cls: 'x-btn-icon x-btn-clear',
19020                 text: '&#160;',
19021                 handler: function()
19022                 {
19023                     _this.collapse();
19024                     _this.clearValue();
19025                     _this.onSelect(false, -1);
19026                 }
19027             });
19028         }
19029         if (this.footer) {
19030             this.assetHeight += this.footer.getHeight();
19031         }
19032         
19033
19034         if(!this.tpl){
19035             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19036         }
19037
19038         this.view = new Roo.View(this.innerList, this.tpl, {
19039             singleSelect:true,
19040             store: this.store,
19041             selectedClass: this.selectedClass
19042         });
19043
19044         this.view.on('click', this.onViewClick, this);
19045
19046         this.store.on('beforeload', this.onBeforeLoad, this);
19047         this.store.on('load', this.onLoad, this);
19048         this.store.on('loadexception', this.onLoadException, this);
19049
19050         if(this.resizable){
19051             this.resizer = new Roo.Resizable(this.list,  {
19052                pinned:true, handles:'se'
19053             });
19054             this.resizer.on('resize', function(r, w, h){
19055                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19056                 this.listWidth = w;
19057                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19058                 this.restrictHeight();
19059             }, this);
19060             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19061         }
19062         if(!this.editable){
19063             this.editable = true;
19064             this.setEditable(false);
19065         }  
19066         
19067         
19068         if (typeof(this.events.add.listeners) != 'undefined') {
19069             
19070             this.addicon = this.wrap.createChild(
19071                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19072        
19073             this.addicon.on('click', function(e) {
19074                 this.fireEvent('add', this);
19075             }, this);
19076         }
19077         if (typeof(this.events.edit.listeners) != 'undefined') {
19078             
19079             this.editicon = this.wrap.createChild(
19080                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19081             if (this.addicon) {
19082                 this.editicon.setStyle('margin-left', '40px');
19083             }
19084             this.editicon.on('click', function(e) {
19085                 
19086                 // we fire even  if inothing is selected..
19087                 this.fireEvent('edit', this, this.lastData );
19088                 
19089             }, this);
19090         }
19091         
19092         
19093         
19094     },
19095
19096     // private
19097     initEvents : function(){
19098         Roo.form.ComboBox.superclass.initEvents.call(this);
19099
19100         this.keyNav = new Roo.KeyNav(this.el, {
19101             "up" : function(e){
19102                 this.inKeyMode = true;
19103                 this.selectPrev();
19104             },
19105
19106             "down" : function(e){
19107                 if(!this.isExpanded()){
19108                     this.onTriggerClick();
19109                 }else{
19110                     this.inKeyMode = true;
19111                     this.selectNext();
19112                 }
19113             },
19114
19115             "enter" : function(e){
19116                 this.onViewClick();
19117                 //return true;
19118             },
19119
19120             "esc" : function(e){
19121                 this.collapse();
19122             },
19123
19124             "tab" : function(e){
19125                 this.onViewClick(false);
19126                 this.fireEvent("specialkey", this, e);
19127                 return true;
19128             },
19129
19130             scope : this,
19131
19132             doRelay : function(foo, bar, hname){
19133                 if(hname == 'down' || this.scope.isExpanded()){
19134                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19135                 }
19136                 return true;
19137             },
19138
19139             forceKeyDown: true
19140         });
19141         this.queryDelay = Math.max(this.queryDelay || 10,
19142                 this.mode == 'local' ? 10 : 250);
19143         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19144         if(this.typeAhead){
19145             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19146         }
19147         if(this.editable !== false){
19148             this.el.on("keyup", this.onKeyUp, this);
19149         }
19150         if(this.forceSelection){
19151             this.on('blur', this.doForce, this);
19152         }
19153     },
19154
19155     onDestroy : function(){
19156         if(this.view){
19157             this.view.setStore(null);
19158             this.view.el.removeAllListeners();
19159             this.view.el.remove();
19160             this.view.purgeListeners();
19161         }
19162         if(this.list){
19163             this.list.destroy();
19164         }
19165         if(this.store){
19166             this.store.un('beforeload', this.onBeforeLoad, this);
19167             this.store.un('load', this.onLoad, this);
19168             this.store.un('loadexception', this.onLoadException, this);
19169         }
19170         Roo.form.ComboBox.superclass.onDestroy.call(this);
19171     },
19172
19173     // private
19174     fireKey : function(e){
19175         if(e.isNavKeyPress() && !this.list.isVisible()){
19176             this.fireEvent("specialkey", this, e);
19177         }
19178     },
19179
19180     // private
19181     onResize: function(w, h){
19182         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19183         
19184         if(typeof w != 'number'){
19185             // we do not handle it!?!?
19186             return;
19187         }
19188         var tw = this.trigger.getWidth();
19189         tw += this.addicon ? this.addicon.getWidth() : 0;
19190         tw += this.editicon ? this.editicon.getWidth() : 0;
19191         var x = w - tw;
19192         this.el.setWidth( this.adjustWidth('input', x));
19193             
19194         this.trigger.setStyle('left', x+'px');
19195         
19196         if(this.list && this.listWidth === undefined){
19197             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19198             this.list.setWidth(lw);
19199             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19200         }
19201         
19202     
19203         
19204     },
19205
19206     /**
19207      * Allow or prevent the user from directly editing the field text.  If false is passed,
19208      * the user will only be able to select from the items defined in the dropdown list.  This method
19209      * is the runtime equivalent of setting the 'editable' config option at config time.
19210      * @param {Boolean} value True to allow the user to directly edit the field text
19211      */
19212     setEditable : function(value){
19213         if(value == this.editable){
19214             return;
19215         }
19216         this.editable = value;
19217         if(!value){
19218             this.el.dom.setAttribute('readOnly', true);
19219             this.el.on('mousedown', this.onTriggerClick,  this);
19220             this.el.addClass('x-combo-noedit');
19221         }else{
19222             this.el.dom.setAttribute('readOnly', false);
19223             this.el.un('mousedown', this.onTriggerClick,  this);
19224             this.el.removeClass('x-combo-noedit');
19225         }
19226     },
19227
19228     // private
19229     onBeforeLoad : function(){
19230         if(!this.hasFocus){
19231             return;
19232         }
19233         this.innerList.update(this.loadingText ?
19234                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19235         this.restrictHeight();
19236         this.selectedIndex = -1;
19237     },
19238
19239     // private
19240     onLoad : function(){
19241         if(!this.hasFocus){
19242             return;
19243         }
19244         if(this.store.getCount() > 0){
19245             this.expand();
19246             this.restrictHeight();
19247             if(this.lastQuery == this.allQuery){
19248                 if(this.editable){
19249                     this.el.dom.select();
19250                 }
19251                 if(!this.selectByValue(this.value, true)){
19252                     this.select(0, true);
19253                 }
19254             }else{
19255                 this.selectNext();
19256                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19257                     this.taTask.delay(this.typeAheadDelay);
19258                 }
19259             }
19260         }else{
19261             this.onEmptyResults();
19262         }
19263         //this.el.focus();
19264     },
19265     // private
19266     onLoadException : function()
19267     {
19268         this.collapse();
19269         Roo.log(this.store.reader.jsonData);
19270         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19271             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19272         }
19273         
19274         
19275     },
19276     // private
19277     onTypeAhead : function(){
19278         if(this.store.getCount() > 0){
19279             var r = this.store.getAt(0);
19280             var newValue = r.data[this.displayField];
19281             var len = newValue.length;
19282             var selStart = this.getRawValue().length;
19283             if(selStart != len){
19284                 this.setRawValue(newValue);
19285                 this.selectText(selStart, newValue.length);
19286             }
19287         }
19288     },
19289
19290     // private
19291     onSelect : function(record, index){
19292         if(this.fireEvent('beforeselect', this, record, index) !== false){
19293             this.setFromData(index > -1 ? record.data : false);
19294             this.collapse();
19295             this.fireEvent('select', this, record, index);
19296         }
19297     },
19298
19299     /**
19300      * Returns the currently selected field value or empty string if no value is set.
19301      * @return {String} value The selected value
19302      */
19303     getValue : function(){
19304         if(this.valueField){
19305             return typeof this.value != 'undefined' ? this.value : '';
19306         }
19307         return Roo.form.ComboBox.superclass.getValue.call(this);
19308     },
19309
19310     /**
19311      * Clears any text/value currently set in the field
19312      */
19313     clearValue : function(){
19314         if(this.hiddenField){
19315             this.hiddenField.value = '';
19316         }
19317         this.value = '';
19318         this.setRawValue('');
19319         this.lastSelectionText = '';
19320         
19321     },
19322
19323     /**
19324      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19325      * will be displayed in the field.  If the value does not match the data value of an existing item,
19326      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19327      * Otherwise the field will be blank (although the value will still be set).
19328      * @param {String} value The value to match
19329      */
19330     setValue : function(v){
19331         var text = v;
19332         if(this.valueField){
19333             var r = this.findRecord(this.valueField, v);
19334             if(r){
19335                 text = r.data[this.displayField];
19336             }else if(this.valueNotFoundText !== undefined){
19337                 text = this.valueNotFoundText;
19338             }
19339         }
19340         this.lastSelectionText = text;
19341         if(this.hiddenField){
19342             this.hiddenField.value = v;
19343         }
19344         Roo.form.ComboBox.superclass.setValue.call(this, text);
19345         this.value = v;
19346     },
19347     /**
19348      * @property {Object} the last set data for the element
19349      */
19350     
19351     lastData : false,
19352     /**
19353      * Sets the value of the field based on a object which is related to the record format for the store.
19354      * @param {Object} value the value to set as. or false on reset?
19355      */
19356     setFromData : function(o){
19357         var dv = ''; // display value
19358         var vv = ''; // value value..
19359         this.lastData = o;
19360         if (this.displayField) {
19361             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19362         } else {
19363             // this is an error condition!!!
19364             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19365         }
19366         
19367         if(this.valueField){
19368             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19369         }
19370         if(this.hiddenField){
19371             this.hiddenField.value = vv;
19372             
19373             this.lastSelectionText = dv;
19374             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19375             this.value = vv;
19376             return;
19377         }
19378         // no hidden field.. - we store the value in 'value', but still display
19379         // display field!!!!
19380         this.lastSelectionText = dv;
19381         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19382         this.value = vv;
19383         
19384         
19385     },
19386     // private
19387     reset : function(){
19388         // overridden so that last data is reset..
19389         this.setValue(this.resetValue);
19390         this.originalValue = this.getValue();
19391         this.clearInvalid();
19392         this.lastData = false;
19393         if (this.view) {
19394             this.view.clearSelections();
19395         }
19396     },
19397     // private
19398     findRecord : function(prop, value){
19399         var record;
19400         if(this.store.getCount() > 0){
19401             this.store.each(function(r){
19402                 if(r.data[prop] == value){
19403                     record = r;
19404                     return false;
19405                 }
19406                 return true;
19407             });
19408         }
19409         return record;
19410     },
19411     
19412     getName: function()
19413     {
19414         // returns hidden if it's set..
19415         if (!this.rendered) {return ''};
19416         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19417         
19418     },
19419     // private
19420     onViewMove : function(e, t){
19421         this.inKeyMode = false;
19422     },
19423
19424     // private
19425     onViewOver : function(e, t){
19426         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19427             return;
19428         }
19429         var item = this.view.findItemFromChild(t);
19430         if(item){
19431             var index = this.view.indexOf(item);
19432             this.select(index, false);
19433         }
19434     },
19435
19436     // private
19437     onViewClick : function(doFocus)
19438     {
19439         var index = this.view.getSelectedIndexes()[0];
19440         var r = this.store.getAt(index);
19441         if(r){
19442             this.onSelect(r, index);
19443         }
19444         if(doFocus !== false && !this.blockFocus){
19445             this.el.focus();
19446         }
19447     },
19448
19449     // private
19450     restrictHeight : function(){
19451         this.innerList.dom.style.height = '';
19452         var inner = this.innerList.dom;
19453         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19454         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19455         this.list.beginUpdate();
19456         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19457         this.list.alignTo(this.el, this.listAlign);
19458         this.list.endUpdate();
19459     },
19460
19461     // private
19462     onEmptyResults : function(){
19463         this.collapse();
19464     },
19465
19466     /**
19467      * Returns true if the dropdown list is expanded, else false.
19468      */
19469     isExpanded : function(){
19470         return this.list.isVisible();
19471     },
19472
19473     /**
19474      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19475      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19476      * @param {String} value The data value of the item to select
19477      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19478      * selected item if it is not currently in view (defaults to true)
19479      * @return {Boolean} True if the value matched an item in the list, else false
19480      */
19481     selectByValue : function(v, scrollIntoView){
19482         if(v !== undefined && v !== null){
19483             var r = this.findRecord(this.valueField || this.displayField, v);
19484             if(r){
19485                 this.select(this.store.indexOf(r), scrollIntoView);
19486                 return true;
19487             }
19488         }
19489         return false;
19490     },
19491
19492     /**
19493      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19494      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19495      * @param {Number} index The zero-based index of the list item to select
19496      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19497      * selected item if it is not currently in view (defaults to true)
19498      */
19499     select : function(index, scrollIntoView){
19500         this.selectedIndex = index;
19501         this.view.select(index);
19502         if(scrollIntoView !== false){
19503             var el = this.view.getNode(index);
19504             if(el){
19505                 this.innerList.scrollChildIntoView(el, false);
19506             }
19507         }
19508     },
19509
19510     // private
19511     selectNext : function(){
19512         var ct = this.store.getCount();
19513         if(ct > 0){
19514             if(this.selectedIndex == -1){
19515                 this.select(0);
19516             }else if(this.selectedIndex < ct-1){
19517                 this.select(this.selectedIndex+1);
19518             }
19519         }
19520     },
19521
19522     // private
19523     selectPrev : function(){
19524         var ct = this.store.getCount();
19525         if(ct > 0){
19526             if(this.selectedIndex == -1){
19527                 this.select(0);
19528             }else if(this.selectedIndex != 0){
19529                 this.select(this.selectedIndex-1);
19530             }
19531         }
19532     },
19533
19534     // private
19535     onKeyUp : function(e){
19536         if(this.editable !== false && !e.isSpecialKey()){
19537             this.lastKey = e.getKey();
19538             this.dqTask.delay(this.queryDelay);
19539         }
19540     },
19541
19542     // private
19543     validateBlur : function(){
19544         return !this.list || !this.list.isVisible();   
19545     },
19546
19547     // private
19548     initQuery : function(){
19549         this.doQuery(this.getRawValue());
19550     },
19551
19552     // private
19553     doForce : function(){
19554         if(this.el.dom.value.length > 0){
19555             this.el.dom.value =
19556                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19557              
19558         }
19559     },
19560
19561     /**
19562      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19563      * query allowing the query action to be canceled if needed.
19564      * @param {String} query The SQL query to execute
19565      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19566      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19567      * saved in the current store (defaults to false)
19568      */
19569     doQuery : function(q, forceAll){
19570         if(q === undefined || q === null){
19571             q = '';
19572         }
19573         var qe = {
19574             query: q,
19575             forceAll: forceAll,
19576             combo: this,
19577             cancel:false
19578         };
19579         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19580             return false;
19581         }
19582         q = qe.query;
19583         forceAll = qe.forceAll;
19584         if(forceAll === true || (q.length >= this.minChars)){
19585             if(this.lastQuery != q || this.alwaysQuery){
19586                 this.lastQuery = q;
19587                 if(this.mode == 'local'){
19588                     this.selectedIndex = -1;
19589                     if(forceAll){
19590                         this.store.clearFilter();
19591                     }else{
19592                         this.store.filter(this.displayField, q);
19593                     }
19594                     this.onLoad();
19595                 }else{
19596                     this.store.baseParams[this.queryParam] = q;
19597                     this.store.load({
19598                         params: this.getParams(q)
19599                     });
19600                     this.expand();
19601                 }
19602             }else{
19603                 this.selectedIndex = -1;
19604                 this.onLoad();   
19605             }
19606         }
19607     },
19608
19609     // private
19610     getParams : function(q){
19611         var p = {};
19612         //p[this.queryParam] = q;
19613         if(this.pageSize){
19614             p.start = 0;
19615             p.limit = this.pageSize;
19616         }
19617         return p;
19618     },
19619
19620     /**
19621      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19622      */
19623     collapse : function(){
19624         if(!this.isExpanded()){
19625             return;
19626         }
19627         this.list.hide();
19628         Roo.get(document).un('mousedown', this.collapseIf, this);
19629         Roo.get(document).un('mousewheel', this.collapseIf, this);
19630         if (!this.editable) {
19631             Roo.get(document).un('keydown', this.listKeyPress, this);
19632         }
19633         this.fireEvent('collapse', this);
19634     },
19635
19636     // private
19637     collapseIf : function(e){
19638         if(!e.within(this.wrap) && !e.within(this.list)){
19639             this.collapse();
19640         }
19641     },
19642
19643     /**
19644      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19645      */
19646     expand : function(){
19647         if(this.isExpanded() || !this.hasFocus){
19648             return;
19649         }
19650         this.list.alignTo(this.el, this.listAlign);
19651         this.list.show();
19652         Roo.get(document).on('mousedown', this.collapseIf, this);
19653         Roo.get(document).on('mousewheel', this.collapseIf, this);
19654         if (!this.editable) {
19655             Roo.get(document).on('keydown', this.listKeyPress, this);
19656         }
19657         
19658         this.fireEvent('expand', this);
19659     },
19660
19661     // private
19662     // Implements the default empty TriggerField.onTriggerClick function
19663     onTriggerClick : function(){
19664         if(this.disabled){
19665             return;
19666         }
19667         if(this.isExpanded()){
19668             this.collapse();
19669             if (!this.blockFocus) {
19670                 this.el.focus();
19671             }
19672             
19673         }else {
19674             this.hasFocus = true;
19675             if(this.triggerAction == 'all') {
19676                 this.doQuery(this.allQuery, true);
19677             } else {
19678                 this.doQuery(this.getRawValue());
19679             }
19680             if (!this.blockFocus) {
19681                 this.el.focus();
19682             }
19683         }
19684     },
19685     listKeyPress : function(e)
19686     {
19687         //Roo.log('listkeypress');
19688         // scroll to first matching element based on key pres..
19689         if (e.isSpecialKey()) {
19690             return false;
19691         }
19692         var k = String.fromCharCode(e.getKey()).toUpperCase();
19693         //Roo.log(k);
19694         var match  = false;
19695         var csel = this.view.getSelectedNodes();
19696         var cselitem = false;
19697         if (csel.length) {
19698             var ix = this.view.indexOf(csel[0]);
19699             cselitem  = this.store.getAt(ix);
19700             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19701                 cselitem = false;
19702             }
19703             
19704         }
19705         
19706         this.store.each(function(v) { 
19707             if (cselitem) {
19708                 // start at existing selection.
19709                 if (cselitem.id == v.id) {
19710                     cselitem = false;
19711                 }
19712                 return;
19713             }
19714                 
19715             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19716                 match = this.store.indexOf(v);
19717                 return false;
19718             }
19719         }, this);
19720         
19721         if (match === false) {
19722             return true; // no more action?
19723         }
19724         // scroll to?
19725         this.view.select(match);
19726         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19727         sn.scrollIntoView(sn.dom.parentNode, false);
19728     } 
19729
19730     /** 
19731     * @cfg {Boolean} grow 
19732     * @hide 
19733     */
19734     /** 
19735     * @cfg {Number} growMin 
19736     * @hide 
19737     */
19738     /** 
19739     * @cfg {Number} growMax 
19740     * @hide 
19741     */
19742     /**
19743      * @hide
19744      * @method autoSize
19745      */
19746 });/*
19747  * Copyright(c) 2010-2012, Roo J Solutions Limited
19748  *
19749  * Licence LGPL
19750  *
19751  */
19752
19753 /**
19754  * @class Roo.form.ComboBoxArray
19755  * @extends Roo.form.TextField
19756  * A facebook style adder... for lists of email / people / countries  etc...
19757  * pick multiple items from a combo box, and shows each one.
19758  *
19759  *  Fred [x]  Brian [x]  [Pick another |v]
19760  *
19761  *
19762  *  For this to work: it needs various extra information
19763  *    - normal combo problay has
19764  *      name, hiddenName
19765  *    + displayField, valueField
19766  *
19767  *    For our purpose...
19768  *
19769  *
19770  *   If we change from 'extends' to wrapping...
19771  *   
19772  *  
19773  *
19774  
19775  
19776  * @constructor
19777  * Create a new ComboBoxArray.
19778  * @param {Object} config Configuration options
19779  */
19780  
19781
19782 Roo.form.ComboBoxArray = function(config)
19783 {
19784     this.addEvents({
19785         /**
19786          * @event beforeremove
19787          * Fires before remove the value from the list
19788              * @param {Roo.form.ComboBoxArray} _self This combo box array
19789              * @param {Roo.form.ComboBoxArray.Item} item removed item
19790              */
19791         'beforeremove' : true,
19792         /**
19793          * @event remove
19794          * Fires when remove the value from the list
19795              * @param {Roo.form.ComboBoxArray} _self This combo box array
19796              * @param {Roo.form.ComboBoxArray.Item} item removed item
19797              */
19798         'remove' : true
19799         
19800         
19801     });
19802     
19803     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19804     
19805     this.items = new Roo.util.MixedCollection(false);
19806     
19807     // construct the child combo...
19808     
19809     
19810     
19811     
19812    
19813     
19814 }
19815
19816  
19817 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19818
19819     /**
19820      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19821      */
19822     
19823     lastData : false,
19824     
19825     // behavies liek a hiddne field
19826     inputType:      'hidden',
19827     /**
19828      * @cfg {Number} width The width of the box that displays the selected element
19829      */ 
19830     width:          300,
19831
19832     
19833     
19834     /**
19835      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19836      */
19837     name : false,
19838     /**
19839      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19840      */
19841     hiddenName : false,
19842     
19843     
19844     // private the array of items that are displayed..
19845     items  : false,
19846     // private - the hidden field el.
19847     hiddenEl : false,
19848     // private - the filed el..
19849     el : false,
19850     
19851     //validateValue : function() { return true; }, // all values are ok!
19852     //onAddClick: function() { },
19853     
19854     onRender : function(ct, position) 
19855     {
19856         
19857         // create the standard hidden element
19858         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19859         
19860         
19861         // give fake names to child combo;
19862         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19863         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19864         
19865         this.combo = Roo.factory(this.combo, Roo.form);
19866         this.combo.onRender(ct, position);
19867         if (typeof(this.combo.width) != 'undefined') {
19868             this.combo.onResize(this.combo.width,0);
19869         }
19870         
19871         this.combo.initEvents();
19872         
19873         // assigned so form know we need to do this..
19874         this.store          = this.combo.store;
19875         this.valueField     = this.combo.valueField;
19876         this.displayField   = this.combo.displayField ;
19877         
19878         
19879         this.combo.wrap.addClass('x-cbarray-grp');
19880         
19881         var cbwrap = this.combo.wrap.createChild(
19882             {tag: 'div', cls: 'x-cbarray-cb'},
19883             this.combo.el.dom
19884         );
19885         
19886              
19887         this.hiddenEl = this.combo.wrap.createChild({
19888             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19889         });
19890         this.el = this.combo.wrap.createChild({
19891             tag: 'input',  type:'hidden' , name: this.name, value : ''
19892         });
19893          //   this.el.dom.removeAttribute("name");
19894         
19895         
19896         this.outerWrap = this.combo.wrap;
19897         this.wrap = cbwrap;
19898         
19899         this.outerWrap.setWidth(this.width);
19900         this.outerWrap.dom.removeChild(this.el.dom);
19901         
19902         this.wrap.dom.appendChild(this.el.dom);
19903         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19904         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19905         
19906         this.combo.trigger.setStyle('position','relative');
19907         this.combo.trigger.setStyle('left', '0px');
19908         this.combo.trigger.setStyle('top', '2px');
19909         
19910         this.combo.el.setStyle('vertical-align', 'text-bottom');
19911         
19912         //this.trigger.setStyle('vertical-align', 'top');
19913         
19914         // this should use the code from combo really... on('add' ....)
19915         if (this.adder) {
19916             
19917         
19918             this.adder = this.outerWrap.createChild(
19919                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19920             var _t = this;
19921             this.adder.on('click', function(e) {
19922                 _t.fireEvent('adderclick', this, e);
19923             }, _t);
19924         }
19925         //var _t = this;
19926         //this.adder.on('click', this.onAddClick, _t);
19927         
19928         
19929         this.combo.on('select', function(cb, rec, ix) {
19930             this.addItem(rec.data);
19931             
19932             cb.setValue('');
19933             cb.el.dom.value = '';
19934             //cb.lastData = rec.data;
19935             // add to list
19936             
19937         }, this);
19938         
19939         
19940     },
19941     
19942     
19943     getName: function()
19944     {
19945         // returns hidden if it's set..
19946         if (!this.rendered) {return ''};
19947         return  this.hiddenName ? this.hiddenName : this.name;
19948         
19949     },
19950     
19951     
19952     onResize: function(w, h){
19953         
19954         return;
19955         // not sure if this is needed..
19956         //this.combo.onResize(w,h);
19957         
19958         if(typeof w != 'number'){
19959             // we do not handle it!?!?
19960             return;
19961         }
19962         var tw = this.combo.trigger.getWidth();
19963         tw += this.addicon ? this.addicon.getWidth() : 0;
19964         tw += this.editicon ? this.editicon.getWidth() : 0;
19965         var x = w - tw;
19966         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19967             
19968         this.combo.trigger.setStyle('left', '0px');
19969         
19970         if(this.list && this.listWidth === undefined){
19971             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19972             this.list.setWidth(lw);
19973             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19974         }
19975         
19976     
19977         
19978     },
19979     
19980     addItem: function(rec)
19981     {
19982         var valueField = this.combo.valueField;
19983         var displayField = this.combo.displayField;
19984         
19985         if (this.items.indexOfKey(rec[valueField]) > -1) {
19986             //console.log("GOT " + rec.data.id);
19987             return;
19988         }
19989         
19990         var x = new Roo.form.ComboBoxArray.Item({
19991             //id : rec[this.idField],
19992             data : rec,
19993             displayField : displayField ,
19994             tipField : displayField ,
19995             cb : this
19996         });
19997         // use the 
19998         this.items.add(rec[valueField],x);
19999         // add it before the element..
20000         this.updateHiddenEl();
20001         x.render(this.outerWrap, this.wrap.dom);
20002         // add the image handler..
20003     },
20004     
20005     updateHiddenEl : function()
20006     {
20007         this.validate();
20008         if (!this.hiddenEl) {
20009             return;
20010         }
20011         var ar = [];
20012         var idField = this.combo.valueField;
20013         
20014         this.items.each(function(f) {
20015             ar.push(f.data[idField]);
20016         });
20017         this.hiddenEl.dom.value = ar.join(',');
20018         this.validate();
20019     },
20020     
20021     reset : function()
20022     {
20023         this.items.clear();
20024         
20025         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20026            el.remove();
20027         });
20028         
20029         this.el.dom.value = '';
20030         if (this.hiddenEl) {
20031             this.hiddenEl.dom.value = '';
20032         }
20033         
20034     },
20035     getValue: function()
20036     {
20037         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20038     },
20039     setValue: function(v) // not a valid action - must use addItems..
20040     {
20041         
20042         this.reset();
20043          
20044         if (this.store.isLocal && (typeof(v) == 'string')) {
20045             // then we can use the store to find the values..
20046             // comma seperated at present.. this needs to allow JSON based encoding..
20047             this.hiddenEl.value  = v;
20048             var v_ar = [];
20049             Roo.each(v.split(','), function(k) {
20050                 Roo.log("CHECK " + this.valueField + ',' + k);
20051                 var li = this.store.query(this.valueField, k);
20052                 if (!li.length) {
20053                     return;
20054                 }
20055                 var add = {};
20056                 add[this.valueField] = k;
20057                 add[this.displayField] = li.item(0).data[this.displayField];
20058                 
20059                 this.addItem(add);
20060             }, this) 
20061              
20062         }
20063         if (typeof(v) == 'object' ) {
20064             // then let's assume it's an array of objects..
20065             Roo.each(v, function(l) {
20066                 this.addItem(l);
20067             }, this);
20068              
20069         }
20070         
20071         
20072     },
20073     setFromData: function(v)
20074     {
20075         // this recieves an object, if setValues is called.
20076         this.reset();
20077         this.el.dom.value = v[this.displayField];
20078         this.hiddenEl.dom.value = v[this.valueField];
20079         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20080             return;
20081         }
20082         var kv = v[this.valueField];
20083         var dv = v[this.displayField];
20084         kv = typeof(kv) != 'string' ? '' : kv;
20085         dv = typeof(dv) != 'string' ? '' : dv;
20086         
20087         
20088         var keys = kv.split(',');
20089         var display = dv.split(',');
20090         for (var i = 0 ; i < keys.length; i++) {
20091             
20092             add = {};
20093             add[this.valueField] = keys[i];
20094             add[this.displayField] = display[i];
20095             this.addItem(add);
20096         }
20097       
20098         
20099     },
20100     
20101     /**
20102      * Validates the combox array value
20103      * @return {Boolean} True if the value is valid, else false
20104      */
20105     validate : function(){
20106         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20107             this.clearInvalid();
20108             return true;
20109         }
20110         return false;
20111     },
20112     
20113     validateValue : function(value){
20114         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20115         
20116     },
20117     
20118     /*@
20119      * overide
20120      * 
20121      */
20122     isDirty : function() {
20123         if(this.disabled) {
20124             return false;
20125         }
20126         
20127         try {
20128             var d = Roo.decode(String(this.originalValue));
20129         } catch (e) {
20130             return String(this.getValue()) !== String(this.originalValue);
20131         }
20132         
20133         var originalValue = [];
20134         
20135         for (var i = 0; i < d.length; i++){
20136             originalValue.push(d[i][this.valueField]);
20137         }
20138         
20139         return String(this.getValue()) !== String(originalValue.join(','));
20140         
20141     }
20142     
20143 });
20144
20145
20146
20147 /**
20148  * @class Roo.form.ComboBoxArray.Item
20149  * @extends Roo.BoxComponent
20150  * A selected item in the list
20151  *  Fred [x]  Brian [x]  [Pick another |v]
20152  * 
20153  * @constructor
20154  * Create a new item.
20155  * @param {Object} config Configuration options
20156  */
20157  
20158 Roo.form.ComboBoxArray.Item = function(config) {
20159     config.id = Roo.id();
20160     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20161 }
20162
20163 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20164     data : {},
20165     cb: false,
20166     displayField : false,
20167     tipField : false,
20168     
20169     
20170     defaultAutoCreate : {
20171         tag: 'div',
20172         cls: 'x-cbarray-item',
20173         cn : [ 
20174             { tag: 'div' },
20175             {
20176                 tag: 'img',
20177                 width:16,
20178                 height : 16,
20179                 src : Roo.BLANK_IMAGE_URL ,
20180                 align: 'center'
20181             }
20182         ]
20183         
20184     },
20185     
20186  
20187     onRender : function(ct, position)
20188     {
20189         Roo.form.Field.superclass.onRender.call(this, ct, position);
20190         
20191         if(!this.el){
20192             var cfg = this.getAutoCreate();
20193             this.el = ct.createChild(cfg, position);
20194         }
20195         
20196         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20197         
20198         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20199             this.cb.renderer(this.data) :
20200             String.format('{0}',this.data[this.displayField]);
20201         
20202             
20203         this.el.child('div').dom.setAttribute('qtip',
20204                         String.format('{0}',this.data[this.tipField])
20205         );
20206         
20207         this.el.child('img').on('click', this.remove, this);
20208         
20209     },
20210    
20211     remove : function()
20212     {
20213         if(this.cb.disabled){
20214             return;
20215         }
20216         
20217         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20218             this.cb.items.remove(this);
20219             this.el.child('img').un('click', this.remove, this);
20220             this.el.remove();
20221             this.cb.updateHiddenEl();
20222
20223             this.cb.fireEvent('remove', this.cb, this);
20224         }
20225         
20226     }
20227 });/*
20228  * RooJS Library 1.1.1
20229  * Copyright(c) 2008-2011  Alan Knowles
20230  *
20231  * License - LGPL
20232  */
20233  
20234
20235 /**
20236  * @class Roo.form.ComboNested
20237  * @extends Roo.form.ComboBox
20238  * A combobox for that allows selection of nested items in a list,
20239  * eg.
20240  *
20241  *  Book
20242  *    -> red
20243  *    -> green
20244  *  Table
20245  *    -> square
20246  *      ->red
20247  *      ->green
20248  *    -> rectangle
20249  *      ->green
20250  *      
20251  * 
20252  * @constructor
20253  * Create a new ComboNested
20254  * @param {Object} config Configuration options
20255  */
20256 Roo.form.ComboNested = function(config){
20257     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20258     // should verify some data...
20259     // like
20260     // hiddenName = required..
20261     // displayField = required
20262     // valudField == required
20263     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20264     var _t = this;
20265     Roo.each(req, function(e) {
20266         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20267             throw "Roo.form.ComboNested : missing value for: " + e;
20268         }
20269     });
20270      
20271     
20272 };
20273
20274 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20275    
20276     /*
20277      * @config {Number} max Number of columns to show
20278      */
20279     
20280     maxColumns : 3,
20281    
20282     list : null, // the outermost div..
20283     innerLists : null, // the
20284     views : null,
20285     stores : null,
20286     // private
20287     onRender : function(ct, position)
20288     {
20289         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20290         
20291         if(this.hiddenName){
20292             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20293                     'before', true);
20294             this.hiddenField.value =
20295                 this.hiddenValue !== undefined ? this.hiddenValue :
20296                 this.value !== undefined ? this.value : '';
20297
20298             // prevent input submission
20299             this.el.dom.removeAttribute('name');
20300              
20301              
20302         }
20303         
20304         if(Roo.isGecko){
20305             this.el.dom.setAttribute('autocomplete', 'off');
20306         }
20307
20308         var cls = 'x-combo-list';
20309
20310         this.list = new Roo.Layer({
20311             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20312         });
20313
20314         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20315         this.list.setWidth(lw);
20316         this.list.swallowEvent('mousewheel');
20317         this.assetHeight = 0;
20318
20319         if(this.title){
20320             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20321             this.assetHeight += this.header.getHeight();
20322         }
20323         this.innerLists = [];
20324         this.views = [];
20325         this.stores = [];
20326         for (var i =0 ; i < this.maxColumns; i++) {
20327             this.onRenderList( cls, i);
20328         }
20329         
20330         // always needs footer, as we are going to have an 'OK' button.
20331         this.footer = this.list.createChild({cls:cls+'-ft'});
20332         this.pageTb = new Roo.Toolbar(this.footer);  
20333         var _this = this;
20334         this.pageTb.add(  {
20335             
20336             text: 'Done',
20337             handler: function()
20338             {
20339                 _this.collapse();
20340             }
20341         });
20342         
20343         if ( this.allowBlank && !this.disableClear) {
20344             
20345             this.pageTb.add(new Roo.Toolbar.Fill(), {
20346                 cls: 'x-btn-icon x-btn-clear',
20347                 text: '&#160;',
20348                 handler: function()
20349                 {
20350                     _this.collapse();
20351                     _this.clearValue();
20352                     _this.onSelect(false, -1);
20353                 }
20354             });
20355         }
20356         if (this.footer) {
20357             this.assetHeight += this.footer.getHeight();
20358         }
20359         
20360     },
20361     onRenderList : function (  cls, i)
20362     {
20363         
20364         var lw = Math.floor(
20365                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20366         );
20367         
20368         this.list.setWidth(lw); // default to '1'
20369
20370         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20371         //il.on('mouseover', this.onViewOver, this, { list:  i });
20372         //il.on('mousemove', this.onViewMove, this, { list:  i });
20373         il.setWidth(lw);
20374         il.setStyle({ 'overflow-x' : 'hidden'});
20375
20376         if(!this.tpl){
20377             this.tpl = new Roo.Template({
20378                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20379                 isEmpty: function (value, allValues) {
20380                     //Roo.log(value);
20381                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20382                     return dl ? 'has-children' : 'no-children'
20383                 }
20384             });
20385         }
20386         
20387         var store  = this.store;
20388         if (i > 0) {
20389             store  = new Roo.data.SimpleStore({
20390                 //fields : this.store.reader.meta.fields,
20391                 reader : this.store.reader,
20392                 data : [ ]
20393             });
20394         }
20395         this.stores[i]  = store;
20396                 
20397         
20398         
20399         var view = this.views[i] = new Roo.View(
20400             il,
20401             this.tpl,
20402             {
20403                 singleSelect:true,
20404                 store: store,
20405                 selectedClass: this.selectedClass
20406             }
20407         );
20408         view.getEl().setWidth(lw);
20409         view.getEl().setStyle({
20410             position: i < 1 ? 'relative' : 'absolute',
20411             top: 0,
20412             left: (i * lw ) + 'px',
20413             display : i > 0 ? 'none' : 'block'
20414         });
20415         view.on('selectionchange', this.onSelectChange, this, {list : i });
20416         view.on('dblclick', this.onDoubleClick, this, {list : i });
20417         //view.on('click', this.onViewClick, this, { list : i });
20418
20419         store.on('beforeload', this.onBeforeLoad, this);
20420         store.on('load',  this.onLoad, this, { list  : i});
20421         store.on('loadexception', this.onLoadException, this);
20422
20423         // hide the other vies..
20424         
20425         
20426         
20427     },
20428     onResize : function()  {},
20429     
20430     restrictHeight : function()
20431     {
20432         var mh = 0;
20433         Roo.each(this.innerLists, function(il,i) {
20434             var el = this.views[i].getEl();
20435             el.dom.style.height = '';
20436             var inner = el.dom;
20437             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20438             // only adjust heights on other ones..
20439             if (i < 1) {
20440                 
20441                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20442                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20443                 mh = Math.max(el.getHeight(), mh);
20444             }
20445             
20446             
20447         }, this);
20448         
20449         this.list.beginUpdate();
20450         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20451         this.list.alignTo(this.el, this.listAlign);
20452         this.list.endUpdate();
20453         
20454     },
20455      
20456     
20457     // -- store handlers..
20458     // private
20459     onBeforeLoad : function()
20460     {
20461         if(!this.hasFocus){
20462             return;
20463         }
20464         this.innerLists[0].update(this.loadingText ?
20465                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20466         this.restrictHeight();
20467         this.selectedIndex = -1;
20468     },
20469     // private
20470     onLoad : function(a,b,c,d)
20471     {
20472         
20473         if(!this.hasFocus){
20474             return;
20475         }
20476         
20477         if(this.store.getCount() > 0) {
20478             this.expand();
20479             this.restrictHeight();   
20480         } else {
20481             this.onEmptyResults();
20482         }
20483         /*
20484         this.stores[1].loadData([]);
20485         this.stores[2].loadData([]);
20486         this.views
20487         */    
20488     
20489         //this.el.focus();
20490     },
20491     
20492     
20493     // private
20494     onLoadException : function()
20495     {
20496         this.collapse();
20497         Roo.log(this.store.reader.jsonData);
20498         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20499             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20500         }
20501         
20502         
20503     } ,
20504      
20505      
20506
20507     onSelectChange : function (view, sels, opts )
20508     {
20509         var ix = view.getSelectedIndexes();
20510         
20511         
20512         if (opts.list > this.maxColumns - 2) {
20513              
20514             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20515             return;
20516         }
20517         
20518         if (!ix.length) {
20519             this.setFromData({});
20520             var str = this.stores[opts.list+1];
20521             str.removeAll();
20522             return;
20523         }
20524         
20525         var rec = view.store.getAt(ix[0]);
20526         this.setFromData(rec.data);
20527         
20528         var lw = Math.floor(
20529                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20530         );
20531         
20532         this.stores[opts.list+1].loadDataFromChildren( rec );
20533         var dl = this.stores[opts.list+1]. getTotalCount();
20534         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20535         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20536         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20537         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20538     },
20539     onDoubleClick : function()
20540     {
20541         this.collapse(); //??
20542     },
20543     
20544      
20545     
20546     findRecord : function (prop,value)
20547     {
20548         return this.findRecordInStore(this.store, prop,value);
20549     },
20550     
20551     // private
20552     findRecordInStore : function(store, prop, value)
20553     {
20554         var cstore = new Roo.data.SimpleStore({
20555             //fields : this.store.reader.meta.fields, // we need array reader.. for
20556             reader : this.store.reader,
20557             data : [ ]
20558         });
20559         var _this = this;
20560         var record  = false;
20561         if(store.getCount() > 0){
20562            store.each(function(r){
20563                 if(r.data[prop] == value){
20564                     record = r;
20565                     return false;
20566                 }
20567                 if (r.data.cn && r.data.cn.length) {
20568                     cstore.loadDataFromChildren( r);
20569                     var cret = _this.findRecordInStore(cstore, prop, value);
20570                     if (cret !== false) {
20571                         record = cret;
20572                         return false;
20573                     }
20574                 }
20575                 
20576                 return true;
20577             });
20578         }
20579         return record;
20580     }
20581     
20582     
20583     
20584     
20585 });/*
20586  * Based on:
20587  * Ext JS Library 1.1.1
20588  * Copyright(c) 2006-2007, Ext JS, LLC.
20589  *
20590  * Originally Released Under LGPL - original licence link has changed is not relivant.
20591  *
20592  * Fork - LGPL
20593  * <script type="text/javascript">
20594  */
20595 /**
20596  * @class Roo.form.Checkbox
20597  * @extends Roo.form.Field
20598  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20599  * @constructor
20600  * Creates a new Checkbox
20601  * @param {Object} config Configuration options
20602  */
20603 Roo.form.Checkbox = function(config){
20604     Roo.form.Checkbox.superclass.constructor.call(this, config);
20605     this.addEvents({
20606         /**
20607          * @event check
20608          * Fires when the checkbox is checked or unchecked.
20609              * @param {Roo.form.Checkbox} this This checkbox
20610              * @param {Boolean} checked The new checked value
20611              */
20612         check : true
20613     });
20614 };
20615
20616 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20617     /**
20618      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20619      */
20620     focusClass : undefined,
20621     /**
20622      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20623      */
20624     fieldClass: "x-form-field",
20625     /**
20626      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20627      */
20628     checked: false,
20629     /**
20630      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20631      * {tag: "input", type: "checkbox", autocomplete: "off"})
20632      */
20633     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20634     /**
20635      * @cfg {String} boxLabel The text that appears beside the checkbox
20636      */
20637     boxLabel : "",
20638     /**
20639      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20640      */  
20641     inputValue : '1',
20642     /**
20643      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20644      */
20645      valueOff: '0', // value when not checked..
20646
20647     actionMode : 'viewEl', 
20648     //
20649     // private
20650     itemCls : 'x-menu-check-item x-form-item',
20651     groupClass : 'x-menu-group-item',
20652     inputType : 'hidden',
20653     
20654     
20655     inSetChecked: false, // check that we are not calling self...
20656     
20657     inputElement: false, // real input element?
20658     basedOn: false, // ????
20659     
20660     isFormField: true, // not sure where this is needed!!!!
20661
20662     onResize : function(){
20663         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20664         if(!this.boxLabel){
20665             this.el.alignTo(this.wrap, 'c-c');
20666         }
20667     },
20668
20669     initEvents : function(){
20670         Roo.form.Checkbox.superclass.initEvents.call(this);
20671         this.el.on("click", this.onClick,  this);
20672         this.el.on("change", this.onClick,  this);
20673     },
20674
20675
20676     getResizeEl : function(){
20677         return this.wrap;
20678     },
20679
20680     getPositionEl : function(){
20681         return this.wrap;
20682     },
20683
20684     // private
20685     onRender : function(ct, position){
20686         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20687         /*
20688         if(this.inputValue !== undefined){
20689             this.el.dom.value = this.inputValue;
20690         }
20691         */
20692         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20693         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20694         var viewEl = this.wrap.createChild({ 
20695             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20696         this.viewEl = viewEl;   
20697         this.wrap.on('click', this.onClick,  this); 
20698         
20699         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20700         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20701         
20702         
20703         
20704         if(this.boxLabel){
20705             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20706         //    viewEl.on('click', this.onClick,  this); 
20707         }
20708         //if(this.checked){
20709             this.setChecked(this.checked);
20710         //}else{
20711             //this.checked = this.el.dom;
20712         //}
20713
20714     },
20715
20716     // private
20717     initValue : Roo.emptyFn,
20718
20719     /**
20720      * Returns the checked state of the checkbox.
20721      * @return {Boolean} True if checked, else false
20722      */
20723     getValue : function(){
20724         if(this.el){
20725             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20726         }
20727         return this.valueOff;
20728         
20729     },
20730
20731         // private
20732     onClick : function(){ 
20733         if (this.disabled) {
20734             return;
20735         }
20736         this.setChecked(!this.checked);
20737
20738         //if(this.el.dom.checked != this.checked){
20739         //    this.setValue(this.el.dom.checked);
20740        // }
20741     },
20742
20743     /**
20744      * Sets the checked state of the checkbox.
20745      * On is always based on a string comparison between inputValue and the param.
20746      * @param {Boolean/String} value - the value to set 
20747      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20748      */
20749     setValue : function(v,suppressEvent){
20750         
20751         
20752         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20753         //if(this.el && this.el.dom){
20754         //    this.el.dom.checked = this.checked;
20755         //    this.el.dom.defaultChecked = this.checked;
20756         //}
20757         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20758         //this.fireEvent("check", this, this.checked);
20759     },
20760     // private..
20761     setChecked : function(state,suppressEvent)
20762     {
20763         if (this.inSetChecked) {
20764             this.checked = state;
20765             return;
20766         }
20767         
20768     
20769         if(this.wrap){
20770             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20771         }
20772         this.checked = state;
20773         if(suppressEvent !== true){
20774             this.fireEvent('check', this, state);
20775         }
20776         this.inSetChecked = true;
20777         this.el.dom.value = state ? this.inputValue : this.valueOff;
20778         this.inSetChecked = false;
20779         
20780     },
20781     // handle setting of hidden value by some other method!!?!?
20782     setFromHidden: function()
20783     {
20784         if(!this.el){
20785             return;
20786         }
20787         //console.log("SET FROM HIDDEN");
20788         //alert('setFrom hidden');
20789         this.setValue(this.el.dom.value);
20790     },
20791     
20792     onDestroy : function()
20793     {
20794         if(this.viewEl){
20795             Roo.get(this.viewEl).remove();
20796         }
20797          
20798         Roo.form.Checkbox.superclass.onDestroy.call(this);
20799     },
20800     
20801     setBoxLabel : function(str)
20802     {
20803         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20804     }
20805
20806 });/*
20807  * Based on:
20808  * Ext JS Library 1.1.1
20809  * Copyright(c) 2006-2007, Ext JS, LLC.
20810  *
20811  * Originally Released Under LGPL - original licence link has changed is not relivant.
20812  *
20813  * Fork - LGPL
20814  * <script type="text/javascript">
20815  */
20816  
20817 /**
20818  * @class Roo.form.Radio
20819  * @extends Roo.form.Checkbox
20820  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20821  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20822  * @constructor
20823  * Creates a new Radio
20824  * @param {Object} config Configuration options
20825  */
20826 Roo.form.Radio = function(){
20827     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20828 };
20829 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20830     inputType: 'radio',
20831
20832     /**
20833      * If this radio is part of a group, it will return the selected value
20834      * @return {String}
20835      */
20836     getGroupValue : function(){
20837         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20838     },
20839     
20840     
20841     onRender : function(ct, position){
20842         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20843         
20844         if(this.inputValue !== undefined){
20845             this.el.dom.value = this.inputValue;
20846         }
20847          
20848         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20849         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20850         //var viewEl = this.wrap.createChild({ 
20851         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20852         //this.viewEl = viewEl;   
20853         //this.wrap.on('click', this.onClick,  this); 
20854         
20855         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20856         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20857         
20858         
20859         
20860         if(this.boxLabel){
20861             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20862         //    viewEl.on('click', this.onClick,  this); 
20863         }
20864          if(this.checked){
20865             this.el.dom.checked =   'checked' ;
20866         }
20867          
20868     } 
20869     
20870     
20871 });//<script type="text/javascript">
20872
20873 /*
20874  * Based  Ext JS Library 1.1.1
20875  * Copyright(c) 2006-2007, Ext JS, LLC.
20876  * LGPL
20877  *
20878  */
20879  
20880 /**
20881  * @class Roo.HtmlEditorCore
20882  * @extends Roo.Component
20883  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20884  *
20885  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20886  */
20887
20888 Roo.HtmlEditorCore = function(config){
20889     
20890     
20891     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20892     
20893     
20894     this.addEvents({
20895         /**
20896          * @event initialize
20897          * Fires when the editor is fully initialized (including the iframe)
20898          * @param {Roo.HtmlEditorCore} this
20899          */
20900         initialize: true,
20901         /**
20902          * @event activate
20903          * Fires when the editor is first receives the focus. Any insertion must wait
20904          * until after this event.
20905          * @param {Roo.HtmlEditorCore} this
20906          */
20907         activate: true,
20908          /**
20909          * @event beforesync
20910          * Fires before the textarea is updated with content from the editor iframe. Return false
20911          * to cancel the sync.
20912          * @param {Roo.HtmlEditorCore} this
20913          * @param {String} html
20914          */
20915         beforesync: true,
20916          /**
20917          * @event beforepush
20918          * Fires before the iframe editor is updated with content from the textarea. Return false
20919          * to cancel the push.
20920          * @param {Roo.HtmlEditorCore} this
20921          * @param {String} html
20922          */
20923         beforepush: true,
20924          /**
20925          * @event sync
20926          * Fires when the textarea is updated with content from the editor iframe.
20927          * @param {Roo.HtmlEditorCore} this
20928          * @param {String} html
20929          */
20930         sync: true,
20931          /**
20932          * @event push
20933          * Fires when the iframe editor is updated with content from the textarea.
20934          * @param {Roo.HtmlEditorCore} this
20935          * @param {String} html
20936          */
20937         push: true,
20938         
20939         /**
20940          * @event editorevent
20941          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20942          * @param {Roo.HtmlEditorCore} this
20943          */
20944         editorevent: true
20945         
20946     });
20947     
20948     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20949     
20950     // defaults : white / black...
20951     this.applyBlacklists();
20952     
20953     
20954     
20955 };
20956
20957
20958 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20959
20960
20961      /**
20962      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20963      */
20964     
20965     owner : false,
20966     
20967      /**
20968      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20969      *                        Roo.resizable.
20970      */
20971     resizable : false,
20972      /**
20973      * @cfg {Number} height (in pixels)
20974      */   
20975     height: 300,
20976    /**
20977      * @cfg {Number} width (in pixels)
20978      */   
20979     width: 500,
20980     
20981     /**
20982      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20983      * 
20984      */
20985     stylesheets: false,
20986     
20987     // id of frame..
20988     frameId: false,
20989     
20990     // private properties
20991     validationEvent : false,
20992     deferHeight: true,
20993     initialized : false,
20994     activated : false,
20995     sourceEditMode : false,
20996     onFocus : Roo.emptyFn,
20997     iframePad:3,
20998     hideMode:'offsets',
20999     
21000     clearUp: true,
21001     
21002     // blacklist + whitelisted elements..
21003     black: false,
21004     white: false,
21005      
21006     bodyCls : '',
21007
21008     /**
21009      * Protected method that will not generally be called directly. It
21010      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21011      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21012      */
21013     getDocMarkup : function(){
21014         // body styles..
21015         var st = '';
21016         
21017         // inherit styels from page...?? 
21018         if (this.stylesheets === false) {
21019             
21020             Roo.get(document.head).select('style').each(function(node) {
21021                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21022             });
21023             
21024             Roo.get(document.head).select('link').each(function(node) { 
21025                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21026             });
21027             
21028         } else if (!this.stylesheets.length) {
21029                 // simple..
21030                 st = '<style type="text/css">' +
21031                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21032                    '</style>';
21033         } else { 
21034             st = '<style type="text/css">' +
21035                     this.stylesheets +
21036                 '</style>';
21037         }
21038         
21039         st +=  '<style type="text/css">' +
21040             'IMG { cursor: pointer } ' +
21041         '</style>';
21042
21043         var cls = 'roo-htmleditor-body';
21044         
21045         if(this.bodyCls.length){
21046             cls += ' ' + this.bodyCls;
21047         }
21048         
21049         return '<html><head>' + st  +
21050             //<style type="text/css">' +
21051             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21052             //'</style>' +
21053             ' </head><body class="' +  cls + '"></body></html>';
21054     },
21055
21056     // private
21057     onRender : function(ct, position)
21058     {
21059         var _t = this;
21060         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21061         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21062         
21063         
21064         this.el.dom.style.border = '0 none';
21065         this.el.dom.setAttribute('tabIndex', -1);
21066         this.el.addClass('x-hidden hide');
21067         
21068         
21069         
21070         if(Roo.isIE){ // fix IE 1px bogus margin
21071             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21072         }
21073        
21074         
21075         this.frameId = Roo.id();
21076         
21077          
21078         
21079         var iframe = this.owner.wrap.createChild({
21080             tag: 'iframe',
21081             cls: 'form-control', // bootstrap..
21082             id: this.frameId,
21083             name: this.frameId,
21084             frameBorder : 'no',
21085             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21086         }, this.el
21087         );
21088         
21089         
21090         this.iframe = iframe.dom;
21091
21092          this.assignDocWin();
21093         
21094         this.doc.designMode = 'on';
21095        
21096         this.doc.open();
21097         this.doc.write(this.getDocMarkup());
21098         this.doc.close();
21099
21100         
21101         var task = { // must defer to wait for browser to be ready
21102             run : function(){
21103                 //console.log("run task?" + this.doc.readyState);
21104                 this.assignDocWin();
21105                 if(this.doc.body || this.doc.readyState == 'complete'){
21106                     try {
21107                         this.doc.designMode="on";
21108                     } catch (e) {
21109                         return;
21110                     }
21111                     Roo.TaskMgr.stop(task);
21112                     this.initEditor.defer(10, this);
21113                 }
21114             },
21115             interval : 10,
21116             duration: 10000,
21117             scope: this
21118         };
21119         Roo.TaskMgr.start(task);
21120
21121     },
21122
21123     // private
21124     onResize : function(w, h)
21125     {
21126          Roo.log('resize: ' +w + ',' + h );
21127         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21128         if(!this.iframe){
21129             return;
21130         }
21131         if(typeof w == 'number'){
21132             
21133             this.iframe.style.width = w + 'px';
21134         }
21135         if(typeof h == 'number'){
21136             
21137             this.iframe.style.height = h + 'px';
21138             if(this.doc){
21139                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21140             }
21141         }
21142         
21143     },
21144
21145     /**
21146      * Toggles the editor between standard and source edit mode.
21147      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21148      */
21149     toggleSourceEdit : function(sourceEditMode){
21150         
21151         this.sourceEditMode = sourceEditMode === true;
21152         
21153         if(this.sourceEditMode){
21154  
21155             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21156             
21157         }else{
21158             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21159             //this.iframe.className = '';
21160             this.deferFocus();
21161         }
21162         //this.setSize(this.owner.wrap.getSize());
21163         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21164     },
21165
21166     
21167   
21168
21169     /**
21170      * Protected method that will not generally be called directly. If you need/want
21171      * custom HTML cleanup, this is the method you should override.
21172      * @param {String} html The HTML to be cleaned
21173      * return {String} The cleaned HTML
21174      */
21175     cleanHtml : function(html){
21176         html = String(html);
21177         if(html.length > 5){
21178             if(Roo.isSafari){ // strip safari nonsense
21179                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21180             }
21181         }
21182         if(html == '&nbsp;'){
21183             html = '';
21184         }
21185         return html;
21186     },
21187
21188     /**
21189      * HTML Editor -> Textarea
21190      * Protected method that will not generally be called directly. Syncs the contents
21191      * of the editor iframe with the textarea.
21192      */
21193     syncValue : function(){
21194         if(this.initialized){
21195             var bd = (this.doc.body || this.doc.documentElement);
21196             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21197             var html = bd.innerHTML;
21198             if(Roo.isSafari){
21199                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21200                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21201                 if(m && m[1]){
21202                     html = '<div style="'+m[0]+'">' + html + '</div>';
21203                 }
21204             }
21205             html = this.cleanHtml(html);
21206             // fix up the special chars.. normaly like back quotes in word...
21207             // however we do not want to do this with chinese..
21208             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21209                 
21210                 var cc = match.charCodeAt();
21211
21212                 // Get the character value, handling surrogate pairs
21213                 if (match.length == 2) {
21214                     // It's a surrogate pair, calculate the Unicode code point
21215                     var high = match.charCodeAt(0) - 0xD800;
21216                     var low  = match.charCodeAt(1) - 0xDC00;
21217                     cc = (high * 0x400) + low + 0x10000;
21218                 }  else if (
21219                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21220                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21221                     (cc >= 0xf900 && cc < 0xfb00 )
21222                 ) {
21223                         return match;
21224                 }  
21225          
21226                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21227                 return "&#" + cc + ";";
21228                 
21229                 
21230             });
21231             
21232             
21233              
21234             if(this.owner.fireEvent('beforesync', this, html) !== false){
21235                 this.el.dom.value = html;
21236                 this.owner.fireEvent('sync', this, html);
21237             }
21238         }
21239     },
21240
21241     /**
21242      * Protected method that will not generally be called directly. Pushes the value of the textarea
21243      * into the iframe editor.
21244      */
21245     pushValue : function(){
21246         if(this.initialized){
21247             var v = this.el.dom.value.trim();
21248             
21249 //            if(v.length < 1){
21250 //                v = '&#160;';
21251 //            }
21252             
21253             if(this.owner.fireEvent('beforepush', this, v) !== false){
21254                 var d = (this.doc.body || this.doc.documentElement);
21255                 d.innerHTML = v;
21256                 this.cleanUpPaste();
21257                 this.el.dom.value = d.innerHTML;
21258                 this.owner.fireEvent('push', this, v);
21259             }
21260         }
21261     },
21262
21263     // private
21264     deferFocus : function(){
21265         this.focus.defer(10, this);
21266     },
21267
21268     // doc'ed in Field
21269     focus : function(){
21270         if(this.win && !this.sourceEditMode){
21271             this.win.focus();
21272         }else{
21273             this.el.focus();
21274         }
21275     },
21276     
21277     assignDocWin: function()
21278     {
21279         var iframe = this.iframe;
21280         
21281          if(Roo.isIE){
21282             this.doc = iframe.contentWindow.document;
21283             this.win = iframe.contentWindow;
21284         } else {
21285 //            if (!Roo.get(this.frameId)) {
21286 //                return;
21287 //            }
21288 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21289 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21290             
21291             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21292                 return;
21293             }
21294             
21295             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21296             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21297         }
21298     },
21299     
21300     // private
21301     initEditor : function(){
21302         //console.log("INIT EDITOR");
21303         this.assignDocWin();
21304         
21305         
21306         
21307         this.doc.designMode="on";
21308         this.doc.open();
21309         this.doc.write(this.getDocMarkup());
21310         this.doc.close();
21311         
21312         var dbody = (this.doc.body || this.doc.documentElement);
21313         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21314         // this copies styles from the containing element into thsi one..
21315         // not sure why we need all of this..
21316         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21317         
21318         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21319         //ss['background-attachment'] = 'fixed'; // w3c
21320         dbody.bgProperties = 'fixed'; // ie
21321         //Roo.DomHelper.applyStyles(dbody, ss);
21322         Roo.EventManager.on(this.doc, {
21323             //'mousedown': this.onEditorEvent,
21324             'mouseup': this.onEditorEvent,
21325             'dblclick': this.onEditorEvent,
21326             'click': this.onEditorEvent,
21327             'keyup': this.onEditorEvent,
21328             buffer:100,
21329             scope: this
21330         });
21331         if(Roo.isGecko){
21332             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21333         }
21334         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21335             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21336         }
21337         this.initialized = true;
21338
21339         this.owner.fireEvent('initialize', this);
21340         this.pushValue();
21341     },
21342
21343     // private
21344     onDestroy : function(){
21345         
21346         
21347         
21348         if(this.rendered){
21349             
21350             //for (var i =0; i < this.toolbars.length;i++) {
21351             //    // fixme - ask toolbars for heights?
21352             //    this.toolbars[i].onDestroy();
21353            // }
21354             
21355             //this.wrap.dom.innerHTML = '';
21356             //this.wrap.remove();
21357         }
21358     },
21359
21360     // private
21361     onFirstFocus : function(){
21362         
21363         this.assignDocWin();
21364         
21365         
21366         this.activated = true;
21367          
21368     
21369         if(Roo.isGecko){ // prevent silly gecko errors
21370             this.win.focus();
21371             var s = this.win.getSelection();
21372             if(!s.focusNode || s.focusNode.nodeType != 3){
21373                 var r = s.getRangeAt(0);
21374                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21375                 r.collapse(true);
21376                 this.deferFocus();
21377             }
21378             try{
21379                 this.execCmd('useCSS', true);
21380                 this.execCmd('styleWithCSS', false);
21381             }catch(e){}
21382         }
21383         this.owner.fireEvent('activate', this);
21384     },
21385
21386     // private
21387     adjustFont: function(btn){
21388         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21389         //if(Roo.isSafari){ // safari
21390         //    adjust *= 2;
21391        // }
21392         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21393         if(Roo.isSafari){ // safari
21394             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21395             v =  (v < 10) ? 10 : v;
21396             v =  (v > 48) ? 48 : v;
21397             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21398             
21399         }
21400         
21401         
21402         v = Math.max(1, v+adjust);
21403         
21404         this.execCmd('FontSize', v  );
21405     },
21406
21407     onEditorEvent : function(e)
21408     {
21409         this.owner.fireEvent('editorevent', this, e);
21410       //  this.updateToolbar();
21411         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21412     },
21413
21414     insertTag : function(tg)
21415     {
21416         // could be a bit smarter... -> wrap the current selected tRoo..
21417         if (tg.toLowerCase() == 'span' ||
21418             tg.toLowerCase() == 'code' ||
21419             tg.toLowerCase() == 'sup' ||
21420             tg.toLowerCase() == 'sub' 
21421             ) {
21422             
21423             range = this.createRange(this.getSelection());
21424             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21425             wrappingNode.appendChild(range.extractContents());
21426             range.insertNode(wrappingNode);
21427
21428             return;
21429             
21430             
21431             
21432         }
21433         this.execCmd("formatblock",   tg);
21434         
21435     },
21436     
21437     insertText : function(txt)
21438     {
21439         
21440         
21441         var range = this.createRange();
21442         range.deleteContents();
21443                //alert(Sender.getAttribute('label'));
21444                
21445         range.insertNode(this.doc.createTextNode(txt));
21446     } ,
21447     
21448      
21449
21450     /**
21451      * Executes a Midas editor command on the editor document and performs necessary focus and
21452      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21453      * @param {String} cmd The Midas command
21454      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21455      */
21456     relayCmd : function(cmd, value){
21457         this.win.focus();
21458         this.execCmd(cmd, value);
21459         this.owner.fireEvent('editorevent', this);
21460         //this.updateToolbar();
21461         this.owner.deferFocus();
21462     },
21463
21464     /**
21465      * Executes a Midas editor command directly on the editor document.
21466      * For visual commands, you should use {@link #relayCmd} instead.
21467      * <b>This should only be called after the editor is initialized.</b>
21468      * @param {String} cmd The Midas command
21469      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21470      */
21471     execCmd : function(cmd, value){
21472         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21473         this.syncValue();
21474     },
21475  
21476  
21477    
21478     /**
21479      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21480      * to insert tRoo.
21481      * @param {String} text | dom node.. 
21482      */
21483     insertAtCursor : function(text)
21484     {
21485         
21486         if(!this.activated){
21487             return;
21488         }
21489         /*
21490         if(Roo.isIE){
21491             this.win.focus();
21492             var r = this.doc.selection.createRange();
21493             if(r){
21494                 r.collapse(true);
21495                 r.pasteHTML(text);
21496                 this.syncValue();
21497                 this.deferFocus();
21498             
21499             }
21500             return;
21501         }
21502         */
21503         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21504             this.win.focus();
21505             
21506             
21507             // from jquery ui (MIT licenced)
21508             var range, node;
21509             var win = this.win;
21510             
21511             if (win.getSelection && win.getSelection().getRangeAt) {
21512                 range = win.getSelection().getRangeAt(0);
21513                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21514                 range.insertNode(node);
21515             } else if (win.document.selection && win.document.selection.createRange) {
21516                 // no firefox support
21517                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21518                 win.document.selection.createRange().pasteHTML(txt);
21519             } else {
21520                 // no firefox support
21521                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21522                 this.execCmd('InsertHTML', txt);
21523             } 
21524             
21525             this.syncValue();
21526             
21527             this.deferFocus();
21528         }
21529     },
21530  // private
21531     mozKeyPress : function(e){
21532         if(e.ctrlKey){
21533             var c = e.getCharCode(), cmd;
21534           
21535             if(c > 0){
21536                 c = String.fromCharCode(c).toLowerCase();
21537                 switch(c){
21538                     case 'b':
21539                         cmd = 'bold';
21540                         break;
21541                     case 'i':
21542                         cmd = 'italic';
21543                         break;
21544                     
21545                     case 'u':
21546                         cmd = 'underline';
21547                         break;
21548                     
21549                     case 'v':
21550                         this.cleanUpPaste.defer(100, this);
21551                         return;
21552                         
21553                 }
21554                 if(cmd){
21555                     this.win.focus();
21556                     this.execCmd(cmd);
21557                     this.deferFocus();
21558                     e.preventDefault();
21559                 }
21560                 
21561             }
21562         }
21563     },
21564
21565     // private
21566     fixKeys : function(){ // load time branching for fastest keydown performance
21567         if(Roo.isIE){
21568             return function(e){
21569                 var k = e.getKey(), r;
21570                 if(k == e.TAB){
21571                     e.stopEvent();
21572                     r = this.doc.selection.createRange();
21573                     if(r){
21574                         r.collapse(true);
21575                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21576                         this.deferFocus();
21577                     }
21578                     return;
21579                 }
21580                 
21581                 if(k == e.ENTER){
21582                     r = this.doc.selection.createRange();
21583                     if(r){
21584                         var target = r.parentElement();
21585                         if(!target || target.tagName.toLowerCase() != 'li'){
21586                             e.stopEvent();
21587                             r.pasteHTML('<br />');
21588                             r.collapse(false);
21589                             r.select();
21590                         }
21591                     }
21592                 }
21593                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21594                     this.cleanUpPaste.defer(100, this);
21595                     return;
21596                 }
21597                 
21598                 
21599             };
21600         }else if(Roo.isOpera){
21601             return function(e){
21602                 var k = e.getKey();
21603                 if(k == e.TAB){
21604                     e.stopEvent();
21605                     this.win.focus();
21606                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21607                     this.deferFocus();
21608                 }
21609                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21610                     this.cleanUpPaste.defer(100, this);
21611                     return;
21612                 }
21613                 
21614             };
21615         }else if(Roo.isSafari){
21616             return function(e){
21617                 var k = e.getKey();
21618                 
21619                 if(k == e.TAB){
21620                     e.stopEvent();
21621                     this.execCmd('InsertText','\t');
21622                     this.deferFocus();
21623                     return;
21624                 }
21625                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21626                     this.cleanUpPaste.defer(100, this);
21627                     return;
21628                 }
21629                 
21630              };
21631         }
21632     }(),
21633     
21634     getAllAncestors: function()
21635     {
21636         var p = this.getSelectedNode();
21637         var a = [];
21638         if (!p) {
21639             a.push(p); // push blank onto stack..
21640             p = this.getParentElement();
21641         }
21642         
21643         
21644         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21645             a.push(p);
21646             p = p.parentNode;
21647         }
21648         a.push(this.doc.body);
21649         return a;
21650     },
21651     lastSel : false,
21652     lastSelNode : false,
21653     
21654     
21655     getSelection : function() 
21656     {
21657         this.assignDocWin();
21658         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21659     },
21660     
21661     getSelectedNode: function() 
21662     {
21663         // this may only work on Gecko!!!
21664         
21665         // should we cache this!!!!
21666         
21667         
21668         
21669          
21670         var range = this.createRange(this.getSelection()).cloneRange();
21671         
21672         if (Roo.isIE) {
21673             var parent = range.parentElement();
21674             while (true) {
21675                 var testRange = range.duplicate();
21676                 testRange.moveToElementText(parent);
21677                 if (testRange.inRange(range)) {
21678                     break;
21679                 }
21680                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21681                     break;
21682                 }
21683                 parent = parent.parentElement;
21684             }
21685             return parent;
21686         }
21687         
21688         // is ancestor a text element.
21689         var ac =  range.commonAncestorContainer;
21690         if (ac.nodeType == 3) {
21691             ac = ac.parentNode;
21692         }
21693         
21694         var ar = ac.childNodes;
21695          
21696         var nodes = [];
21697         var other_nodes = [];
21698         var has_other_nodes = false;
21699         for (var i=0;i<ar.length;i++) {
21700             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21701                 continue;
21702             }
21703             // fullly contained node.
21704             
21705             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21706                 nodes.push(ar[i]);
21707                 continue;
21708             }
21709             
21710             // probably selected..
21711             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21712                 other_nodes.push(ar[i]);
21713                 continue;
21714             }
21715             // outer..
21716             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21717                 continue;
21718             }
21719             
21720             
21721             has_other_nodes = true;
21722         }
21723         if (!nodes.length && other_nodes.length) {
21724             nodes= other_nodes;
21725         }
21726         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21727             return false;
21728         }
21729         
21730         return nodes[0];
21731     },
21732     createRange: function(sel)
21733     {
21734         // this has strange effects when using with 
21735         // top toolbar - not sure if it's a great idea.
21736         //this.editor.contentWindow.focus();
21737         if (typeof sel != "undefined") {
21738             try {
21739                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21740             } catch(e) {
21741                 return this.doc.createRange();
21742             }
21743         } else {
21744             return this.doc.createRange();
21745         }
21746     },
21747     getParentElement: function()
21748     {
21749         
21750         this.assignDocWin();
21751         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21752         
21753         var range = this.createRange(sel);
21754          
21755         try {
21756             var p = range.commonAncestorContainer;
21757             while (p.nodeType == 3) { // text node
21758                 p = p.parentNode;
21759             }
21760             return p;
21761         } catch (e) {
21762             return null;
21763         }
21764     
21765     },
21766     /***
21767      *
21768      * Range intersection.. the hard stuff...
21769      *  '-1' = before
21770      *  '0' = hits..
21771      *  '1' = after.
21772      *         [ -- selected range --- ]
21773      *   [fail]                        [fail]
21774      *
21775      *    basically..
21776      *      if end is before start or  hits it. fail.
21777      *      if start is after end or hits it fail.
21778      *
21779      *   if either hits (but other is outside. - then it's not 
21780      *   
21781      *    
21782      **/
21783     
21784     
21785     // @see http://www.thismuchiknow.co.uk/?p=64.
21786     rangeIntersectsNode : function(range, node)
21787     {
21788         var nodeRange = node.ownerDocument.createRange();
21789         try {
21790             nodeRange.selectNode(node);
21791         } catch (e) {
21792             nodeRange.selectNodeContents(node);
21793         }
21794     
21795         var rangeStartRange = range.cloneRange();
21796         rangeStartRange.collapse(true);
21797     
21798         var rangeEndRange = range.cloneRange();
21799         rangeEndRange.collapse(false);
21800     
21801         var nodeStartRange = nodeRange.cloneRange();
21802         nodeStartRange.collapse(true);
21803     
21804         var nodeEndRange = nodeRange.cloneRange();
21805         nodeEndRange.collapse(false);
21806     
21807         return rangeStartRange.compareBoundaryPoints(
21808                  Range.START_TO_START, nodeEndRange) == -1 &&
21809                rangeEndRange.compareBoundaryPoints(
21810                  Range.START_TO_START, nodeStartRange) == 1;
21811         
21812          
21813     },
21814     rangeCompareNode : function(range, node)
21815     {
21816         var nodeRange = node.ownerDocument.createRange();
21817         try {
21818             nodeRange.selectNode(node);
21819         } catch (e) {
21820             nodeRange.selectNodeContents(node);
21821         }
21822         
21823         
21824         range.collapse(true);
21825     
21826         nodeRange.collapse(true);
21827      
21828         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21829         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21830          
21831         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21832         
21833         var nodeIsBefore   =  ss == 1;
21834         var nodeIsAfter    = ee == -1;
21835         
21836         if (nodeIsBefore && nodeIsAfter) {
21837             return 0; // outer
21838         }
21839         if (!nodeIsBefore && nodeIsAfter) {
21840             return 1; //right trailed.
21841         }
21842         
21843         if (nodeIsBefore && !nodeIsAfter) {
21844             return 2;  // left trailed.
21845         }
21846         // fully contined.
21847         return 3;
21848     },
21849
21850     // private? - in a new class?
21851     cleanUpPaste :  function()
21852     {
21853         // cleans up the whole document..
21854         Roo.log('cleanuppaste');
21855         
21856         this.cleanUpChildren(this.doc.body);
21857         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21858         if (clean != this.doc.body.innerHTML) {
21859             this.doc.body.innerHTML = clean;
21860         }
21861         
21862     },
21863     
21864     cleanWordChars : function(input) {// change the chars to hex code
21865         var he = Roo.HtmlEditorCore;
21866         
21867         var output = input;
21868         Roo.each(he.swapCodes, function(sw) { 
21869             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21870             
21871             output = output.replace(swapper, sw[1]);
21872         });
21873         
21874         return output;
21875     },
21876     
21877     
21878     cleanUpChildren : function (n)
21879     {
21880         if (!n.childNodes.length) {
21881             return;
21882         }
21883         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21884            this.cleanUpChild(n.childNodes[i]);
21885         }
21886     },
21887     
21888     
21889         
21890     
21891     cleanUpChild : function (node)
21892     {
21893         var ed = this;
21894         //console.log(node);
21895         if (node.nodeName == "#text") {
21896             // clean up silly Windows -- stuff?
21897             return; 
21898         }
21899         if (node.nodeName == "#comment") {
21900             node.parentNode.removeChild(node);
21901             // clean up silly Windows -- stuff?
21902             return; 
21903         }
21904         var lcname = node.tagName.toLowerCase();
21905         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21906         // whitelist of tags..
21907         
21908         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21909             // remove node.
21910             node.parentNode.removeChild(node);
21911             return;
21912             
21913         }
21914         
21915         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21916         
21917         // spans with no attributes - just remove them..
21918         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21919             remove_keep_children = true;
21920         }
21921         
21922         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21923         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21924         
21925         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21926         //    remove_keep_children = true;
21927         //}
21928         
21929         if (remove_keep_children) {
21930             this.cleanUpChildren(node);
21931             // inserts everything just before this node...
21932             while (node.childNodes.length) {
21933                 var cn = node.childNodes[0];
21934                 node.removeChild(cn);
21935                 node.parentNode.insertBefore(cn, node);
21936             }
21937             node.parentNode.removeChild(node);
21938             return;
21939         }
21940         
21941         if (!node.attributes || !node.attributes.length) {
21942             
21943           
21944             
21945             
21946             this.cleanUpChildren(node);
21947             return;
21948         }
21949         
21950         function cleanAttr(n,v)
21951         {
21952             
21953             if (v.match(/^\./) || v.match(/^\//)) {
21954                 return;
21955             }
21956             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21957                 return;
21958             }
21959             if (v.match(/^#/)) {
21960                 return;
21961             }
21962 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21963             node.removeAttribute(n);
21964             
21965         }
21966         
21967         var cwhite = this.cwhite;
21968         var cblack = this.cblack;
21969             
21970         function cleanStyle(n,v)
21971         {
21972             if (v.match(/expression/)) { //XSS?? should we even bother..
21973                 node.removeAttribute(n);
21974                 return;
21975             }
21976             
21977             var parts = v.split(/;/);
21978             var clean = [];
21979             
21980             Roo.each(parts, function(p) {
21981                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21982                 if (!p.length) {
21983                     return true;
21984                 }
21985                 var l = p.split(':').shift().replace(/\s+/g,'');
21986                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21987                 
21988                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21989 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21990                     //node.removeAttribute(n);
21991                     return true;
21992                 }
21993                 //Roo.log()
21994                 // only allow 'c whitelisted system attributes'
21995                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21996 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21997                     //node.removeAttribute(n);
21998                     return true;
21999                 }
22000                 
22001                 
22002                  
22003                 
22004                 clean.push(p);
22005                 return true;
22006             });
22007             if (clean.length) { 
22008                 node.setAttribute(n, clean.join(';'));
22009             } else {
22010                 node.removeAttribute(n);
22011             }
22012             
22013         }
22014         
22015         
22016         for (var i = node.attributes.length-1; i > -1 ; i--) {
22017             var a = node.attributes[i];
22018             //console.log(a);
22019             
22020             if (a.name.toLowerCase().substr(0,2)=='on')  {
22021                 node.removeAttribute(a.name);
22022                 continue;
22023             }
22024             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22025                 node.removeAttribute(a.name);
22026                 continue;
22027             }
22028             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22029                 cleanAttr(a.name,a.value); // fixme..
22030                 continue;
22031             }
22032             if (a.name == 'style') {
22033                 cleanStyle(a.name,a.value);
22034                 continue;
22035             }
22036             /// clean up MS crap..
22037             // tecnically this should be a list of valid class'es..
22038             
22039             
22040             if (a.name == 'class') {
22041                 if (a.value.match(/^Mso/)) {
22042                     node.removeAttribute('class');
22043                 }
22044                 
22045                 if (a.value.match(/^body$/)) {
22046                     node.removeAttribute('class');
22047                 }
22048                 continue;
22049             }
22050             
22051             // style cleanup!?
22052             // class cleanup?
22053             
22054         }
22055         
22056         
22057         this.cleanUpChildren(node);
22058         
22059         
22060     },
22061     
22062     /**
22063      * Clean up MS wordisms...
22064      */
22065     cleanWord : function(node)
22066     {
22067         if (!node) {
22068             this.cleanWord(this.doc.body);
22069             return;
22070         }
22071         
22072         if(
22073                 node.nodeName == 'SPAN' &&
22074                 !node.hasAttributes() &&
22075                 node.childNodes.length == 1 &&
22076                 node.firstChild.nodeName == "#text"  
22077         ) {
22078             var textNode = node.firstChild;
22079             node.removeChild(textNode);
22080             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22081                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22082             }
22083             node.parentNode.insertBefore(textNode, node);
22084             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22085                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22086             }
22087             node.parentNode.removeChild(node);
22088         }
22089         
22090         if (node.nodeName == "#text") {
22091             // clean up silly Windows -- stuff?
22092             return; 
22093         }
22094         if (node.nodeName == "#comment") {
22095             node.parentNode.removeChild(node);
22096             // clean up silly Windows -- stuff?
22097             return; 
22098         }
22099         
22100         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22101             node.parentNode.removeChild(node);
22102             return;
22103         }
22104         //Roo.log(node.tagName);
22105         // remove - but keep children..
22106         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22107             //Roo.log('-- removed');
22108             while (node.childNodes.length) {
22109                 var cn = node.childNodes[0];
22110                 node.removeChild(cn);
22111                 node.parentNode.insertBefore(cn, node);
22112                 // move node to parent - and clean it..
22113                 this.cleanWord(cn);
22114             }
22115             node.parentNode.removeChild(node);
22116             /// no need to iterate chidlren = it's got none..
22117             //this.iterateChildren(node, this.cleanWord);
22118             return;
22119         }
22120         // clean styles
22121         if (node.className.length) {
22122             
22123             var cn = node.className.split(/\W+/);
22124             var cna = [];
22125             Roo.each(cn, function(cls) {
22126                 if (cls.match(/Mso[a-zA-Z]+/)) {
22127                     return;
22128                 }
22129                 cna.push(cls);
22130             });
22131             node.className = cna.length ? cna.join(' ') : '';
22132             if (!cna.length) {
22133                 node.removeAttribute("class");
22134             }
22135         }
22136         
22137         if (node.hasAttribute("lang")) {
22138             node.removeAttribute("lang");
22139         }
22140         
22141         if (node.hasAttribute("style")) {
22142             
22143             var styles = node.getAttribute("style").split(";");
22144             var nstyle = [];
22145             Roo.each(styles, function(s) {
22146                 if (!s.match(/:/)) {
22147                     return;
22148                 }
22149                 var kv = s.split(":");
22150                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22151                     return;
22152                 }
22153                 // what ever is left... we allow.
22154                 nstyle.push(s);
22155             });
22156             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22157             if (!nstyle.length) {
22158                 node.removeAttribute('style');
22159             }
22160         }
22161         this.iterateChildren(node, this.cleanWord);
22162         
22163         
22164         
22165     },
22166     /**
22167      * iterateChildren of a Node, calling fn each time, using this as the scole..
22168      * @param {DomNode} node node to iterate children of.
22169      * @param {Function} fn method of this class to call on each item.
22170      */
22171     iterateChildren : function(node, fn)
22172     {
22173         if (!node.childNodes.length) {
22174                 return;
22175         }
22176         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22177            fn.call(this, node.childNodes[i])
22178         }
22179     },
22180     
22181     
22182     /**
22183      * cleanTableWidths.
22184      *
22185      * Quite often pasting from word etc.. results in tables with column and widths.
22186      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22187      *
22188      */
22189     cleanTableWidths : function(node)
22190     {
22191          
22192          
22193         if (!node) {
22194             this.cleanTableWidths(this.doc.body);
22195             return;
22196         }
22197         
22198         // ignore list...
22199         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22200             return; 
22201         }
22202         Roo.log(node.tagName);
22203         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22204             this.iterateChildren(node, this.cleanTableWidths);
22205             return;
22206         }
22207         if (node.hasAttribute('width')) {
22208             node.removeAttribute('width');
22209         }
22210         
22211          
22212         if (node.hasAttribute("style")) {
22213             // pretty basic...
22214             
22215             var styles = node.getAttribute("style").split(";");
22216             var nstyle = [];
22217             Roo.each(styles, function(s) {
22218                 if (!s.match(/:/)) {
22219                     return;
22220                 }
22221                 var kv = s.split(":");
22222                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22223                     return;
22224                 }
22225                 // what ever is left... we allow.
22226                 nstyle.push(s);
22227             });
22228             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22229             if (!nstyle.length) {
22230                 node.removeAttribute('style');
22231             }
22232         }
22233         
22234         this.iterateChildren(node, this.cleanTableWidths);
22235         
22236         
22237     },
22238     
22239     
22240     
22241     
22242     domToHTML : function(currentElement, depth, nopadtext) {
22243         
22244         depth = depth || 0;
22245         nopadtext = nopadtext || false;
22246     
22247         if (!currentElement) {
22248             return this.domToHTML(this.doc.body);
22249         }
22250         
22251         //Roo.log(currentElement);
22252         var j;
22253         var allText = false;
22254         var nodeName = currentElement.nodeName;
22255         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22256         
22257         if  (nodeName == '#text') {
22258             
22259             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22260         }
22261         
22262         
22263         var ret = '';
22264         if (nodeName != 'BODY') {
22265              
22266             var i = 0;
22267             // Prints the node tagName, such as <A>, <IMG>, etc
22268             if (tagName) {
22269                 var attr = [];
22270                 for(i = 0; i < currentElement.attributes.length;i++) {
22271                     // quoting?
22272                     var aname = currentElement.attributes.item(i).name;
22273                     if (!currentElement.attributes.item(i).value.length) {
22274                         continue;
22275                     }
22276                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22277                 }
22278                 
22279                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22280             } 
22281             else {
22282                 
22283                 // eack
22284             }
22285         } else {
22286             tagName = false;
22287         }
22288         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22289             return ret;
22290         }
22291         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22292             nopadtext = true;
22293         }
22294         
22295         
22296         // Traverse the tree
22297         i = 0;
22298         var currentElementChild = currentElement.childNodes.item(i);
22299         var allText = true;
22300         var innerHTML  = '';
22301         lastnode = '';
22302         while (currentElementChild) {
22303             // Formatting code (indent the tree so it looks nice on the screen)
22304             var nopad = nopadtext;
22305             if (lastnode == 'SPAN') {
22306                 nopad  = true;
22307             }
22308             // text
22309             if  (currentElementChild.nodeName == '#text') {
22310                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22311                 toadd = nopadtext ? toadd : toadd.trim();
22312                 if (!nopad && toadd.length > 80) {
22313                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22314                 }
22315                 innerHTML  += toadd;
22316                 
22317                 i++;
22318                 currentElementChild = currentElement.childNodes.item(i);
22319                 lastNode = '';
22320                 continue;
22321             }
22322             allText = false;
22323             
22324             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22325                 
22326             // Recursively traverse the tree structure of the child node
22327             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22328             lastnode = currentElementChild.nodeName;
22329             i++;
22330             currentElementChild=currentElement.childNodes.item(i);
22331         }
22332         
22333         ret += innerHTML;
22334         
22335         if (!allText) {
22336                 // The remaining code is mostly for formatting the tree
22337             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22338         }
22339         
22340         
22341         if (tagName) {
22342             ret+= "</"+tagName+">";
22343         }
22344         return ret;
22345         
22346     },
22347         
22348     applyBlacklists : function()
22349     {
22350         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22351         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22352         
22353         this.white = [];
22354         this.black = [];
22355         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22356             if (b.indexOf(tag) > -1) {
22357                 return;
22358             }
22359             this.white.push(tag);
22360             
22361         }, this);
22362         
22363         Roo.each(w, function(tag) {
22364             if (b.indexOf(tag) > -1) {
22365                 return;
22366             }
22367             if (this.white.indexOf(tag) > -1) {
22368                 return;
22369             }
22370             this.white.push(tag);
22371             
22372         }, this);
22373         
22374         
22375         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22376             if (w.indexOf(tag) > -1) {
22377                 return;
22378             }
22379             this.black.push(tag);
22380             
22381         }, this);
22382         
22383         Roo.each(b, function(tag) {
22384             if (w.indexOf(tag) > -1) {
22385                 return;
22386             }
22387             if (this.black.indexOf(tag) > -1) {
22388                 return;
22389             }
22390             this.black.push(tag);
22391             
22392         }, this);
22393         
22394         
22395         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22396         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22397         
22398         this.cwhite = [];
22399         this.cblack = [];
22400         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22401             if (b.indexOf(tag) > -1) {
22402                 return;
22403             }
22404             this.cwhite.push(tag);
22405             
22406         }, this);
22407         
22408         Roo.each(w, function(tag) {
22409             if (b.indexOf(tag) > -1) {
22410                 return;
22411             }
22412             if (this.cwhite.indexOf(tag) > -1) {
22413                 return;
22414             }
22415             this.cwhite.push(tag);
22416             
22417         }, this);
22418         
22419         
22420         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22421             if (w.indexOf(tag) > -1) {
22422                 return;
22423             }
22424             this.cblack.push(tag);
22425             
22426         }, this);
22427         
22428         Roo.each(b, function(tag) {
22429             if (w.indexOf(tag) > -1) {
22430                 return;
22431             }
22432             if (this.cblack.indexOf(tag) > -1) {
22433                 return;
22434             }
22435             this.cblack.push(tag);
22436             
22437         }, this);
22438     },
22439     
22440     setStylesheets : function(stylesheets)
22441     {
22442         if(typeof(stylesheets) == 'string'){
22443             Roo.get(this.iframe.contentDocument.head).createChild({
22444                 tag : 'link',
22445                 rel : 'stylesheet',
22446                 type : 'text/css',
22447                 href : stylesheets
22448             });
22449             
22450             return;
22451         }
22452         var _this = this;
22453      
22454         Roo.each(stylesheets, function(s) {
22455             if(!s.length){
22456                 return;
22457             }
22458             
22459             Roo.get(_this.iframe.contentDocument.head).createChild({
22460                 tag : 'link',
22461                 rel : 'stylesheet',
22462                 type : 'text/css',
22463                 href : s
22464             });
22465         });
22466
22467         
22468     },
22469     
22470     removeStylesheets : function()
22471     {
22472         var _this = this;
22473         
22474         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22475             s.remove();
22476         });
22477     },
22478     
22479     setStyle : function(style)
22480     {
22481         Roo.get(this.iframe.contentDocument.head).createChild({
22482             tag : 'style',
22483             type : 'text/css',
22484             html : style
22485         });
22486
22487         return;
22488     }
22489     
22490     // hide stuff that is not compatible
22491     /**
22492      * @event blur
22493      * @hide
22494      */
22495     /**
22496      * @event change
22497      * @hide
22498      */
22499     /**
22500      * @event focus
22501      * @hide
22502      */
22503     /**
22504      * @event specialkey
22505      * @hide
22506      */
22507     /**
22508      * @cfg {String} fieldClass @hide
22509      */
22510     /**
22511      * @cfg {String} focusClass @hide
22512      */
22513     /**
22514      * @cfg {String} autoCreate @hide
22515      */
22516     /**
22517      * @cfg {String} inputType @hide
22518      */
22519     /**
22520      * @cfg {String} invalidClass @hide
22521      */
22522     /**
22523      * @cfg {String} invalidText @hide
22524      */
22525     /**
22526      * @cfg {String} msgFx @hide
22527      */
22528     /**
22529      * @cfg {String} validateOnBlur @hide
22530      */
22531 });
22532
22533 Roo.HtmlEditorCore.white = [
22534         'area', 'br', 'img', 'input', 'hr', 'wbr',
22535         
22536        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22537        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22538        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22539        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22540        'table',   'ul',         'xmp', 
22541        
22542        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22543       'thead',   'tr', 
22544      
22545       'dir', 'menu', 'ol', 'ul', 'dl',
22546        
22547       'embed',  'object'
22548 ];
22549
22550
22551 Roo.HtmlEditorCore.black = [
22552     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22553         'applet', // 
22554         'base',   'basefont', 'bgsound', 'blink',  'body', 
22555         'frame',  'frameset', 'head',    'html',   'ilayer', 
22556         'iframe', 'layer',  'link',     'meta',    'object',   
22557         'script', 'style' ,'title',  'xml' // clean later..
22558 ];
22559 Roo.HtmlEditorCore.clean = [
22560     'script', 'style', 'title', 'xml'
22561 ];
22562 Roo.HtmlEditorCore.remove = [
22563     'font'
22564 ];
22565 // attributes..
22566
22567 Roo.HtmlEditorCore.ablack = [
22568     'on'
22569 ];
22570     
22571 Roo.HtmlEditorCore.aclean = [ 
22572     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22573 ];
22574
22575 // protocols..
22576 Roo.HtmlEditorCore.pwhite= [
22577         'http',  'https',  'mailto'
22578 ];
22579
22580 // white listed style attributes.
22581 Roo.HtmlEditorCore.cwhite= [
22582       //  'text-align', /// default is to allow most things..
22583       
22584          
22585 //        'font-size'//??
22586 ];
22587
22588 // black listed style attributes.
22589 Roo.HtmlEditorCore.cblack= [
22590       //  'font-size' -- this can be set by the project 
22591 ];
22592
22593
22594 Roo.HtmlEditorCore.swapCodes   =[ 
22595     [    8211, "--" ], 
22596     [    8212, "--" ], 
22597     [    8216,  "'" ],  
22598     [    8217, "'" ],  
22599     [    8220, '"' ],  
22600     [    8221, '"' ],  
22601     [    8226, "*" ],  
22602     [    8230, "..." ]
22603 ]; 
22604
22605     //<script type="text/javascript">
22606
22607 /*
22608  * Ext JS Library 1.1.1
22609  * Copyright(c) 2006-2007, Ext JS, LLC.
22610  * Licence LGPL
22611  * 
22612  */
22613  
22614  
22615 Roo.form.HtmlEditor = function(config){
22616     
22617     
22618     
22619     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22620     
22621     if (!this.toolbars) {
22622         this.toolbars = [];
22623     }
22624     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22625     
22626     
22627 };
22628
22629 /**
22630  * @class Roo.form.HtmlEditor
22631  * @extends Roo.form.Field
22632  * Provides a lightweight HTML Editor component.
22633  *
22634  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22635  * 
22636  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22637  * supported by this editor.</b><br/><br/>
22638  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22639  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22640  */
22641 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22642     /**
22643      * @cfg {Boolean} clearUp
22644      */
22645     clearUp : true,
22646       /**
22647      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22648      */
22649     toolbars : false,
22650    
22651      /**
22652      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22653      *                        Roo.resizable.
22654      */
22655     resizable : false,
22656      /**
22657      * @cfg {Number} height (in pixels)
22658      */   
22659     height: 300,
22660    /**
22661      * @cfg {Number} width (in pixels)
22662      */   
22663     width: 500,
22664     
22665     /**
22666      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22667      * 
22668      */
22669     stylesheets: false,
22670     
22671     
22672      /**
22673      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22674      * 
22675      */
22676     cblack: false,
22677     /**
22678      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22679      * 
22680      */
22681     cwhite: false,
22682     
22683      /**
22684      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22685      * 
22686      */
22687     black: false,
22688     /**
22689      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22690      * 
22691      */
22692     white: false,
22693     
22694     // id of frame..
22695     frameId: false,
22696     
22697     // private properties
22698     validationEvent : false,
22699     deferHeight: true,
22700     initialized : false,
22701     activated : false,
22702     
22703     onFocus : Roo.emptyFn,
22704     iframePad:3,
22705     hideMode:'offsets',
22706     
22707     actionMode : 'container', // defaults to hiding it...
22708     
22709     defaultAutoCreate : { // modified by initCompnoent..
22710         tag: "textarea",
22711         style:"width:500px;height:300px;",
22712         autocomplete: "new-password"
22713     },
22714
22715     // private
22716     initComponent : function(){
22717         this.addEvents({
22718             /**
22719              * @event initialize
22720              * Fires when the editor is fully initialized (including the iframe)
22721              * @param {HtmlEditor} this
22722              */
22723             initialize: true,
22724             /**
22725              * @event activate
22726              * Fires when the editor is first receives the focus. Any insertion must wait
22727              * until after this event.
22728              * @param {HtmlEditor} this
22729              */
22730             activate: true,
22731              /**
22732              * @event beforesync
22733              * Fires before the textarea is updated with content from the editor iframe. Return false
22734              * to cancel the sync.
22735              * @param {HtmlEditor} this
22736              * @param {String} html
22737              */
22738             beforesync: true,
22739              /**
22740              * @event beforepush
22741              * Fires before the iframe editor is updated with content from the textarea. Return false
22742              * to cancel the push.
22743              * @param {HtmlEditor} this
22744              * @param {String} html
22745              */
22746             beforepush: true,
22747              /**
22748              * @event sync
22749              * Fires when the textarea is updated with content from the editor iframe.
22750              * @param {HtmlEditor} this
22751              * @param {String} html
22752              */
22753             sync: true,
22754              /**
22755              * @event push
22756              * Fires when the iframe editor is updated with content from the textarea.
22757              * @param {HtmlEditor} this
22758              * @param {String} html
22759              */
22760             push: true,
22761              /**
22762              * @event editmodechange
22763              * Fires when the editor switches edit modes
22764              * @param {HtmlEditor} this
22765              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22766              */
22767             editmodechange: true,
22768             /**
22769              * @event editorevent
22770              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22771              * @param {HtmlEditor} this
22772              */
22773             editorevent: true,
22774             /**
22775              * @event firstfocus
22776              * Fires when on first focus - needed by toolbars..
22777              * @param {HtmlEditor} this
22778              */
22779             firstfocus: true,
22780             /**
22781              * @event autosave
22782              * Auto save the htmlEditor value as a file into Events
22783              * @param {HtmlEditor} this
22784              */
22785             autosave: true,
22786             /**
22787              * @event savedpreview
22788              * preview the saved version of htmlEditor
22789              * @param {HtmlEditor} this
22790              */
22791             savedpreview: true,
22792             
22793             /**
22794             * @event stylesheetsclick
22795             * Fires when press the Sytlesheets button
22796             * @param {Roo.HtmlEditorCore} this
22797             */
22798             stylesheetsclick: true
22799         });
22800         this.defaultAutoCreate =  {
22801             tag: "textarea",
22802             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22803             autocomplete: "new-password"
22804         };
22805     },
22806
22807     /**
22808      * Protected method that will not generally be called directly. It
22809      * is called when the editor creates its toolbar. Override this method if you need to
22810      * add custom toolbar buttons.
22811      * @param {HtmlEditor} editor
22812      */
22813     createToolbar : function(editor){
22814         Roo.log("create toolbars");
22815         if (!editor.toolbars || !editor.toolbars.length) {
22816             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22817         }
22818         
22819         for (var i =0 ; i < editor.toolbars.length;i++) {
22820             editor.toolbars[i] = Roo.factory(
22821                     typeof(editor.toolbars[i]) == 'string' ?
22822                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22823                 Roo.form.HtmlEditor);
22824             editor.toolbars[i].init(editor);
22825         }
22826          
22827         
22828     },
22829
22830      
22831     // private
22832     onRender : function(ct, position)
22833     {
22834         var _t = this;
22835         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22836         
22837         this.wrap = this.el.wrap({
22838             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22839         });
22840         
22841         this.editorcore.onRender(ct, position);
22842          
22843         if (this.resizable) {
22844             this.resizeEl = new Roo.Resizable(this.wrap, {
22845                 pinned : true,
22846                 wrap: true,
22847                 dynamic : true,
22848                 minHeight : this.height,
22849                 height: this.height,
22850                 handles : this.resizable,
22851                 width: this.width,
22852                 listeners : {
22853                     resize : function(r, w, h) {
22854                         _t.onResize(w,h); // -something
22855                     }
22856                 }
22857             });
22858             
22859         }
22860         this.createToolbar(this);
22861        
22862         
22863         if(!this.width){
22864             this.setSize(this.wrap.getSize());
22865         }
22866         if (this.resizeEl) {
22867             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22868             // should trigger onReize..
22869         }
22870         
22871         this.keyNav = new Roo.KeyNav(this.el, {
22872             
22873             "tab" : function(e){
22874                 e.preventDefault();
22875                 
22876                 var value = this.getValue();
22877                 
22878                 var start = this.el.dom.selectionStart;
22879                 var end = this.el.dom.selectionEnd;
22880                 
22881                 if(!e.shiftKey){
22882                     
22883                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22884                     this.el.dom.setSelectionRange(end + 1, end + 1);
22885                     return;
22886                 }
22887                 
22888                 var f = value.substring(0, start).split("\t");
22889                 
22890                 if(f.pop().length != 0){
22891                     return;
22892                 }
22893                 
22894                 this.setValue(f.join("\t") + value.substring(end));
22895                 this.el.dom.setSelectionRange(start - 1, start - 1);
22896                 
22897             },
22898             
22899             "home" : function(e){
22900                 e.preventDefault();
22901                 
22902                 var curr = this.el.dom.selectionStart;
22903                 var lines = this.getValue().split("\n");
22904                 
22905                 if(!lines.length){
22906                     return;
22907                 }
22908                 
22909                 if(e.ctrlKey){
22910                     this.el.dom.setSelectionRange(0, 0);
22911                     return;
22912                 }
22913                 
22914                 var pos = 0;
22915                 
22916                 for (var i = 0; i < lines.length;i++) {
22917                     pos += lines[i].length;
22918                     
22919                     if(i != 0){
22920                         pos += 1;
22921                     }
22922                     
22923                     if(pos < curr){
22924                         continue;
22925                     }
22926                     
22927                     pos -= lines[i].length;
22928                     
22929                     break;
22930                 }
22931                 
22932                 if(!e.shiftKey){
22933                     this.el.dom.setSelectionRange(pos, pos);
22934                     return;
22935                 }
22936                 
22937                 this.el.dom.selectionStart = pos;
22938                 this.el.dom.selectionEnd = curr;
22939             },
22940             
22941             "end" : function(e){
22942                 e.preventDefault();
22943                 
22944                 var curr = this.el.dom.selectionStart;
22945                 var lines = this.getValue().split("\n");
22946                 
22947                 if(!lines.length){
22948                     return;
22949                 }
22950                 
22951                 if(e.ctrlKey){
22952                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22953                     return;
22954                 }
22955                 
22956                 var pos = 0;
22957                 
22958                 for (var i = 0; i < lines.length;i++) {
22959                     
22960                     pos += lines[i].length;
22961                     
22962                     if(i != 0){
22963                         pos += 1;
22964                     }
22965                     
22966                     if(pos < curr){
22967                         continue;
22968                     }
22969                     
22970                     break;
22971                 }
22972                 
22973                 if(!e.shiftKey){
22974                     this.el.dom.setSelectionRange(pos, pos);
22975                     return;
22976                 }
22977                 
22978                 this.el.dom.selectionStart = curr;
22979                 this.el.dom.selectionEnd = pos;
22980             },
22981
22982             scope : this,
22983
22984             doRelay : function(foo, bar, hname){
22985                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22986             },
22987
22988             forceKeyDown: true
22989         });
22990         
22991 //        if(this.autosave && this.w){
22992 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22993 //        }
22994     },
22995
22996     // private
22997     onResize : function(w, h)
22998     {
22999         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23000         var ew = false;
23001         var eh = false;
23002         
23003         if(this.el ){
23004             if(typeof w == 'number'){
23005                 var aw = w - this.wrap.getFrameWidth('lr');
23006                 this.el.setWidth(this.adjustWidth('textarea', aw));
23007                 ew = aw;
23008             }
23009             if(typeof h == 'number'){
23010                 var tbh = 0;
23011                 for (var i =0; i < this.toolbars.length;i++) {
23012                     // fixme - ask toolbars for heights?
23013                     tbh += this.toolbars[i].tb.el.getHeight();
23014                     if (this.toolbars[i].footer) {
23015                         tbh += this.toolbars[i].footer.el.getHeight();
23016                     }
23017                 }
23018                 
23019                 
23020                 
23021                 
23022                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23023                 ah -= 5; // knock a few pixes off for look..
23024 //                Roo.log(ah);
23025                 this.el.setHeight(this.adjustWidth('textarea', ah));
23026                 var eh = ah;
23027             }
23028         }
23029         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23030         this.editorcore.onResize(ew,eh);
23031         
23032     },
23033
23034     /**
23035      * Toggles the editor between standard and source edit mode.
23036      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23037      */
23038     toggleSourceEdit : function(sourceEditMode)
23039     {
23040         this.editorcore.toggleSourceEdit(sourceEditMode);
23041         
23042         if(this.editorcore.sourceEditMode){
23043             Roo.log('editor - showing textarea');
23044             
23045 //            Roo.log('in');
23046 //            Roo.log(this.syncValue());
23047             this.editorcore.syncValue();
23048             this.el.removeClass('x-hidden');
23049             this.el.dom.removeAttribute('tabIndex');
23050             this.el.focus();
23051             
23052             for (var i = 0; i < this.toolbars.length; i++) {
23053                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23054                     this.toolbars[i].tb.hide();
23055                     this.toolbars[i].footer.hide();
23056                 }
23057             }
23058             
23059         }else{
23060             Roo.log('editor - hiding textarea');
23061 //            Roo.log('out')
23062 //            Roo.log(this.pushValue()); 
23063             this.editorcore.pushValue();
23064             
23065             this.el.addClass('x-hidden');
23066             this.el.dom.setAttribute('tabIndex', -1);
23067             
23068             for (var i = 0; i < this.toolbars.length; i++) {
23069                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23070                     this.toolbars[i].tb.show();
23071                     this.toolbars[i].footer.show();
23072                 }
23073             }
23074             
23075             //this.deferFocus();
23076         }
23077         
23078         this.setSize(this.wrap.getSize());
23079         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23080         
23081         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23082     },
23083  
23084     // private (for BoxComponent)
23085     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23086
23087     // private (for BoxComponent)
23088     getResizeEl : function(){
23089         return this.wrap;
23090     },
23091
23092     // private (for BoxComponent)
23093     getPositionEl : function(){
23094         return this.wrap;
23095     },
23096
23097     // private
23098     initEvents : function(){
23099         this.originalValue = this.getValue();
23100     },
23101
23102     /**
23103      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23104      * @method
23105      */
23106     markInvalid : Roo.emptyFn,
23107     /**
23108      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23109      * @method
23110      */
23111     clearInvalid : Roo.emptyFn,
23112
23113     setValue : function(v){
23114         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23115         this.editorcore.pushValue();
23116     },
23117
23118      
23119     // private
23120     deferFocus : function(){
23121         this.focus.defer(10, this);
23122     },
23123
23124     // doc'ed in Field
23125     focus : function(){
23126         this.editorcore.focus();
23127         
23128     },
23129       
23130
23131     // private
23132     onDestroy : function(){
23133         
23134         
23135         
23136         if(this.rendered){
23137             
23138             for (var i =0; i < this.toolbars.length;i++) {
23139                 // fixme - ask toolbars for heights?
23140                 this.toolbars[i].onDestroy();
23141             }
23142             
23143             this.wrap.dom.innerHTML = '';
23144             this.wrap.remove();
23145         }
23146     },
23147
23148     // private
23149     onFirstFocus : function(){
23150         //Roo.log("onFirstFocus");
23151         this.editorcore.onFirstFocus();
23152          for (var i =0; i < this.toolbars.length;i++) {
23153             this.toolbars[i].onFirstFocus();
23154         }
23155         
23156     },
23157     
23158     // private
23159     syncValue : function()
23160     {
23161         this.editorcore.syncValue();
23162     },
23163     
23164     pushValue : function()
23165     {
23166         this.editorcore.pushValue();
23167     },
23168     
23169     setStylesheets : function(stylesheets)
23170     {
23171         this.editorcore.setStylesheets(stylesheets);
23172     },
23173     
23174     removeStylesheets : function()
23175     {
23176         this.editorcore.removeStylesheets();
23177     }
23178      
23179     
23180     // hide stuff that is not compatible
23181     /**
23182      * @event blur
23183      * @hide
23184      */
23185     /**
23186      * @event change
23187      * @hide
23188      */
23189     /**
23190      * @event focus
23191      * @hide
23192      */
23193     /**
23194      * @event specialkey
23195      * @hide
23196      */
23197     /**
23198      * @cfg {String} fieldClass @hide
23199      */
23200     /**
23201      * @cfg {String} focusClass @hide
23202      */
23203     /**
23204      * @cfg {String} autoCreate @hide
23205      */
23206     /**
23207      * @cfg {String} inputType @hide
23208      */
23209     /**
23210      * @cfg {String} invalidClass @hide
23211      */
23212     /**
23213      * @cfg {String} invalidText @hide
23214      */
23215     /**
23216      * @cfg {String} msgFx @hide
23217      */
23218     /**
23219      * @cfg {String} validateOnBlur @hide
23220      */
23221 });
23222  
23223     // <script type="text/javascript">
23224 /*
23225  * Based on
23226  * Ext JS Library 1.1.1
23227  * Copyright(c) 2006-2007, Ext JS, LLC.
23228  *  
23229  
23230  */
23231
23232 /**
23233  * @class Roo.form.HtmlEditorToolbar1
23234  * Basic Toolbar
23235  * 
23236  * Usage:
23237  *
23238  new Roo.form.HtmlEditor({
23239     ....
23240     toolbars : [
23241         new Roo.form.HtmlEditorToolbar1({
23242             disable : { fonts: 1 , format: 1, ..., ... , ...],
23243             btns : [ .... ]
23244         })
23245     }
23246      
23247  * 
23248  * @cfg {Object} disable List of elements to disable..
23249  * @cfg {Array} btns List of additional buttons.
23250  * 
23251  * 
23252  * NEEDS Extra CSS? 
23253  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23254  */
23255  
23256 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23257 {
23258     
23259     Roo.apply(this, config);
23260     
23261     // default disabled, based on 'good practice'..
23262     this.disable = this.disable || {};
23263     Roo.applyIf(this.disable, {
23264         fontSize : true,
23265         colors : true,
23266         specialElements : true
23267     });
23268     
23269     
23270     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23271     // dont call parent... till later.
23272 }
23273
23274 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23275     
23276     tb: false,
23277     
23278     rendered: false,
23279     
23280     editor : false,
23281     editorcore : false,
23282     /**
23283      * @cfg {Object} disable  List of toolbar elements to disable
23284          
23285      */
23286     disable : false,
23287     
23288     
23289      /**
23290      * @cfg {String} createLinkText The default text for the create link prompt
23291      */
23292     createLinkText : 'Please enter the URL for the link:',
23293     /**
23294      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23295      */
23296     defaultLinkValue : 'http:/'+'/',
23297    
23298     
23299       /**
23300      * @cfg {Array} fontFamilies An array of available font families
23301      */
23302     fontFamilies : [
23303         'Arial',
23304         'Courier New',
23305         'Tahoma',
23306         'Times New Roman',
23307         'Verdana'
23308     ],
23309     
23310     specialChars : [
23311            "&#169;",
23312           "&#174;",     
23313           "&#8482;",    
23314           "&#163;" ,    
23315          // "&#8212;",    
23316           "&#8230;",    
23317           "&#247;" ,    
23318         //  "&#225;" ,     ?? a acute?
23319            "&#8364;"    , //Euro
23320        //   "&#8220;"    ,
23321         //  "&#8221;"    ,
23322         //  "&#8226;"    ,
23323           "&#176;"  //   , // degrees
23324
23325          // "&#233;"     , // e ecute
23326          // "&#250;"     , // u ecute?
23327     ],
23328     
23329     specialElements : [
23330         {
23331             text: "Insert Table",
23332             xtype: 'MenuItem',
23333             xns : Roo.Menu,
23334             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23335                 
23336         },
23337         {    
23338             text: "Insert Image",
23339             xtype: 'MenuItem',
23340             xns : Roo.Menu,
23341             ihtml : '<img src="about:blank"/>'
23342             
23343         }
23344         
23345          
23346     ],
23347     
23348     
23349     inputElements : [ 
23350             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23351             "input:submit", "input:button", "select", "textarea", "label" ],
23352     formats : [
23353         ["p"] ,  
23354         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23355         ["pre"],[ "code"], 
23356         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23357         ['div'],['span'],
23358         ['sup'],['sub']
23359     ],
23360     
23361     cleanStyles : [
23362         "font-size"
23363     ],
23364      /**
23365      * @cfg {String} defaultFont default font to use.
23366      */
23367     defaultFont: 'tahoma',
23368    
23369     fontSelect : false,
23370     
23371     
23372     formatCombo : false,
23373     
23374     init : function(editor)
23375     {
23376         this.editor = editor;
23377         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23378         var editorcore = this.editorcore;
23379         
23380         var _t = this;
23381         
23382         var fid = editorcore.frameId;
23383         var etb = this;
23384         function btn(id, toggle, handler){
23385             var xid = fid + '-'+ id ;
23386             return {
23387                 id : xid,
23388                 cmd : id,
23389                 cls : 'x-btn-icon x-edit-'+id,
23390                 enableToggle:toggle !== false,
23391                 scope: _t, // was editor...
23392                 handler:handler||_t.relayBtnCmd,
23393                 clickEvent:'mousedown',
23394                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23395                 tabIndex:-1
23396             };
23397         }
23398         
23399         
23400         
23401         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23402         this.tb = tb;
23403          // stop form submits
23404         tb.el.on('click', function(e){
23405             e.preventDefault(); // what does this do?
23406         });
23407
23408         if(!this.disable.font) { // && !Roo.isSafari){
23409             /* why no safari for fonts 
23410             editor.fontSelect = tb.el.createChild({
23411                 tag:'select',
23412                 tabIndex: -1,
23413                 cls:'x-font-select',
23414                 html: this.createFontOptions()
23415             });
23416             
23417             editor.fontSelect.on('change', function(){
23418                 var font = editor.fontSelect.dom.value;
23419                 editor.relayCmd('fontname', font);
23420                 editor.deferFocus();
23421             }, editor);
23422             
23423             tb.add(
23424                 editor.fontSelect.dom,
23425                 '-'
23426             );
23427             */
23428             
23429         };
23430         if(!this.disable.formats){
23431             this.formatCombo = new Roo.form.ComboBox({
23432                 store: new Roo.data.SimpleStore({
23433                     id : 'tag',
23434                     fields: ['tag'],
23435                     data : this.formats // from states.js
23436                 }),
23437                 blockFocus : true,
23438                 name : '',
23439                 //autoCreate : {tag: "div",  size: "20"},
23440                 displayField:'tag',
23441                 typeAhead: false,
23442                 mode: 'local',
23443                 editable : false,
23444                 triggerAction: 'all',
23445                 emptyText:'Add tag',
23446                 selectOnFocus:true,
23447                 width:135,
23448                 listeners : {
23449                     'select': function(c, r, i) {
23450                         editorcore.insertTag(r.get('tag'));
23451                         editor.focus();
23452                     }
23453                 }
23454
23455             });
23456             tb.addField(this.formatCombo);
23457             
23458         }
23459         
23460         if(!this.disable.format){
23461             tb.add(
23462                 btn('bold'),
23463                 btn('italic'),
23464                 btn('underline'),
23465                 btn('strikethrough')
23466             );
23467         };
23468         if(!this.disable.fontSize){
23469             tb.add(
23470                 '-',
23471                 
23472                 
23473                 btn('increasefontsize', false, editorcore.adjustFont),
23474                 btn('decreasefontsize', false, editorcore.adjustFont)
23475             );
23476         };
23477         
23478         
23479         if(!this.disable.colors){
23480             tb.add(
23481                 '-', {
23482                     id:editorcore.frameId +'-forecolor',
23483                     cls:'x-btn-icon x-edit-forecolor',
23484                     clickEvent:'mousedown',
23485                     tooltip: this.buttonTips['forecolor'] || undefined,
23486                     tabIndex:-1,
23487                     menu : new Roo.menu.ColorMenu({
23488                         allowReselect: true,
23489                         focus: Roo.emptyFn,
23490                         value:'000000',
23491                         plain:true,
23492                         selectHandler: function(cp, color){
23493                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23494                             editor.deferFocus();
23495                         },
23496                         scope: editorcore,
23497                         clickEvent:'mousedown'
23498                     })
23499                 }, {
23500                     id:editorcore.frameId +'backcolor',
23501                     cls:'x-btn-icon x-edit-backcolor',
23502                     clickEvent:'mousedown',
23503                     tooltip: this.buttonTips['backcolor'] || undefined,
23504                     tabIndex:-1,
23505                     menu : new Roo.menu.ColorMenu({
23506                         focus: Roo.emptyFn,
23507                         value:'FFFFFF',
23508                         plain:true,
23509                         allowReselect: true,
23510                         selectHandler: function(cp, color){
23511                             if(Roo.isGecko){
23512                                 editorcore.execCmd('useCSS', false);
23513                                 editorcore.execCmd('hilitecolor', color);
23514                                 editorcore.execCmd('useCSS', true);
23515                                 editor.deferFocus();
23516                             }else{
23517                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23518                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23519                                 editor.deferFocus();
23520                             }
23521                         },
23522                         scope:editorcore,
23523                         clickEvent:'mousedown'
23524                     })
23525                 }
23526             );
23527         };
23528         // now add all the items...
23529         
23530
23531         if(!this.disable.alignments){
23532             tb.add(
23533                 '-',
23534                 btn('justifyleft'),
23535                 btn('justifycenter'),
23536                 btn('justifyright')
23537             );
23538         };
23539
23540         //if(!Roo.isSafari){
23541             if(!this.disable.links){
23542                 tb.add(
23543                     '-',
23544                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23545                 );
23546             };
23547
23548             if(!this.disable.lists){
23549                 tb.add(
23550                     '-',
23551                     btn('insertorderedlist'),
23552                     btn('insertunorderedlist')
23553                 );
23554             }
23555             if(!this.disable.sourceEdit){
23556                 tb.add(
23557                     '-',
23558                     btn('sourceedit', true, function(btn){
23559                         this.toggleSourceEdit(btn.pressed);
23560                     })
23561                 );
23562             }
23563         //}
23564         
23565         var smenu = { };
23566         // special menu.. - needs to be tidied up..
23567         if (!this.disable.special) {
23568             smenu = {
23569                 text: "&#169;",
23570                 cls: 'x-edit-none',
23571                 
23572                 menu : {
23573                     items : []
23574                 }
23575             };
23576             for (var i =0; i < this.specialChars.length; i++) {
23577                 smenu.menu.items.push({
23578                     
23579                     html: this.specialChars[i],
23580                     handler: function(a,b) {
23581                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23582                         //editor.insertAtCursor(a.html);
23583                         
23584                     },
23585                     tabIndex:-1
23586                 });
23587             }
23588             
23589             
23590             tb.add(smenu);
23591             
23592             
23593         }
23594         
23595         var cmenu = { };
23596         if (!this.disable.cleanStyles) {
23597             cmenu = {
23598                 cls: 'x-btn-icon x-btn-clear',
23599                 
23600                 menu : {
23601                     items : []
23602                 }
23603             };
23604             for (var i =0; i < this.cleanStyles.length; i++) {
23605                 cmenu.menu.items.push({
23606                     actiontype : this.cleanStyles[i],
23607                     html: 'Remove ' + this.cleanStyles[i],
23608                     handler: function(a,b) {
23609 //                        Roo.log(a);
23610 //                        Roo.log(b);
23611                         var c = Roo.get(editorcore.doc.body);
23612                         c.select('[style]').each(function(s) {
23613                             s.dom.style.removeProperty(a.actiontype);
23614                         });
23615                         editorcore.syncValue();
23616                     },
23617                     tabIndex:-1
23618                 });
23619             }
23620              cmenu.menu.items.push({
23621                 actiontype : 'tablewidths',
23622                 html: 'Remove Table Widths',
23623                 handler: function(a,b) {
23624                     editorcore.cleanTableWidths();
23625                     editorcore.syncValue();
23626                 },
23627                 tabIndex:-1
23628             });
23629             cmenu.menu.items.push({
23630                 actiontype : 'word',
23631                 html: 'Remove MS Word Formating',
23632                 handler: function(a,b) {
23633                     editorcore.cleanWord();
23634                     editorcore.syncValue();
23635                 },
23636                 tabIndex:-1
23637             });
23638             
23639             cmenu.menu.items.push({
23640                 actiontype : 'all',
23641                 html: 'Remove All Styles',
23642                 handler: function(a,b) {
23643                     
23644                     var c = Roo.get(editorcore.doc.body);
23645                     c.select('[style]').each(function(s) {
23646                         s.dom.removeAttribute('style');
23647                     });
23648                     editorcore.syncValue();
23649                 },
23650                 tabIndex:-1
23651             });
23652             
23653             cmenu.menu.items.push({
23654                 actiontype : 'all',
23655                 html: 'Remove All CSS Classes',
23656                 handler: function(a,b) {
23657                     
23658                     var c = Roo.get(editorcore.doc.body);
23659                     c.select('[class]').each(function(s) {
23660                         s.dom.removeAttribute('class');
23661                     });
23662                     editorcore.cleanWord();
23663                     editorcore.syncValue();
23664                 },
23665                 tabIndex:-1
23666             });
23667             
23668              cmenu.menu.items.push({
23669                 actiontype : 'tidy',
23670                 html: 'Tidy HTML Source',
23671                 handler: function(a,b) {
23672                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23673                     editorcore.syncValue();
23674                 },
23675                 tabIndex:-1
23676             });
23677             
23678             
23679             tb.add(cmenu);
23680         }
23681          
23682         if (!this.disable.specialElements) {
23683             var semenu = {
23684                 text: "Other;",
23685                 cls: 'x-edit-none',
23686                 menu : {
23687                     items : []
23688                 }
23689             };
23690             for (var i =0; i < this.specialElements.length; i++) {
23691                 semenu.menu.items.push(
23692                     Roo.apply({ 
23693                         handler: function(a,b) {
23694                             editor.insertAtCursor(this.ihtml);
23695                         }
23696                     }, this.specialElements[i])
23697                 );
23698                     
23699             }
23700             
23701             tb.add(semenu);
23702             
23703             
23704         }
23705          
23706         
23707         if (this.btns) {
23708             for(var i =0; i< this.btns.length;i++) {
23709                 var b = Roo.factory(this.btns[i],Roo.form);
23710                 b.cls =  'x-edit-none';
23711                 
23712                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23713                     b.cls += ' x-init-enable';
23714                 }
23715                 
23716                 b.scope = editorcore;
23717                 tb.add(b);
23718             }
23719         
23720         }
23721         
23722         
23723         
23724         // disable everything...
23725         
23726         this.tb.items.each(function(item){
23727             
23728            if(
23729                 item.id != editorcore.frameId+ '-sourceedit' && 
23730                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23731             ){
23732                 
23733                 item.disable();
23734             }
23735         });
23736         this.rendered = true;
23737         
23738         // the all the btns;
23739         editor.on('editorevent', this.updateToolbar, this);
23740         // other toolbars need to implement this..
23741         //editor.on('editmodechange', this.updateToolbar, this);
23742     },
23743     
23744     
23745     relayBtnCmd : function(btn) {
23746         this.editorcore.relayCmd(btn.cmd);
23747     },
23748     // private used internally
23749     createLink : function(){
23750         Roo.log("create link?");
23751         var url = prompt(this.createLinkText, this.defaultLinkValue);
23752         if(url && url != 'http:/'+'/'){
23753             this.editorcore.relayCmd('createlink', url);
23754         }
23755     },
23756
23757     
23758     /**
23759      * Protected method that will not generally be called directly. It triggers
23760      * a toolbar update by reading the markup state of the current selection in the editor.
23761      */
23762     updateToolbar: function(){
23763
23764         if(!this.editorcore.activated){
23765             this.editor.onFirstFocus();
23766             return;
23767         }
23768
23769         var btns = this.tb.items.map, 
23770             doc = this.editorcore.doc,
23771             frameId = this.editorcore.frameId;
23772
23773         if(!this.disable.font && !Roo.isSafari){
23774             /*
23775             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23776             if(name != this.fontSelect.dom.value){
23777                 this.fontSelect.dom.value = name;
23778             }
23779             */
23780         }
23781         if(!this.disable.format){
23782             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23783             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23784             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23785             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23786         }
23787         if(!this.disable.alignments){
23788             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23789             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23790             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23791         }
23792         if(!Roo.isSafari && !this.disable.lists){
23793             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23794             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23795         }
23796         
23797         var ans = this.editorcore.getAllAncestors();
23798         if (this.formatCombo) {
23799             
23800             
23801             var store = this.formatCombo.store;
23802             this.formatCombo.setValue("");
23803             for (var i =0; i < ans.length;i++) {
23804                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23805                     // select it..
23806                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23807                     break;
23808                 }
23809             }
23810         }
23811         
23812         
23813         
23814         // hides menus... - so this cant be on a menu...
23815         Roo.menu.MenuMgr.hideAll();
23816
23817         //this.editorsyncValue();
23818     },
23819    
23820     
23821     createFontOptions : function(){
23822         var buf = [], fs = this.fontFamilies, ff, lc;
23823         
23824         
23825         
23826         for(var i = 0, len = fs.length; i< len; i++){
23827             ff = fs[i];
23828             lc = ff.toLowerCase();
23829             buf.push(
23830                 '<option value="',lc,'" style="font-family:',ff,';"',
23831                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23832                     ff,
23833                 '</option>'
23834             );
23835         }
23836         return buf.join('');
23837     },
23838     
23839     toggleSourceEdit : function(sourceEditMode){
23840         
23841         Roo.log("toolbar toogle");
23842         if(sourceEditMode === undefined){
23843             sourceEditMode = !this.sourceEditMode;
23844         }
23845         this.sourceEditMode = sourceEditMode === true;
23846         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23847         // just toggle the button?
23848         if(btn.pressed !== this.sourceEditMode){
23849             btn.toggle(this.sourceEditMode);
23850             return;
23851         }
23852         
23853         if(sourceEditMode){
23854             Roo.log("disabling buttons");
23855             this.tb.items.each(function(item){
23856                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23857                     item.disable();
23858                 }
23859             });
23860           
23861         }else{
23862             Roo.log("enabling buttons");
23863             if(this.editorcore.initialized){
23864                 this.tb.items.each(function(item){
23865                     item.enable();
23866                 });
23867             }
23868             
23869         }
23870         Roo.log("calling toggole on editor");
23871         // tell the editor that it's been pressed..
23872         this.editor.toggleSourceEdit(sourceEditMode);
23873        
23874     },
23875      /**
23876      * Object collection of toolbar tooltips for the buttons in the editor. The key
23877      * is the command id associated with that button and the value is a valid QuickTips object.
23878      * For example:
23879 <pre><code>
23880 {
23881     bold : {
23882         title: 'Bold (Ctrl+B)',
23883         text: 'Make the selected text bold.',
23884         cls: 'x-html-editor-tip'
23885     },
23886     italic : {
23887         title: 'Italic (Ctrl+I)',
23888         text: 'Make the selected text italic.',
23889         cls: 'x-html-editor-tip'
23890     },
23891     ...
23892 </code></pre>
23893     * @type Object
23894      */
23895     buttonTips : {
23896         bold : {
23897             title: 'Bold (Ctrl+B)',
23898             text: 'Make the selected text bold.',
23899             cls: 'x-html-editor-tip'
23900         },
23901         italic : {
23902             title: 'Italic (Ctrl+I)',
23903             text: 'Make the selected text italic.',
23904             cls: 'x-html-editor-tip'
23905         },
23906         underline : {
23907             title: 'Underline (Ctrl+U)',
23908             text: 'Underline the selected text.',
23909             cls: 'x-html-editor-tip'
23910         },
23911         strikethrough : {
23912             title: 'Strikethrough',
23913             text: 'Strikethrough the selected text.',
23914             cls: 'x-html-editor-tip'
23915         },
23916         increasefontsize : {
23917             title: 'Grow Text',
23918             text: 'Increase the font size.',
23919             cls: 'x-html-editor-tip'
23920         },
23921         decreasefontsize : {
23922             title: 'Shrink Text',
23923             text: 'Decrease the font size.',
23924             cls: 'x-html-editor-tip'
23925         },
23926         backcolor : {
23927             title: 'Text Highlight Color',
23928             text: 'Change the background color of the selected text.',
23929             cls: 'x-html-editor-tip'
23930         },
23931         forecolor : {
23932             title: 'Font Color',
23933             text: 'Change the color of the selected text.',
23934             cls: 'x-html-editor-tip'
23935         },
23936         justifyleft : {
23937             title: 'Align Text Left',
23938             text: 'Align text to the left.',
23939             cls: 'x-html-editor-tip'
23940         },
23941         justifycenter : {
23942             title: 'Center Text',
23943             text: 'Center text in the editor.',
23944             cls: 'x-html-editor-tip'
23945         },
23946         justifyright : {
23947             title: 'Align Text Right',
23948             text: 'Align text to the right.',
23949             cls: 'x-html-editor-tip'
23950         },
23951         insertunorderedlist : {
23952             title: 'Bullet List',
23953             text: 'Start a bulleted list.',
23954             cls: 'x-html-editor-tip'
23955         },
23956         insertorderedlist : {
23957             title: 'Numbered List',
23958             text: 'Start a numbered list.',
23959             cls: 'x-html-editor-tip'
23960         },
23961         createlink : {
23962             title: 'Hyperlink',
23963             text: 'Make the selected text a hyperlink.',
23964             cls: 'x-html-editor-tip'
23965         },
23966         sourceedit : {
23967             title: 'Source Edit',
23968             text: 'Switch to source editing mode.',
23969             cls: 'x-html-editor-tip'
23970         }
23971     },
23972     // private
23973     onDestroy : function(){
23974         if(this.rendered){
23975             
23976             this.tb.items.each(function(item){
23977                 if(item.menu){
23978                     item.menu.removeAll();
23979                     if(item.menu.el){
23980                         item.menu.el.destroy();
23981                     }
23982                 }
23983                 item.destroy();
23984             });
23985              
23986         }
23987     },
23988     onFirstFocus: function() {
23989         this.tb.items.each(function(item){
23990            item.enable();
23991         });
23992     }
23993 });
23994
23995
23996
23997
23998 // <script type="text/javascript">
23999 /*
24000  * Based on
24001  * Ext JS Library 1.1.1
24002  * Copyright(c) 2006-2007, Ext JS, LLC.
24003  *  
24004  
24005  */
24006
24007  
24008 /**
24009  * @class Roo.form.HtmlEditor.ToolbarContext
24010  * Context Toolbar
24011  * 
24012  * Usage:
24013  *
24014  new Roo.form.HtmlEditor({
24015     ....
24016     toolbars : [
24017         { xtype: 'ToolbarStandard', styles : {} }
24018         { xtype: 'ToolbarContext', disable : {} }
24019     ]
24020 })
24021
24022      
24023  * 
24024  * @config : {Object} disable List of elements to disable.. (not done yet.)
24025  * @config : {Object} styles  Map of styles available.
24026  * 
24027  */
24028
24029 Roo.form.HtmlEditor.ToolbarContext = function(config)
24030 {
24031     
24032     Roo.apply(this, config);
24033     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24034     // dont call parent... till later.
24035     this.styles = this.styles || {};
24036 }
24037
24038  
24039
24040 Roo.form.HtmlEditor.ToolbarContext.types = {
24041     'IMG' : {
24042         width : {
24043             title: "Width",
24044             width: 40
24045         },
24046         height:  {
24047             title: "Height",
24048             width: 40
24049         },
24050         align: {
24051             title: "Align",
24052             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24053             width : 80
24054             
24055         },
24056         border: {
24057             title: "Border",
24058             width: 40
24059         },
24060         alt: {
24061             title: "Alt",
24062             width: 120
24063         },
24064         src : {
24065             title: "Src",
24066             width: 220
24067         }
24068         
24069     },
24070     'A' : {
24071         name : {
24072             title: "Name",
24073             width: 50
24074         },
24075         target:  {
24076             title: "Target",
24077             width: 120
24078         },
24079         href:  {
24080             title: "Href",
24081             width: 220
24082         } // border?
24083         
24084     },
24085     'TABLE' : {
24086         rows : {
24087             title: "Rows",
24088             width: 20
24089         },
24090         cols : {
24091             title: "Cols",
24092             width: 20
24093         },
24094         width : {
24095             title: "Width",
24096             width: 40
24097         },
24098         height : {
24099             title: "Height",
24100             width: 40
24101         },
24102         border : {
24103             title: "Border",
24104             width: 20
24105         }
24106     },
24107     'TD' : {
24108         width : {
24109             title: "Width",
24110             width: 40
24111         },
24112         height : {
24113             title: "Height",
24114             width: 40
24115         },   
24116         align: {
24117             title: "Align",
24118             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24119             width: 80
24120         },
24121         valign: {
24122             title: "Valign",
24123             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24124             width: 80
24125         },
24126         colspan: {
24127             title: "Colspan",
24128             width: 20
24129             
24130         },
24131          'font-family'  : {
24132             title : "Font",
24133             style : 'fontFamily',
24134             displayField: 'display',
24135             optname : 'font-family',
24136             width: 140
24137         }
24138     },
24139     'INPUT' : {
24140         name : {
24141             title: "name",
24142             width: 120
24143         },
24144         value : {
24145             title: "Value",
24146             width: 120
24147         },
24148         width : {
24149             title: "Width",
24150             width: 40
24151         }
24152     },
24153     'LABEL' : {
24154         'for' : {
24155             title: "For",
24156             width: 120
24157         }
24158     },
24159     'TEXTAREA' : {
24160           name : {
24161             title: "name",
24162             width: 120
24163         },
24164         rows : {
24165             title: "Rows",
24166             width: 20
24167         },
24168         cols : {
24169             title: "Cols",
24170             width: 20
24171         }
24172     },
24173     'SELECT' : {
24174         name : {
24175             title: "name",
24176             width: 120
24177         },
24178         selectoptions : {
24179             title: "Options",
24180             width: 200
24181         }
24182     },
24183     
24184     // should we really allow this??
24185     // should this just be 
24186     'BODY' : {
24187         title : {
24188             title: "Title",
24189             width: 200,
24190             disabled : true
24191         }
24192     },
24193     'SPAN' : {
24194         'font-family'  : {
24195             title : "Font",
24196             style : 'fontFamily',
24197             displayField: 'display',
24198             optname : 'font-family',
24199             width: 140
24200         }
24201     },
24202     'DIV' : {
24203         'font-family'  : {
24204             title : "Font",
24205             style : 'fontFamily',
24206             displayField: 'display',
24207             optname : 'font-family',
24208             width: 140
24209         }
24210     },
24211      'P' : {
24212         'font-family'  : {
24213             title : "Font",
24214             style : 'fontFamily',
24215             displayField: 'display',
24216             optname : 'font-family',
24217             width: 140
24218         }
24219     },
24220     
24221     '*' : {
24222         // empty..
24223     }
24224
24225 };
24226
24227 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24228 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24229
24230 Roo.form.HtmlEditor.ToolbarContext.options = {
24231         'font-family'  : [ 
24232                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24233                 [ 'Courier New', 'Courier New'],
24234                 [ 'Tahoma', 'Tahoma'],
24235                 [ 'Times New Roman,serif', 'Times'],
24236                 [ 'Verdana','Verdana' ]
24237         ]
24238 };
24239
24240 // fixme - these need to be configurable..
24241  
24242
24243 //Roo.form.HtmlEditor.ToolbarContext.types
24244
24245
24246 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24247     
24248     tb: false,
24249     
24250     rendered: false,
24251     
24252     editor : false,
24253     editorcore : false,
24254     /**
24255      * @cfg {Object} disable  List of toolbar elements to disable
24256          
24257      */
24258     disable : false,
24259     /**
24260      * @cfg {Object} styles List of styles 
24261      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24262      *
24263      * These must be defined in the page, so they get rendered correctly..
24264      * .headline { }
24265      * TD.underline { }
24266      * 
24267      */
24268     styles : false,
24269     
24270     options: false,
24271     
24272     toolbars : false,
24273     
24274     init : function(editor)
24275     {
24276         this.editor = editor;
24277         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24278         var editorcore = this.editorcore;
24279         
24280         var fid = editorcore.frameId;
24281         var etb = this;
24282         function btn(id, toggle, handler){
24283             var xid = fid + '-'+ id ;
24284             return {
24285                 id : xid,
24286                 cmd : id,
24287                 cls : 'x-btn-icon x-edit-'+id,
24288                 enableToggle:toggle !== false,
24289                 scope: editorcore, // was editor...
24290                 handler:handler||editorcore.relayBtnCmd,
24291                 clickEvent:'mousedown',
24292                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24293                 tabIndex:-1
24294             };
24295         }
24296         // create a new element.
24297         var wdiv = editor.wrap.createChild({
24298                 tag: 'div'
24299             }, editor.wrap.dom.firstChild.nextSibling, true);
24300         
24301         // can we do this more than once??
24302         
24303          // stop form submits
24304       
24305  
24306         // disable everything...
24307         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24308         this.toolbars = {};
24309            
24310         for (var i in  ty) {
24311           
24312             this.toolbars[i] = this.buildToolbar(ty[i],i);
24313         }
24314         this.tb = this.toolbars.BODY;
24315         this.tb.el.show();
24316         this.buildFooter();
24317         this.footer.show();
24318         editor.on('hide', function( ) { this.footer.hide() }, this);
24319         editor.on('show', function( ) { this.footer.show() }, this);
24320         
24321          
24322         this.rendered = true;
24323         
24324         // the all the btns;
24325         editor.on('editorevent', this.updateToolbar, this);
24326         // other toolbars need to implement this..
24327         //editor.on('editmodechange', this.updateToolbar, this);
24328     },
24329     
24330     
24331     
24332     /**
24333      * Protected method that will not generally be called directly. It triggers
24334      * a toolbar update by reading the markup state of the current selection in the editor.
24335      *
24336      * Note you can force an update by calling on('editorevent', scope, false)
24337      */
24338     updateToolbar: function(editor,ev,sel){
24339
24340         //Roo.log(ev);
24341         // capture mouse up - this is handy for selecting images..
24342         // perhaps should go somewhere else...
24343         if(!this.editorcore.activated){
24344              this.editor.onFirstFocus();
24345             return;
24346         }
24347         
24348         
24349         
24350         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24351         // selectNode - might want to handle IE?
24352         if (ev &&
24353             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24354             ev.target && ev.target.tagName == 'IMG') {
24355             // they have click on an image...
24356             // let's see if we can change the selection...
24357             sel = ev.target;
24358          
24359               var nodeRange = sel.ownerDocument.createRange();
24360             try {
24361                 nodeRange.selectNode(sel);
24362             } catch (e) {
24363                 nodeRange.selectNodeContents(sel);
24364             }
24365             //nodeRange.collapse(true);
24366             var s = this.editorcore.win.getSelection();
24367             s.removeAllRanges();
24368             s.addRange(nodeRange);
24369         }  
24370         
24371       
24372         var updateFooter = sel ? false : true;
24373         
24374         
24375         var ans = this.editorcore.getAllAncestors();
24376         
24377         // pick
24378         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24379         
24380         if (!sel) { 
24381             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24382             sel = sel ? sel : this.editorcore.doc.body;
24383             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24384             
24385         }
24386         // pick a menu that exists..
24387         var tn = sel.tagName.toUpperCase();
24388         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24389         
24390         tn = sel.tagName.toUpperCase();
24391         
24392         var lastSel = this.tb.selectedNode;
24393         
24394         this.tb.selectedNode = sel;
24395         
24396         // if current menu does not match..
24397         
24398         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24399                 
24400             this.tb.el.hide();
24401             ///console.log("show: " + tn);
24402             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24403             this.tb.el.show();
24404             // update name
24405             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24406             
24407             
24408             // update attributes
24409             if (this.tb.fields) {
24410                 this.tb.fields.each(function(e) {
24411                     if (e.stylename) {
24412                         e.setValue(sel.style[e.stylename]);
24413                         return;
24414                     } 
24415                    e.setValue(sel.getAttribute(e.attrname));
24416                 });
24417             }
24418             
24419             var hasStyles = false;
24420             for(var i in this.styles) {
24421                 hasStyles = true;
24422                 break;
24423             }
24424             
24425             // update styles
24426             if (hasStyles) { 
24427                 var st = this.tb.fields.item(0);
24428                 
24429                 st.store.removeAll();
24430                
24431                 
24432                 var cn = sel.className.split(/\s+/);
24433                 
24434                 var avs = [];
24435                 if (this.styles['*']) {
24436                     
24437                     Roo.each(this.styles['*'], function(v) {
24438                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24439                     });
24440                 }
24441                 if (this.styles[tn]) { 
24442                     Roo.each(this.styles[tn], function(v) {
24443                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24444                     });
24445                 }
24446                 
24447                 st.store.loadData(avs);
24448                 st.collapse();
24449                 st.setValue(cn);
24450             }
24451             // flag our selected Node.
24452             this.tb.selectedNode = sel;
24453            
24454            
24455             Roo.menu.MenuMgr.hideAll();
24456
24457         }
24458         
24459         if (!updateFooter) {
24460             //this.footDisp.dom.innerHTML = ''; 
24461             return;
24462         }
24463         // update the footer
24464         //
24465         var html = '';
24466         
24467         this.footerEls = ans.reverse();
24468         Roo.each(this.footerEls, function(a,i) {
24469             if (!a) { return; }
24470             html += html.length ? ' &gt; '  :  '';
24471             
24472             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24473             
24474         });
24475        
24476         // 
24477         var sz = this.footDisp.up('td').getSize();
24478         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24479         this.footDisp.dom.style.marginLeft = '5px';
24480         
24481         this.footDisp.dom.style.overflow = 'hidden';
24482         
24483         this.footDisp.dom.innerHTML = html;
24484             
24485         //this.editorsyncValue();
24486     },
24487      
24488     
24489    
24490        
24491     // private
24492     onDestroy : function(){
24493         if(this.rendered){
24494             
24495             this.tb.items.each(function(item){
24496                 if(item.menu){
24497                     item.menu.removeAll();
24498                     if(item.menu.el){
24499                         item.menu.el.destroy();
24500                     }
24501                 }
24502                 item.destroy();
24503             });
24504              
24505         }
24506     },
24507     onFirstFocus: function() {
24508         // need to do this for all the toolbars..
24509         this.tb.items.each(function(item){
24510            item.enable();
24511         });
24512     },
24513     buildToolbar: function(tlist, nm)
24514     {
24515         var editor = this.editor;
24516         var editorcore = this.editorcore;
24517          // create a new element.
24518         var wdiv = editor.wrap.createChild({
24519                 tag: 'div'
24520             }, editor.wrap.dom.firstChild.nextSibling, true);
24521         
24522        
24523         var tb = new Roo.Toolbar(wdiv);
24524         // add the name..
24525         
24526         tb.add(nm+ ":&nbsp;");
24527         
24528         var styles = [];
24529         for(var i in this.styles) {
24530             styles.push(i);
24531         }
24532         
24533         // styles...
24534         if (styles && styles.length) {
24535             
24536             // this needs a multi-select checkbox...
24537             tb.addField( new Roo.form.ComboBox({
24538                 store: new Roo.data.SimpleStore({
24539                     id : 'val',
24540                     fields: ['val', 'selected'],
24541                     data : [] 
24542                 }),
24543                 name : '-roo-edit-className',
24544                 attrname : 'className',
24545                 displayField: 'val',
24546                 typeAhead: false,
24547                 mode: 'local',
24548                 editable : false,
24549                 triggerAction: 'all',
24550                 emptyText:'Select Style',
24551                 selectOnFocus:true,
24552                 width: 130,
24553                 listeners : {
24554                     'select': function(c, r, i) {
24555                         // initial support only for on class per el..
24556                         tb.selectedNode.className =  r ? r.get('val') : '';
24557                         editorcore.syncValue();
24558                     }
24559                 }
24560     
24561             }));
24562         }
24563         
24564         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24565         var tbops = tbc.options;
24566         
24567         for (var i in tlist) {
24568             
24569             var item = tlist[i];
24570             tb.add(item.title + ":&nbsp;");
24571             
24572             
24573             //optname == used so you can configure the options available..
24574             var opts = item.opts ? item.opts : false;
24575             if (item.optname) {
24576                 opts = tbops[item.optname];
24577            
24578             }
24579             
24580             if (opts) {
24581                 // opts == pulldown..
24582                 tb.addField( new Roo.form.ComboBox({
24583                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24584                         id : 'val',
24585                         fields: ['val', 'display'],
24586                         data : opts  
24587                     }),
24588                     name : '-roo-edit-' + i,
24589                     attrname : i,
24590                     stylename : item.style ? item.style : false,
24591                     displayField: item.displayField ? item.displayField : 'val',
24592                     valueField :  'val',
24593                     typeAhead: false,
24594                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24595                     editable : false,
24596                     triggerAction: 'all',
24597                     emptyText:'Select',
24598                     selectOnFocus:true,
24599                     width: item.width ? item.width  : 130,
24600                     listeners : {
24601                         'select': function(c, r, i) {
24602                             if (c.stylename) {
24603                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24604                                 return;
24605                             }
24606                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24607                         }
24608                     }
24609
24610                 }));
24611                 continue;
24612                     
24613                  
24614                 
24615                 tb.addField( new Roo.form.TextField({
24616                     name: i,
24617                     width: 100,
24618                     //allowBlank:false,
24619                     value: ''
24620                 }));
24621                 continue;
24622             }
24623             tb.addField( new Roo.form.TextField({
24624                 name: '-roo-edit-' + i,
24625                 attrname : i,
24626                 
24627                 width: item.width,
24628                 //allowBlank:true,
24629                 value: '',
24630                 listeners: {
24631                     'change' : function(f, nv, ov) {
24632                         tb.selectedNode.setAttribute(f.attrname, nv);
24633                         editorcore.syncValue();
24634                     }
24635                 }
24636             }));
24637              
24638         }
24639         
24640         var _this = this;
24641         
24642         if(nm == 'BODY'){
24643             tb.addSeparator();
24644         
24645             tb.addButton( {
24646                 text: 'Stylesheets',
24647
24648                 listeners : {
24649                     click : function ()
24650                     {
24651                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24652                     }
24653                 }
24654             });
24655         }
24656         
24657         tb.addFill();
24658         tb.addButton( {
24659             text: 'Remove Tag',
24660     
24661             listeners : {
24662                 click : function ()
24663                 {
24664                     // remove
24665                     // undo does not work.
24666                      
24667                     var sn = tb.selectedNode;
24668                     
24669                     var pn = sn.parentNode;
24670                     
24671                     var stn =  sn.childNodes[0];
24672                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24673                     while (sn.childNodes.length) {
24674                         var node = sn.childNodes[0];
24675                         sn.removeChild(node);
24676                         //Roo.log(node);
24677                         pn.insertBefore(node, sn);
24678                         
24679                     }
24680                     pn.removeChild(sn);
24681                     var range = editorcore.createRange();
24682         
24683                     range.setStart(stn,0);
24684                     range.setEnd(en,0); //????
24685                     //range.selectNode(sel);
24686                     
24687                     
24688                     var selection = editorcore.getSelection();
24689                     selection.removeAllRanges();
24690                     selection.addRange(range);
24691                     
24692                     
24693                     
24694                     //_this.updateToolbar(null, null, pn);
24695                     _this.updateToolbar(null, null, null);
24696                     _this.footDisp.dom.innerHTML = ''; 
24697                 }
24698             }
24699             
24700                     
24701                 
24702             
24703         });
24704         
24705         
24706         tb.el.on('click', function(e){
24707             e.preventDefault(); // what does this do?
24708         });
24709         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24710         tb.el.hide();
24711         tb.name = nm;
24712         // dont need to disable them... as they will get hidden
24713         return tb;
24714          
24715         
24716     },
24717     buildFooter : function()
24718     {
24719         
24720         var fel = this.editor.wrap.createChild();
24721         this.footer = new Roo.Toolbar(fel);
24722         // toolbar has scrolly on left / right?
24723         var footDisp= new Roo.Toolbar.Fill();
24724         var _t = this;
24725         this.footer.add(
24726             {
24727                 text : '&lt;',
24728                 xtype: 'Button',
24729                 handler : function() {
24730                     _t.footDisp.scrollTo('left',0,true)
24731                 }
24732             }
24733         );
24734         this.footer.add( footDisp );
24735         this.footer.add( 
24736             {
24737                 text : '&gt;',
24738                 xtype: 'Button',
24739                 handler : function() {
24740                     // no animation..
24741                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24742                 }
24743             }
24744         );
24745         var fel = Roo.get(footDisp.el);
24746         fel.addClass('x-editor-context');
24747         this.footDispWrap = fel; 
24748         this.footDispWrap.overflow  = 'hidden';
24749         
24750         this.footDisp = fel.createChild();
24751         this.footDispWrap.on('click', this.onContextClick, this)
24752         
24753         
24754     },
24755     onContextClick : function (ev,dom)
24756     {
24757         ev.preventDefault();
24758         var  cn = dom.className;
24759         //Roo.log(cn);
24760         if (!cn.match(/x-ed-loc-/)) {
24761             return;
24762         }
24763         var n = cn.split('-').pop();
24764         var ans = this.footerEls;
24765         var sel = ans[n];
24766         
24767          // pick
24768         var range = this.editorcore.createRange();
24769         
24770         range.selectNodeContents(sel);
24771         //range.selectNode(sel);
24772         
24773         
24774         var selection = this.editorcore.getSelection();
24775         selection.removeAllRanges();
24776         selection.addRange(range);
24777         
24778         
24779         
24780         this.updateToolbar(null, null, sel);
24781         
24782         
24783     }
24784     
24785     
24786     
24787     
24788     
24789 });
24790
24791
24792
24793
24794
24795 /*
24796  * Based on:
24797  * Ext JS Library 1.1.1
24798  * Copyright(c) 2006-2007, Ext JS, LLC.
24799  *
24800  * Originally Released Under LGPL - original licence link has changed is not relivant.
24801  *
24802  * Fork - LGPL
24803  * <script type="text/javascript">
24804  */
24805  
24806 /**
24807  * @class Roo.form.BasicForm
24808  * @extends Roo.util.Observable
24809  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24810  * @constructor
24811  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24812  * @param {Object} config Configuration options
24813  */
24814 Roo.form.BasicForm = function(el, config){
24815     this.allItems = [];
24816     this.childForms = [];
24817     Roo.apply(this, config);
24818     /*
24819      * The Roo.form.Field items in this form.
24820      * @type MixedCollection
24821      */
24822      
24823      
24824     this.items = new Roo.util.MixedCollection(false, function(o){
24825         return o.id || (o.id = Roo.id());
24826     });
24827     this.addEvents({
24828         /**
24829          * @event beforeaction
24830          * Fires before any action is performed. Return false to cancel the action.
24831          * @param {Form} this
24832          * @param {Action} action The action to be performed
24833          */
24834         beforeaction: true,
24835         /**
24836          * @event actionfailed
24837          * Fires when an action fails.
24838          * @param {Form} this
24839          * @param {Action} action The action that failed
24840          */
24841         actionfailed : true,
24842         /**
24843          * @event actioncomplete
24844          * Fires when an action is completed.
24845          * @param {Form} this
24846          * @param {Action} action The action that completed
24847          */
24848         actioncomplete : true
24849     });
24850     if(el){
24851         this.initEl(el);
24852     }
24853     Roo.form.BasicForm.superclass.constructor.call(this);
24854     
24855     Roo.form.BasicForm.popover.apply();
24856 };
24857
24858 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24859     /**
24860      * @cfg {String} method
24861      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24862      */
24863     /**
24864      * @cfg {DataReader} reader
24865      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24866      * This is optional as there is built-in support for processing JSON.
24867      */
24868     /**
24869      * @cfg {DataReader} errorReader
24870      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24871      * This is completely optional as there is built-in support for processing JSON.
24872      */
24873     /**
24874      * @cfg {String} url
24875      * The URL to use for form actions if one isn't supplied in the action options.
24876      */
24877     /**
24878      * @cfg {Boolean} fileUpload
24879      * Set to true if this form is a file upload.
24880      */
24881      
24882     /**
24883      * @cfg {Object} baseParams
24884      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24885      */
24886      /**
24887      
24888     /**
24889      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24890      */
24891     timeout: 30,
24892
24893     // private
24894     activeAction : null,
24895
24896     /**
24897      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24898      * or setValues() data instead of when the form was first created.
24899      */
24900     trackResetOnLoad : false,
24901     
24902     
24903     /**
24904      * childForms - used for multi-tab forms
24905      * @type {Array}
24906      */
24907     childForms : false,
24908     
24909     /**
24910      * allItems - full list of fields.
24911      * @type {Array}
24912      */
24913     allItems : false,
24914     
24915     /**
24916      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24917      * element by passing it or its id or mask the form itself by passing in true.
24918      * @type Mixed
24919      */
24920     waitMsgTarget : false,
24921     
24922     /**
24923      * @type Boolean
24924      */
24925     disableMask : false,
24926     
24927     /**
24928      * @cfg {Boolean} errorMask (true|false) default false
24929      */
24930     errorMask : false,
24931     
24932     /**
24933      * @cfg {Number} maskOffset Default 100
24934      */
24935     maskOffset : 100,
24936
24937     // private
24938     initEl : function(el){
24939         this.el = Roo.get(el);
24940         this.id = this.el.id || Roo.id();
24941         this.el.on('submit', this.onSubmit, this);
24942         this.el.addClass('x-form');
24943     },
24944
24945     // private
24946     onSubmit : function(e){
24947         e.stopEvent();
24948     },
24949
24950     /**
24951      * Returns true if client-side validation on the form is successful.
24952      * @return Boolean
24953      */
24954     isValid : function(){
24955         var valid = true;
24956         var target = false;
24957         this.items.each(function(f){
24958             if(f.validate()){
24959                 return;
24960             }
24961             
24962             valid = false;
24963                 
24964             if(!target && f.el.isVisible(true)){
24965                 target = f;
24966             }
24967         });
24968         
24969         if(this.errorMask && !valid){
24970             Roo.form.BasicForm.popover.mask(this, target);
24971         }
24972         
24973         return valid;
24974     },
24975
24976     /**
24977      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24978      * @return Boolean
24979      */
24980     isDirty : function(){
24981         var dirty = false;
24982         this.items.each(function(f){
24983            if(f.isDirty()){
24984                dirty = true;
24985                return false;
24986            }
24987         });
24988         return dirty;
24989     },
24990     
24991     /**
24992      * Returns true if any fields in this form have changed since their original load. (New version)
24993      * @return Boolean
24994      */
24995     
24996     hasChanged : function()
24997     {
24998         var dirty = false;
24999         this.items.each(function(f){
25000            if(f.hasChanged()){
25001                dirty = true;
25002                return false;
25003            }
25004         });
25005         return dirty;
25006         
25007     },
25008     /**
25009      * Resets all hasChanged to 'false' -
25010      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
25011      * So hasChanged storage is only to be used for this purpose
25012      * @return Boolean
25013      */
25014     resetHasChanged : function()
25015     {
25016         this.items.each(function(f){
25017            f.resetHasChanged();
25018         });
25019         
25020     },
25021     
25022     
25023     /**
25024      * Performs a predefined action (submit or load) or custom actions you define on this form.
25025      * @param {String} actionName The name of the action type
25026      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25027      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25028      * accept other config options):
25029      * <pre>
25030 Property          Type             Description
25031 ----------------  ---------------  ----------------------------------------------------------------------------------
25032 url               String           The url for the action (defaults to the form's url)
25033 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25034 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25035 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25036                                    validate the form on the client (defaults to false)
25037      * </pre>
25038      * @return {BasicForm} this
25039      */
25040     doAction : function(action, options){
25041         if(typeof action == 'string'){
25042             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25043         }
25044         if(this.fireEvent('beforeaction', this, action) !== false){
25045             this.beforeAction(action);
25046             action.run.defer(100, action);
25047         }
25048         return this;
25049     },
25050
25051     /**
25052      * Shortcut to do a submit action.
25053      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25054      * @return {BasicForm} this
25055      */
25056     submit : function(options){
25057         this.doAction('submit', options);
25058         return this;
25059     },
25060
25061     /**
25062      * Shortcut to do a load action.
25063      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25064      * @return {BasicForm} this
25065      */
25066     load : function(options){
25067         this.doAction('load', options);
25068         return this;
25069     },
25070
25071     /**
25072      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25073      * @param {Record} record The record to edit
25074      * @return {BasicForm} this
25075      */
25076     updateRecord : function(record){
25077         record.beginEdit();
25078         var fs = record.fields;
25079         fs.each(function(f){
25080             var field = this.findField(f.name);
25081             if(field){
25082                 record.set(f.name, field.getValue());
25083             }
25084         }, this);
25085         record.endEdit();
25086         return this;
25087     },
25088
25089     /**
25090      * Loads an Roo.data.Record into this form.
25091      * @param {Record} record The record to load
25092      * @return {BasicForm} this
25093      */
25094     loadRecord : function(record){
25095         this.setValues(record.data);
25096         return this;
25097     },
25098
25099     // private
25100     beforeAction : function(action){
25101         var o = action.options;
25102         
25103         if(!this.disableMask) {
25104             if(this.waitMsgTarget === true){
25105                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25106             }else if(this.waitMsgTarget){
25107                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25108                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25109             }else {
25110                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25111             }
25112         }
25113         
25114          
25115     },
25116
25117     // private
25118     afterAction : function(action, success){
25119         this.activeAction = null;
25120         var o = action.options;
25121         
25122         if(!this.disableMask) {
25123             if(this.waitMsgTarget === true){
25124                 this.el.unmask();
25125             }else if(this.waitMsgTarget){
25126                 this.waitMsgTarget.unmask();
25127             }else{
25128                 Roo.MessageBox.updateProgress(1);
25129                 Roo.MessageBox.hide();
25130             }
25131         }
25132         
25133         if(success){
25134             if(o.reset){
25135                 this.reset();
25136             }
25137             Roo.callback(o.success, o.scope, [this, action]);
25138             this.fireEvent('actioncomplete', this, action);
25139             
25140         }else{
25141             
25142             // failure condition..
25143             // we have a scenario where updates need confirming.
25144             // eg. if a locking scenario exists..
25145             // we look for { errors : { needs_confirm : true }} in the response.
25146             if (
25147                 (typeof(action.result) != 'undefined')  &&
25148                 (typeof(action.result.errors) != 'undefined')  &&
25149                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25150            ){
25151                 var _t = this;
25152                 Roo.MessageBox.confirm(
25153                     "Change requires confirmation",
25154                     action.result.errorMsg,
25155                     function(r) {
25156                         if (r != 'yes') {
25157                             return;
25158                         }
25159                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25160                     }
25161                     
25162                 );
25163                 
25164                 
25165                 
25166                 return;
25167             }
25168             
25169             Roo.callback(o.failure, o.scope, [this, action]);
25170             // show an error message if no failed handler is set..
25171             if (!this.hasListener('actionfailed')) {
25172                 Roo.MessageBox.alert("Error",
25173                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25174                         action.result.errorMsg :
25175                         "Saving Failed, please check your entries or try again"
25176                 );
25177             }
25178             
25179             this.fireEvent('actionfailed', this, action);
25180         }
25181         
25182     },
25183
25184     /**
25185      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25186      * @param {String} id The value to search for
25187      * @return Field
25188      */
25189     findField : function(id){
25190         var field = this.items.get(id);
25191         if(!field){
25192             this.items.each(function(f){
25193                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25194                     field = f;
25195                     return false;
25196                 }
25197             });
25198         }
25199         return field || null;
25200     },
25201
25202     /**
25203      * Add a secondary form to this one, 
25204      * Used to provide tabbed forms. One form is primary, with hidden values 
25205      * which mirror the elements from the other forms.
25206      * 
25207      * @param {Roo.form.Form} form to add.
25208      * 
25209      */
25210     addForm : function(form)
25211     {
25212        
25213         if (this.childForms.indexOf(form) > -1) {
25214             // already added..
25215             return;
25216         }
25217         this.childForms.push(form);
25218         var n = '';
25219         Roo.each(form.allItems, function (fe) {
25220             
25221             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25222             if (this.findField(n)) { // already added..
25223                 return;
25224             }
25225             var add = new Roo.form.Hidden({
25226                 name : n
25227             });
25228             add.render(this.el);
25229             
25230             this.add( add );
25231         }, this);
25232         
25233     },
25234     /**
25235      * Mark fields in this form invalid in bulk.
25236      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25237      * @return {BasicForm} this
25238      */
25239     markInvalid : function(errors){
25240         if(errors instanceof Array){
25241             for(var i = 0, len = errors.length; i < len; i++){
25242                 var fieldError = errors[i];
25243                 var f = this.findField(fieldError.id);
25244                 if(f){
25245                     f.markInvalid(fieldError.msg);
25246                 }
25247             }
25248         }else{
25249             var field, id;
25250             for(id in errors){
25251                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25252                     field.markInvalid(errors[id]);
25253                 }
25254             }
25255         }
25256         Roo.each(this.childForms || [], function (f) {
25257             f.markInvalid(errors);
25258         });
25259         
25260         return this;
25261     },
25262
25263     /**
25264      * Set values for fields in this form in bulk.
25265      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25266      * @return {BasicForm} this
25267      */
25268     setValues : function(values){
25269         if(values instanceof Array){ // array of objects
25270             for(var i = 0, len = values.length; i < len; i++){
25271                 var v = values[i];
25272                 var f = this.findField(v.id);
25273                 if(f){
25274                     f.setValue(v.value);
25275                     if(this.trackResetOnLoad){
25276                         f.originalValue = f.getValue();
25277                     }
25278                 }
25279             }
25280         }else{ // object hash
25281             var field, id;
25282             for(id in values){
25283                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25284                     
25285                     if (field.setFromData && 
25286                         field.valueField && 
25287                         field.displayField &&
25288                         // combos' with local stores can 
25289                         // be queried via setValue()
25290                         // to set their value..
25291                         (field.store && !field.store.isLocal)
25292                         ) {
25293                         // it's a combo
25294                         var sd = { };
25295                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25296                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25297                         field.setFromData(sd);
25298                         
25299                     } else {
25300                         field.setValue(values[id]);
25301                     }
25302                     
25303                     
25304                     if(this.trackResetOnLoad){
25305                         field.originalValue = field.getValue();
25306                     }
25307                 }
25308             }
25309         }
25310         this.resetHasChanged();
25311         
25312         
25313         Roo.each(this.childForms || [], function (f) {
25314             f.setValues(values);
25315             f.resetHasChanged();
25316         });
25317                 
25318         return this;
25319     },
25320  
25321     /**
25322      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25323      * they are returned as an array.
25324      * @param {Boolean} asString
25325      * @return {Object}
25326      */
25327     getValues : function(asString){
25328         if (this.childForms) {
25329             // copy values from the child forms
25330             Roo.each(this.childForms, function (f) {
25331                 this.setValues(f.getValues());
25332             }, this);
25333         }
25334         
25335         // use formdata
25336         if (typeof(FormData) != 'undefined' && asString !== true) {
25337             var fd = (new FormData(this.el.dom)).entries();
25338             var ret = {};
25339             var ent = fd.next();
25340             while (!ent.done) {
25341                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25342                 ent = fd.next();
25343             };
25344             return ret;
25345         }
25346         
25347         
25348         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25349         if(asString === true){
25350             return fs;
25351         }
25352         return Roo.urlDecode(fs);
25353     },
25354     
25355     /**
25356      * Returns the fields in this form as an object with key/value pairs. 
25357      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25358      * @return {Object}
25359      */
25360     getFieldValues : function(with_hidden)
25361     {
25362         if (this.childForms) {
25363             // copy values from the child forms
25364             // should this call getFieldValues - probably not as we do not currently copy
25365             // hidden fields when we generate..
25366             Roo.each(this.childForms, function (f) {
25367                 this.setValues(f.getValues());
25368             }, this);
25369         }
25370         
25371         var ret = {};
25372         this.items.each(function(f){
25373             if (!f.getName()) {
25374                 return;
25375             }
25376             var v = f.getValue();
25377             if (f.inputType =='radio') {
25378                 if (typeof(ret[f.getName()]) == 'undefined') {
25379                     ret[f.getName()] = ''; // empty..
25380                 }
25381                 
25382                 if (!f.el.dom.checked) {
25383                     return;
25384                     
25385                 }
25386                 v = f.el.dom.value;
25387                 
25388             }
25389             
25390             // not sure if this supported any more..
25391             if ((typeof(v) == 'object') && f.getRawValue) {
25392                 v = f.getRawValue() ; // dates..
25393             }
25394             // combo boxes where name != hiddenName...
25395             if (f.name != f.getName()) {
25396                 ret[f.name] = f.getRawValue();
25397             }
25398             ret[f.getName()] = v;
25399         });
25400         
25401         return ret;
25402     },
25403
25404     /**
25405      * Clears all invalid messages in this form.
25406      * @return {BasicForm} this
25407      */
25408     clearInvalid : function(){
25409         this.items.each(function(f){
25410            f.clearInvalid();
25411         });
25412         
25413         Roo.each(this.childForms || [], function (f) {
25414             f.clearInvalid();
25415         });
25416         
25417         
25418         return this;
25419     },
25420
25421     /**
25422      * Resets this form.
25423      * @return {BasicForm} this
25424      */
25425     reset : function(){
25426         this.items.each(function(f){
25427             f.reset();
25428         });
25429         
25430         Roo.each(this.childForms || [], function (f) {
25431             f.reset();
25432         });
25433         this.resetHasChanged();
25434         
25435         return this;
25436     },
25437
25438     /**
25439      * Add Roo.form components to this form.
25440      * @param {Field} field1
25441      * @param {Field} field2 (optional)
25442      * @param {Field} etc (optional)
25443      * @return {BasicForm} this
25444      */
25445     add : function(){
25446         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25447         return this;
25448     },
25449
25450
25451     /**
25452      * Removes a field from the items collection (does NOT remove its markup).
25453      * @param {Field} field
25454      * @return {BasicForm} this
25455      */
25456     remove : function(field){
25457         this.items.remove(field);
25458         return this;
25459     },
25460
25461     /**
25462      * Looks at the fields in this form, checks them for an id attribute,
25463      * and calls applyTo on the existing dom element with that id.
25464      * @return {BasicForm} this
25465      */
25466     render : function(){
25467         this.items.each(function(f){
25468             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25469                 f.applyTo(f.id);
25470             }
25471         });
25472         return this;
25473     },
25474
25475     /**
25476      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25477      * @param {Object} values
25478      * @return {BasicForm} this
25479      */
25480     applyToFields : function(o){
25481         this.items.each(function(f){
25482            Roo.apply(f, o);
25483         });
25484         return this;
25485     },
25486
25487     /**
25488      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25489      * @param {Object} values
25490      * @return {BasicForm} this
25491      */
25492     applyIfToFields : function(o){
25493         this.items.each(function(f){
25494            Roo.applyIf(f, o);
25495         });
25496         return this;
25497     }
25498 });
25499
25500 // back compat
25501 Roo.BasicForm = Roo.form.BasicForm;
25502
25503 Roo.apply(Roo.form.BasicForm, {
25504     
25505     popover : {
25506         
25507         padding : 5,
25508         
25509         isApplied : false,
25510         
25511         isMasked : false,
25512         
25513         form : false,
25514         
25515         target : false,
25516         
25517         intervalID : false,
25518         
25519         maskEl : false,
25520         
25521         apply : function()
25522         {
25523             if(this.isApplied){
25524                 return;
25525             }
25526             
25527             this.maskEl = {
25528                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25529                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25530                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25531                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25532             };
25533             
25534             this.maskEl.top.enableDisplayMode("block");
25535             this.maskEl.left.enableDisplayMode("block");
25536             this.maskEl.bottom.enableDisplayMode("block");
25537             this.maskEl.right.enableDisplayMode("block");
25538             
25539             Roo.get(document.body).on('click', function(){
25540                 this.unmask();
25541             }, this);
25542             
25543             Roo.get(document.body).on('touchstart', function(){
25544                 this.unmask();
25545             }, this);
25546             
25547             this.isApplied = true
25548         },
25549         
25550         mask : function(form, target)
25551         {
25552             this.form = form;
25553             
25554             this.target = target;
25555             
25556             if(!this.form.errorMask || !target.el){
25557                 return;
25558             }
25559             
25560             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25561             
25562             var ot = this.target.el.calcOffsetsTo(scrollable);
25563             
25564             var scrollTo = ot[1] - this.form.maskOffset;
25565             
25566             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25567             
25568             scrollable.scrollTo('top', scrollTo);
25569             
25570             var el = this.target.wrap || this.target.el;
25571             
25572             var box = el.getBox();
25573             
25574             this.maskEl.top.setStyle('position', 'absolute');
25575             this.maskEl.top.setStyle('z-index', 10000);
25576             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25577             this.maskEl.top.setLeft(0);
25578             this.maskEl.top.setTop(0);
25579             this.maskEl.top.show();
25580             
25581             this.maskEl.left.setStyle('position', 'absolute');
25582             this.maskEl.left.setStyle('z-index', 10000);
25583             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25584             this.maskEl.left.setLeft(0);
25585             this.maskEl.left.setTop(box.y - this.padding);
25586             this.maskEl.left.show();
25587
25588             this.maskEl.bottom.setStyle('position', 'absolute');
25589             this.maskEl.bottom.setStyle('z-index', 10000);
25590             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25591             this.maskEl.bottom.setLeft(0);
25592             this.maskEl.bottom.setTop(box.bottom + this.padding);
25593             this.maskEl.bottom.show();
25594
25595             this.maskEl.right.setStyle('position', 'absolute');
25596             this.maskEl.right.setStyle('z-index', 10000);
25597             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25598             this.maskEl.right.setLeft(box.right + this.padding);
25599             this.maskEl.right.setTop(box.y - this.padding);
25600             this.maskEl.right.show();
25601
25602             this.intervalID = window.setInterval(function() {
25603                 Roo.form.BasicForm.popover.unmask();
25604             }, 10000);
25605
25606             window.onwheel = function(){ return false;};
25607             
25608             (function(){ this.isMasked = true; }).defer(500, this);
25609             
25610         },
25611         
25612         unmask : function()
25613         {
25614             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25615                 return;
25616             }
25617             
25618             this.maskEl.top.setStyle('position', 'absolute');
25619             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25620             this.maskEl.top.hide();
25621
25622             this.maskEl.left.setStyle('position', 'absolute');
25623             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25624             this.maskEl.left.hide();
25625
25626             this.maskEl.bottom.setStyle('position', 'absolute');
25627             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25628             this.maskEl.bottom.hide();
25629
25630             this.maskEl.right.setStyle('position', 'absolute');
25631             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25632             this.maskEl.right.hide();
25633             
25634             window.onwheel = function(){ return true;};
25635             
25636             if(this.intervalID){
25637                 window.clearInterval(this.intervalID);
25638                 this.intervalID = false;
25639             }
25640             
25641             this.isMasked = false;
25642             
25643         }
25644         
25645     }
25646     
25647 });/*
25648  * Based on:
25649  * Ext JS Library 1.1.1
25650  * Copyright(c) 2006-2007, Ext JS, LLC.
25651  *
25652  * Originally Released Under LGPL - original licence link has changed is not relivant.
25653  *
25654  * Fork - LGPL
25655  * <script type="text/javascript">
25656  */
25657
25658 /**
25659  * @class Roo.form.Form
25660  * @extends Roo.form.BasicForm
25661  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25662  * @constructor
25663  * @param {Object} config Configuration options
25664  */
25665 Roo.form.Form = function(config){
25666     var xitems =  [];
25667     if (config.items) {
25668         xitems = config.items;
25669         delete config.items;
25670     }
25671    
25672     
25673     Roo.form.Form.superclass.constructor.call(this, null, config);
25674     this.url = this.url || this.action;
25675     if(!this.root){
25676         this.root = new Roo.form.Layout(Roo.applyIf({
25677             id: Roo.id()
25678         }, config));
25679     }
25680     this.active = this.root;
25681     /**
25682      * Array of all the buttons that have been added to this form via {@link addButton}
25683      * @type Array
25684      */
25685     this.buttons = [];
25686     this.allItems = [];
25687     this.addEvents({
25688         /**
25689          * @event clientvalidation
25690          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25691          * @param {Form} this
25692          * @param {Boolean} valid true if the form has passed client-side validation
25693          */
25694         clientvalidation: true,
25695         /**
25696          * @event rendered
25697          * Fires when the form is rendered
25698          * @param {Roo.form.Form} form
25699          */
25700         rendered : true
25701     });
25702     
25703     if (this.progressUrl) {
25704             // push a hidden field onto the list of fields..
25705             this.addxtype( {
25706                     xns: Roo.form, 
25707                     xtype : 'Hidden', 
25708                     name : 'UPLOAD_IDENTIFIER' 
25709             });
25710         }
25711         
25712     
25713     Roo.each(xitems, this.addxtype, this);
25714     
25715 };
25716
25717 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25718     /**
25719      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25720      */
25721     /**
25722      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25723      */
25724     /**
25725      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25726      */
25727     buttonAlign:'center',
25728
25729     /**
25730      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25731      */
25732     minButtonWidth:75,
25733
25734     /**
25735      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25736      * This property cascades to child containers if not set.
25737      */
25738     labelAlign:'left',
25739
25740     /**
25741      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25742      * fires a looping event with that state. This is required to bind buttons to the valid
25743      * state using the config value formBind:true on the button.
25744      */
25745     monitorValid : false,
25746
25747     /**
25748      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25749      */
25750     monitorPoll : 200,
25751     
25752     /**
25753      * @cfg {String} progressUrl - Url to return progress data 
25754      */
25755     
25756     progressUrl : false,
25757     /**
25758      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25759      * sending a formdata with extra parameters - eg uploaded elements.
25760      */
25761     
25762     formData : false,
25763     
25764     /**
25765      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25766      * fields are added and the column is closed. If no fields are passed the column remains open
25767      * until end() is called.
25768      * @param {Object} config The config to pass to the column
25769      * @param {Field} field1 (optional)
25770      * @param {Field} field2 (optional)
25771      * @param {Field} etc (optional)
25772      * @return Column The column container object
25773      */
25774     column : function(c){
25775         var col = new Roo.form.Column(c);
25776         this.start(col);
25777         if(arguments.length > 1){ // duplicate code required because of Opera
25778             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25779             this.end();
25780         }
25781         return col;
25782     },
25783
25784     /**
25785      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25786      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25787      * until end() is called.
25788      * @param {Object} config The config to pass to the fieldset
25789      * @param {Field} field1 (optional)
25790      * @param {Field} field2 (optional)
25791      * @param {Field} etc (optional)
25792      * @return FieldSet The fieldset container object
25793      */
25794     fieldset : function(c){
25795         var fs = new Roo.form.FieldSet(c);
25796         this.start(fs);
25797         if(arguments.length > 1){ // duplicate code required because of Opera
25798             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25799             this.end();
25800         }
25801         return fs;
25802     },
25803
25804     /**
25805      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25806      * fields are added and the container is closed. If no fields are passed the container remains open
25807      * until end() is called.
25808      * @param {Object} config The config to pass to the Layout
25809      * @param {Field} field1 (optional)
25810      * @param {Field} field2 (optional)
25811      * @param {Field} etc (optional)
25812      * @return Layout The container object
25813      */
25814     container : function(c){
25815         var l = new Roo.form.Layout(c);
25816         this.start(l);
25817         if(arguments.length > 1){ // duplicate code required because of Opera
25818             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25819             this.end();
25820         }
25821         return l;
25822     },
25823
25824     /**
25825      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25826      * @param {Object} container A Roo.form.Layout or subclass of Layout
25827      * @return {Form} this
25828      */
25829     start : function(c){
25830         // cascade label info
25831         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25832         this.active.stack.push(c);
25833         c.ownerCt = this.active;
25834         this.active = c;
25835         return this;
25836     },
25837
25838     /**
25839      * Closes the current open container
25840      * @return {Form} this
25841      */
25842     end : function(){
25843         if(this.active == this.root){
25844             return this;
25845         }
25846         this.active = this.active.ownerCt;
25847         return this;
25848     },
25849
25850     /**
25851      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25852      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25853      * as the label of the field.
25854      * @param {Field} field1
25855      * @param {Field} field2 (optional)
25856      * @param {Field} etc. (optional)
25857      * @return {Form} this
25858      */
25859     add : function(){
25860         this.active.stack.push.apply(this.active.stack, arguments);
25861         this.allItems.push.apply(this.allItems,arguments);
25862         var r = [];
25863         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25864             if(a[i].isFormField){
25865                 r.push(a[i]);
25866             }
25867         }
25868         if(r.length > 0){
25869             Roo.form.Form.superclass.add.apply(this, r);
25870         }
25871         return this;
25872     },
25873     
25874
25875     
25876     
25877     
25878      /**
25879      * Find any element that has been added to a form, using it's ID or name
25880      * This can include framesets, columns etc. along with regular fields..
25881      * @param {String} id - id or name to find.
25882      
25883      * @return {Element} e - or false if nothing found.
25884      */
25885     findbyId : function(id)
25886     {
25887         var ret = false;
25888         if (!id) {
25889             return ret;
25890         }
25891         Roo.each(this.allItems, function(f){
25892             if (f.id == id || f.name == id ){
25893                 ret = f;
25894                 return false;
25895             }
25896         });
25897         return ret;
25898     },
25899
25900     
25901     
25902     /**
25903      * Render this form into the passed container. This should only be called once!
25904      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25905      * @return {Form} this
25906      */
25907     render : function(ct)
25908     {
25909         
25910         
25911         
25912         ct = Roo.get(ct);
25913         var o = this.autoCreate || {
25914             tag: 'form',
25915             method : this.method || 'POST',
25916             id : this.id || Roo.id()
25917         };
25918         this.initEl(ct.createChild(o));
25919
25920         this.root.render(this.el);
25921         
25922        
25923              
25924         this.items.each(function(f){
25925             f.render('x-form-el-'+f.id);
25926         });
25927
25928         if(this.buttons.length > 0){
25929             // tables are required to maintain order and for correct IE layout
25930             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25931                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25932                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25933             }}, null, true);
25934             var tr = tb.getElementsByTagName('tr')[0];
25935             for(var i = 0, len = this.buttons.length; i < len; i++) {
25936                 var b = this.buttons[i];
25937                 var td = document.createElement('td');
25938                 td.className = 'x-form-btn-td';
25939                 b.render(tr.appendChild(td));
25940             }
25941         }
25942         if(this.monitorValid){ // initialize after render
25943             this.startMonitoring();
25944         }
25945         this.fireEvent('rendered', this);
25946         return this;
25947     },
25948
25949     /**
25950      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25951      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25952      * object or a valid Roo.DomHelper element config
25953      * @param {Function} handler The function called when the button is clicked
25954      * @param {Object} scope (optional) The scope of the handler function
25955      * @return {Roo.Button}
25956      */
25957     addButton : function(config, handler, scope){
25958         var bc = {
25959             handler: handler,
25960             scope: scope,
25961             minWidth: this.minButtonWidth,
25962             hideParent:true
25963         };
25964         if(typeof config == "string"){
25965             bc.text = config;
25966         }else{
25967             Roo.apply(bc, config);
25968         }
25969         var btn = new Roo.Button(null, bc);
25970         this.buttons.push(btn);
25971         return btn;
25972     },
25973
25974      /**
25975      * Adds a series of form elements (using the xtype property as the factory method.
25976      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25977      * @param {Object} config 
25978      */
25979     
25980     addxtype : function()
25981     {
25982         var ar = Array.prototype.slice.call(arguments, 0);
25983         var ret = false;
25984         for(var i = 0; i < ar.length; i++) {
25985             if (!ar[i]) {
25986                 continue; // skip -- if this happends something invalid got sent, we 
25987                 // should ignore it, as basically that interface element will not show up
25988                 // and that should be pretty obvious!!
25989             }
25990             
25991             if (Roo.form[ar[i].xtype]) {
25992                 ar[i].form = this;
25993                 var fe = Roo.factory(ar[i], Roo.form);
25994                 if (!ret) {
25995                     ret = fe;
25996                 }
25997                 fe.form = this;
25998                 if (fe.store) {
25999                     fe.store.form = this;
26000                 }
26001                 if (fe.isLayout) {  
26002                          
26003                     this.start(fe);
26004                     this.allItems.push(fe);
26005                     if (fe.items && fe.addxtype) {
26006                         fe.addxtype.apply(fe, fe.items);
26007                         delete fe.items;
26008                     }
26009                      this.end();
26010                     continue;
26011                 }
26012                 
26013                 
26014                  
26015                 this.add(fe);
26016               //  console.log('adding ' + ar[i].xtype);
26017             }
26018             if (ar[i].xtype == 'Button') {  
26019                 //console.log('adding button');
26020                 //console.log(ar[i]);
26021                 this.addButton(ar[i]);
26022                 this.allItems.push(fe);
26023                 continue;
26024             }
26025             
26026             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26027                 alert('end is not supported on xtype any more, use items');
26028             //    this.end();
26029             //    //console.log('adding end');
26030             }
26031             
26032         }
26033         return ret;
26034     },
26035     
26036     /**
26037      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26038      * option "monitorValid"
26039      */
26040     startMonitoring : function(){
26041         if(!this.bound){
26042             this.bound = true;
26043             Roo.TaskMgr.start({
26044                 run : this.bindHandler,
26045                 interval : this.monitorPoll || 200,
26046                 scope: this
26047             });
26048         }
26049     },
26050
26051     /**
26052      * Stops monitoring of the valid state of this form
26053      */
26054     stopMonitoring : function(){
26055         this.bound = false;
26056     },
26057
26058     // private
26059     bindHandler : function(){
26060         if(!this.bound){
26061             return false; // stops binding
26062         }
26063         var valid = true;
26064         this.items.each(function(f){
26065             if(!f.isValid(true)){
26066                 valid = false;
26067                 return false;
26068             }
26069         });
26070         for(var i = 0, len = this.buttons.length; i < len; i++){
26071             var btn = this.buttons[i];
26072             if(btn.formBind === true && btn.disabled === valid){
26073                 btn.setDisabled(!valid);
26074             }
26075         }
26076         this.fireEvent('clientvalidation', this, valid);
26077     }
26078     
26079     
26080     
26081     
26082     
26083     
26084     
26085     
26086 });
26087
26088
26089 // back compat
26090 Roo.Form = Roo.form.Form;
26091 /*
26092  * Based on:
26093  * Ext JS Library 1.1.1
26094  * Copyright(c) 2006-2007, Ext JS, LLC.
26095  *
26096  * Originally Released Under LGPL - original licence link has changed is not relivant.
26097  *
26098  * Fork - LGPL
26099  * <script type="text/javascript">
26100  */
26101
26102 // as we use this in bootstrap.
26103 Roo.namespace('Roo.form');
26104  /**
26105  * @class Roo.form.Action
26106  * Internal Class used to handle form actions
26107  * @constructor
26108  * @param {Roo.form.BasicForm} el The form element or its id
26109  * @param {Object} config Configuration options
26110  */
26111
26112  
26113  
26114 // define the action interface
26115 Roo.form.Action = function(form, options){
26116     this.form = form;
26117     this.options = options || {};
26118 };
26119 /**
26120  * Client Validation Failed
26121  * @const 
26122  */
26123 Roo.form.Action.CLIENT_INVALID = 'client';
26124 /**
26125  * Server Validation Failed
26126  * @const 
26127  */
26128 Roo.form.Action.SERVER_INVALID = 'server';
26129  /**
26130  * Connect to Server Failed
26131  * @const 
26132  */
26133 Roo.form.Action.CONNECT_FAILURE = 'connect';
26134 /**
26135  * Reading Data from Server Failed
26136  * @const 
26137  */
26138 Roo.form.Action.LOAD_FAILURE = 'load';
26139
26140 Roo.form.Action.prototype = {
26141     type : 'default',
26142     failureType : undefined,
26143     response : undefined,
26144     result : undefined,
26145
26146     // interface method
26147     run : function(options){
26148
26149     },
26150
26151     // interface method
26152     success : function(response){
26153
26154     },
26155
26156     // interface method
26157     handleResponse : function(response){
26158
26159     },
26160
26161     // default connection failure
26162     failure : function(response){
26163         
26164         this.response = response;
26165         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26166         this.form.afterAction(this, false);
26167     },
26168
26169     processResponse : function(response){
26170         this.response = response;
26171         if(!response.responseText){
26172             return true;
26173         }
26174         this.result = this.handleResponse(response);
26175         return this.result;
26176     },
26177
26178     // utility functions used internally
26179     getUrl : function(appendParams){
26180         var url = this.options.url || this.form.url || this.form.el.dom.action;
26181         if(appendParams){
26182             var p = this.getParams();
26183             if(p){
26184                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26185             }
26186         }
26187         return url;
26188     },
26189
26190     getMethod : function(){
26191         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26192     },
26193
26194     getParams : function(){
26195         var bp = this.form.baseParams;
26196         var p = this.options.params;
26197         if(p){
26198             if(typeof p == "object"){
26199                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26200             }else if(typeof p == 'string' && bp){
26201                 p += '&' + Roo.urlEncode(bp);
26202             }
26203         }else if(bp){
26204             p = Roo.urlEncode(bp);
26205         }
26206         return p;
26207     },
26208
26209     createCallback : function(){
26210         return {
26211             success: this.success,
26212             failure: this.failure,
26213             scope: this,
26214             timeout: (this.form.timeout*1000),
26215             upload: this.form.fileUpload ? this.success : undefined
26216         };
26217     }
26218 };
26219
26220 Roo.form.Action.Submit = function(form, options){
26221     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26222 };
26223
26224 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26225     type : 'submit',
26226
26227     haveProgress : false,
26228     uploadComplete : false,
26229     
26230     // uploadProgress indicator.
26231     uploadProgress : function()
26232     {
26233         if (!this.form.progressUrl) {
26234             return;
26235         }
26236         
26237         if (!this.haveProgress) {
26238             Roo.MessageBox.progress("Uploading", "Uploading");
26239         }
26240         if (this.uploadComplete) {
26241            Roo.MessageBox.hide();
26242            return;
26243         }
26244         
26245         this.haveProgress = true;
26246    
26247         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26248         
26249         var c = new Roo.data.Connection();
26250         c.request({
26251             url : this.form.progressUrl,
26252             params: {
26253                 id : uid
26254             },
26255             method: 'GET',
26256             success : function(req){
26257                //console.log(data);
26258                 var rdata = false;
26259                 var edata;
26260                 try  {
26261                    rdata = Roo.decode(req.responseText)
26262                 } catch (e) {
26263                     Roo.log("Invalid data from server..");
26264                     Roo.log(edata);
26265                     return;
26266                 }
26267                 if (!rdata || !rdata.success) {
26268                     Roo.log(rdata);
26269                     Roo.MessageBox.alert(Roo.encode(rdata));
26270                     return;
26271                 }
26272                 var data = rdata.data;
26273                 
26274                 if (this.uploadComplete) {
26275                    Roo.MessageBox.hide();
26276                    return;
26277                 }
26278                    
26279                 if (data){
26280                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26281                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26282                     );
26283                 }
26284                 this.uploadProgress.defer(2000,this);
26285             },
26286        
26287             failure: function(data) {
26288                 Roo.log('progress url failed ');
26289                 Roo.log(data);
26290             },
26291             scope : this
26292         });
26293            
26294     },
26295     
26296     
26297     run : function()
26298     {
26299         // run get Values on the form, so it syncs any secondary forms.
26300         this.form.getValues();
26301         
26302         var o = this.options;
26303         var method = this.getMethod();
26304         var isPost = method == 'POST';
26305         if(o.clientValidation === false || this.form.isValid()){
26306             
26307             if (this.form.progressUrl) {
26308                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26309                     (new Date() * 1) + '' + Math.random());
26310                     
26311             } 
26312             
26313             
26314             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26315                 form:this.form.el.dom,
26316                 url:this.getUrl(!isPost),
26317                 method: method,
26318                 params:isPost ? this.getParams() : null,
26319                 isUpload: this.form.fileUpload,
26320                 formData : this.form.formData
26321             }));
26322             
26323             this.uploadProgress();
26324
26325         }else if (o.clientValidation !== false){ // client validation failed
26326             this.failureType = Roo.form.Action.CLIENT_INVALID;
26327             this.form.afterAction(this, false);
26328         }
26329     },
26330
26331     success : function(response)
26332     {
26333         this.uploadComplete= true;
26334         if (this.haveProgress) {
26335             Roo.MessageBox.hide();
26336         }
26337         
26338         
26339         var result = this.processResponse(response);
26340         if(result === true || result.success){
26341             this.form.afterAction(this, true);
26342             return;
26343         }
26344         if(result.errors){
26345             this.form.markInvalid(result.errors);
26346             this.failureType = Roo.form.Action.SERVER_INVALID;
26347         }
26348         this.form.afterAction(this, false);
26349     },
26350     failure : function(response)
26351     {
26352         this.uploadComplete= true;
26353         if (this.haveProgress) {
26354             Roo.MessageBox.hide();
26355         }
26356         
26357         this.response = response;
26358         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26359         this.form.afterAction(this, false);
26360     },
26361     
26362     handleResponse : function(response){
26363         if(this.form.errorReader){
26364             var rs = this.form.errorReader.read(response);
26365             var errors = [];
26366             if(rs.records){
26367                 for(var i = 0, len = rs.records.length; i < len; i++) {
26368                     var r = rs.records[i];
26369                     errors[i] = r.data;
26370                 }
26371             }
26372             if(errors.length < 1){
26373                 errors = null;
26374             }
26375             return {
26376                 success : rs.success,
26377                 errors : errors
26378             };
26379         }
26380         var ret = false;
26381         try {
26382             ret = Roo.decode(response.responseText);
26383         } catch (e) {
26384             ret = {
26385                 success: false,
26386                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26387                 errors : []
26388             };
26389         }
26390         return ret;
26391         
26392     }
26393 });
26394
26395
26396 Roo.form.Action.Load = function(form, options){
26397     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26398     this.reader = this.form.reader;
26399 };
26400
26401 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26402     type : 'load',
26403
26404     run : function(){
26405         
26406         Roo.Ajax.request(Roo.apply(
26407                 this.createCallback(), {
26408                     method:this.getMethod(),
26409                     url:this.getUrl(false),
26410                     params:this.getParams()
26411         }));
26412     },
26413
26414     success : function(response){
26415         
26416         var result = this.processResponse(response);
26417         if(result === true || !result.success || !result.data){
26418             this.failureType = Roo.form.Action.LOAD_FAILURE;
26419             this.form.afterAction(this, false);
26420             return;
26421         }
26422         this.form.clearInvalid();
26423         this.form.setValues(result.data);
26424         this.form.afterAction(this, true);
26425     },
26426
26427     handleResponse : function(response){
26428         if(this.form.reader){
26429             var rs = this.form.reader.read(response);
26430             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26431             return {
26432                 success : rs.success,
26433                 data : data
26434             };
26435         }
26436         return Roo.decode(response.responseText);
26437     }
26438 });
26439
26440 Roo.form.Action.ACTION_TYPES = {
26441     'load' : Roo.form.Action.Load,
26442     'submit' : Roo.form.Action.Submit
26443 };/*
26444  * Based on:
26445  * Ext JS Library 1.1.1
26446  * Copyright(c) 2006-2007, Ext JS, LLC.
26447  *
26448  * Originally Released Under LGPL - original licence link has changed is not relivant.
26449  *
26450  * Fork - LGPL
26451  * <script type="text/javascript">
26452  */
26453  
26454 /**
26455  * @class Roo.form.Layout
26456  * @extends Roo.Component
26457  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26458  * @constructor
26459  * @param {Object} config Configuration options
26460  */
26461 Roo.form.Layout = function(config){
26462     var xitems = [];
26463     if (config.items) {
26464         xitems = config.items;
26465         delete config.items;
26466     }
26467     Roo.form.Layout.superclass.constructor.call(this, config);
26468     this.stack = [];
26469     Roo.each(xitems, this.addxtype, this);
26470      
26471 };
26472
26473 Roo.extend(Roo.form.Layout, Roo.Component, {
26474     /**
26475      * @cfg {String/Object} autoCreate
26476      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26477      */
26478     /**
26479      * @cfg {String/Object/Function} style
26480      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26481      * a function which returns such a specification.
26482      */
26483     /**
26484      * @cfg {String} labelAlign
26485      * Valid values are "left," "top" and "right" (defaults to "left")
26486      */
26487     /**
26488      * @cfg {Number} labelWidth
26489      * Fixed width in pixels of all field labels (defaults to undefined)
26490      */
26491     /**
26492      * @cfg {Boolean} clear
26493      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26494      */
26495     clear : true,
26496     /**
26497      * @cfg {String} labelSeparator
26498      * The separator to use after field labels (defaults to ':')
26499      */
26500     labelSeparator : ':',
26501     /**
26502      * @cfg {Boolean} hideLabels
26503      * True to suppress the display of field labels in this layout (defaults to false)
26504      */
26505     hideLabels : false,
26506
26507     // private
26508     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26509     
26510     isLayout : true,
26511     
26512     // private
26513     onRender : function(ct, position){
26514         if(this.el){ // from markup
26515             this.el = Roo.get(this.el);
26516         }else {  // generate
26517             var cfg = this.getAutoCreate();
26518             this.el = ct.createChild(cfg, position);
26519         }
26520         if(this.style){
26521             this.el.applyStyles(this.style);
26522         }
26523         if(this.labelAlign){
26524             this.el.addClass('x-form-label-'+this.labelAlign);
26525         }
26526         if(this.hideLabels){
26527             this.labelStyle = "display:none";
26528             this.elementStyle = "padding-left:0;";
26529         }else{
26530             if(typeof this.labelWidth == 'number'){
26531                 this.labelStyle = "width:"+this.labelWidth+"px;";
26532                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26533             }
26534             if(this.labelAlign == 'top'){
26535                 this.labelStyle = "width:auto;";
26536                 this.elementStyle = "padding-left:0;";
26537             }
26538         }
26539         var stack = this.stack;
26540         var slen = stack.length;
26541         if(slen > 0){
26542             if(!this.fieldTpl){
26543                 var t = new Roo.Template(
26544                     '<div class="x-form-item {5}">',
26545                         '<label for="{0}" style="{2}">{1}{4}</label>',
26546                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26547                         '</div>',
26548                     '</div><div class="x-form-clear-left"></div>'
26549                 );
26550                 t.disableFormats = true;
26551                 t.compile();
26552                 Roo.form.Layout.prototype.fieldTpl = t;
26553             }
26554             for(var i = 0; i < slen; i++) {
26555                 if(stack[i].isFormField){
26556                     this.renderField(stack[i]);
26557                 }else{
26558                     this.renderComponent(stack[i]);
26559                 }
26560             }
26561         }
26562         if(this.clear){
26563             this.el.createChild({cls:'x-form-clear'});
26564         }
26565     },
26566
26567     // private
26568     renderField : function(f){
26569         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26570                f.id, //0
26571                f.fieldLabel, //1
26572                f.labelStyle||this.labelStyle||'', //2
26573                this.elementStyle||'', //3
26574                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26575                f.itemCls||this.itemCls||''  //5
26576        ], true).getPrevSibling());
26577     },
26578
26579     // private
26580     renderComponent : function(c){
26581         c.render(c.isLayout ? this.el : this.el.createChild());    
26582     },
26583     /**
26584      * Adds a object form elements (using the xtype property as the factory method.)
26585      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26586      * @param {Object} config 
26587      */
26588     addxtype : function(o)
26589     {
26590         // create the lement.
26591         o.form = this.form;
26592         var fe = Roo.factory(o, Roo.form);
26593         this.form.allItems.push(fe);
26594         this.stack.push(fe);
26595         
26596         if (fe.isFormField) {
26597             this.form.items.add(fe);
26598         }
26599          
26600         return fe;
26601     }
26602 });
26603
26604 /**
26605  * @class Roo.form.Column
26606  * @extends Roo.form.Layout
26607  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26608  * @constructor
26609  * @param {Object} config Configuration options
26610  */
26611 Roo.form.Column = function(config){
26612     Roo.form.Column.superclass.constructor.call(this, config);
26613 };
26614
26615 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26616     /**
26617      * @cfg {Number/String} width
26618      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26619      */
26620     /**
26621      * @cfg {String/Object} autoCreate
26622      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26623      */
26624
26625     // private
26626     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26627
26628     // private
26629     onRender : function(ct, position){
26630         Roo.form.Column.superclass.onRender.call(this, ct, position);
26631         if(this.width){
26632             this.el.setWidth(this.width);
26633         }
26634     }
26635 });
26636
26637
26638 /**
26639  * @class Roo.form.Row
26640  * @extends Roo.form.Layout
26641  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26642  * @constructor
26643  * @param {Object} config Configuration options
26644  */
26645
26646  
26647 Roo.form.Row = function(config){
26648     Roo.form.Row.superclass.constructor.call(this, config);
26649 };
26650  
26651 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26652       /**
26653      * @cfg {Number/String} width
26654      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26655      */
26656     /**
26657      * @cfg {Number/String} height
26658      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26659      */
26660     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26661     
26662     padWidth : 20,
26663     // private
26664     onRender : function(ct, position){
26665         //console.log('row render');
26666         if(!this.rowTpl){
26667             var t = new Roo.Template(
26668                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26669                     '<label for="{0}" style="{2}">{1}{4}</label>',
26670                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26671                     '</div>',
26672                 '</div>'
26673             );
26674             t.disableFormats = true;
26675             t.compile();
26676             Roo.form.Layout.prototype.rowTpl = t;
26677         }
26678         this.fieldTpl = this.rowTpl;
26679         
26680         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26681         var labelWidth = 100;
26682         
26683         if ((this.labelAlign != 'top')) {
26684             if (typeof this.labelWidth == 'number') {
26685                 labelWidth = this.labelWidth
26686             }
26687             this.padWidth =  20 + labelWidth;
26688             
26689         }
26690         
26691         Roo.form.Column.superclass.onRender.call(this, ct, position);
26692         if(this.width){
26693             this.el.setWidth(this.width);
26694         }
26695         if(this.height){
26696             this.el.setHeight(this.height);
26697         }
26698     },
26699     
26700     // private
26701     renderField : function(f){
26702         f.fieldEl = this.fieldTpl.append(this.el, [
26703                f.id, f.fieldLabel,
26704                f.labelStyle||this.labelStyle||'',
26705                this.elementStyle||'',
26706                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26707                f.itemCls||this.itemCls||'',
26708                f.width ? f.width + this.padWidth : 160 + this.padWidth
26709        ],true);
26710     }
26711 });
26712  
26713
26714 /**
26715  * @class Roo.form.FieldSet
26716  * @extends Roo.form.Layout
26717  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26718  * @constructor
26719  * @param {Object} config Configuration options
26720  */
26721 Roo.form.FieldSet = function(config){
26722     Roo.form.FieldSet.superclass.constructor.call(this, config);
26723 };
26724
26725 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26726     /**
26727      * @cfg {String} legend
26728      * The text to display as the legend for the FieldSet (defaults to '')
26729      */
26730     /**
26731      * @cfg {String/Object} autoCreate
26732      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26733      */
26734
26735     // private
26736     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26737
26738     // private
26739     onRender : function(ct, position){
26740         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26741         if(this.legend){
26742             this.setLegend(this.legend);
26743         }
26744     },
26745
26746     // private
26747     setLegend : function(text){
26748         if(this.rendered){
26749             this.el.child('legend').update(text);
26750         }
26751     }
26752 });/*
26753  * Based on:
26754  * Ext JS Library 1.1.1
26755  * Copyright(c) 2006-2007, Ext JS, LLC.
26756  *
26757  * Originally Released Under LGPL - original licence link has changed is not relivant.
26758  *
26759  * Fork - LGPL
26760  * <script type="text/javascript">
26761  */
26762 /**
26763  * @class Roo.form.VTypes
26764  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26765  * @singleton
26766  */
26767 Roo.form.VTypes = function(){
26768     // closure these in so they are only created once.
26769     var alpha = /^[a-zA-Z_]+$/;
26770     var alphanum = /^[a-zA-Z0-9_]+$/;
26771     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26772     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26773
26774     // All these messages and functions are configurable
26775     return {
26776         /**
26777          * The function used to validate email addresses
26778          * @param {String} value The email address
26779          */
26780         'email' : function(v){
26781             return email.test(v);
26782         },
26783         /**
26784          * The error text to display when the email validation function returns false
26785          * @type String
26786          */
26787         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26788         /**
26789          * The keystroke filter mask to be applied on email input
26790          * @type RegExp
26791          */
26792         'emailMask' : /[a-z0-9_\.\-@]/i,
26793
26794         /**
26795          * The function used to validate URLs
26796          * @param {String} value The URL
26797          */
26798         'url' : function(v){
26799             return url.test(v);
26800         },
26801         /**
26802          * The error text to display when the url validation function returns false
26803          * @type String
26804          */
26805         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26806         
26807         /**
26808          * The function used to validate alpha values
26809          * @param {String} value The value
26810          */
26811         'alpha' : function(v){
26812             return alpha.test(v);
26813         },
26814         /**
26815          * The error text to display when the alpha validation function returns false
26816          * @type String
26817          */
26818         'alphaText' : 'This field should only contain letters and _',
26819         /**
26820          * The keystroke filter mask to be applied on alpha input
26821          * @type RegExp
26822          */
26823         'alphaMask' : /[a-z_]/i,
26824
26825         /**
26826          * The function used to validate alphanumeric values
26827          * @param {String} value The value
26828          */
26829         'alphanum' : function(v){
26830             return alphanum.test(v);
26831         },
26832         /**
26833          * The error text to display when the alphanumeric validation function returns false
26834          * @type String
26835          */
26836         'alphanumText' : 'This field should only contain letters, numbers and _',
26837         /**
26838          * The keystroke filter mask to be applied on alphanumeric input
26839          * @type RegExp
26840          */
26841         'alphanumMask' : /[a-z0-9_]/i
26842     };
26843 }();//<script type="text/javascript">
26844
26845 /**
26846  * @class Roo.form.FCKeditor
26847  * @extends Roo.form.TextArea
26848  * Wrapper around the FCKEditor http://www.fckeditor.net
26849  * @constructor
26850  * Creates a new FCKeditor
26851  * @param {Object} config Configuration options
26852  */
26853 Roo.form.FCKeditor = function(config){
26854     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26855     this.addEvents({
26856          /**
26857          * @event editorinit
26858          * Fired when the editor is initialized - you can add extra handlers here..
26859          * @param {FCKeditor} this
26860          * @param {Object} the FCK object.
26861          */
26862         editorinit : true
26863     });
26864     
26865     
26866 };
26867 Roo.form.FCKeditor.editors = { };
26868 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26869 {
26870     //defaultAutoCreate : {
26871     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26872     //},
26873     // private
26874     /**
26875      * @cfg {Object} fck options - see fck manual for details.
26876      */
26877     fckconfig : false,
26878     
26879     /**
26880      * @cfg {Object} fck toolbar set (Basic or Default)
26881      */
26882     toolbarSet : 'Basic',
26883     /**
26884      * @cfg {Object} fck BasePath
26885      */ 
26886     basePath : '/fckeditor/',
26887     
26888     
26889     frame : false,
26890     
26891     value : '',
26892     
26893    
26894     onRender : function(ct, position)
26895     {
26896         if(!this.el){
26897             this.defaultAutoCreate = {
26898                 tag: "textarea",
26899                 style:"width:300px;height:60px;",
26900                 autocomplete: "new-password"
26901             };
26902         }
26903         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26904         /*
26905         if(this.grow){
26906             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26907             if(this.preventScrollbars){
26908                 this.el.setStyle("overflow", "hidden");
26909             }
26910             this.el.setHeight(this.growMin);
26911         }
26912         */
26913         //console.log('onrender' + this.getId() );
26914         Roo.form.FCKeditor.editors[this.getId()] = this;
26915          
26916
26917         this.replaceTextarea() ;
26918         
26919     },
26920     
26921     getEditor : function() {
26922         return this.fckEditor;
26923     },
26924     /**
26925      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26926      * @param {Mixed} value The value to set
26927      */
26928     
26929     
26930     setValue : function(value)
26931     {
26932         //console.log('setValue: ' + value);
26933         
26934         if(typeof(value) == 'undefined') { // not sure why this is happending...
26935             return;
26936         }
26937         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26938         
26939         //if(!this.el || !this.getEditor()) {
26940         //    this.value = value;
26941             //this.setValue.defer(100,this,[value]);    
26942         //    return;
26943         //} 
26944         
26945         if(!this.getEditor()) {
26946             return;
26947         }
26948         
26949         this.getEditor().SetData(value);
26950         
26951         //
26952
26953     },
26954
26955     /**
26956      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26957      * @return {Mixed} value The field value
26958      */
26959     getValue : function()
26960     {
26961         
26962         if (this.frame && this.frame.dom.style.display == 'none') {
26963             return Roo.form.FCKeditor.superclass.getValue.call(this);
26964         }
26965         
26966         if(!this.el || !this.getEditor()) {
26967            
26968            // this.getValue.defer(100,this); 
26969             return this.value;
26970         }
26971        
26972         
26973         var value=this.getEditor().GetData();
26974         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26975         return Roo.form.FCKeditor.superclass.getValue.call(this);
26976         
26977
26978     },
26979
26980     /**
26981      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26982      * @return {Mixed} value The field value
26983      */
26984     getRawValue : function()
26985     {
26986         if (this.frame && this.frame.dom.style.display == 'none') {
26987             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26988         }
26989         
26990         if(!this.el || !this.getEditor()) {
26991             //this.getRawValue.defer(100,this); 
26992             return this.value;
26993             return;
26994         }
26995         
26996         
26997         
26998         var value=this.getEditor().GetData();
26999         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27000         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27001          
27002     },
27003     
27004     setSize : function(w,h) {
27005         
27006         
27007         
27008         //if (this.frame && this.frame.dom.style.display == 'none') {
27009         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27010         //    return;
27011         //}
27012         //if(!this.el || !this.getEditor()) {
27013         //    this.setSize.defer(100,this, [w,h]); 
27014         //    return;
27015         //}
27016         
27017         
27018         
27019         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27020         
27021         this.frame.dom.setAttribute('width', w);
27022         this.frame.dom.setAttribute('height', h);
27023         this.frame.setSize(w,h);
27024         
27025     },
27026     
27027     toggleSourceEdit : function(value) {
27028         
27029       
27030          
27031         this.el.dom.style.display = value ? '' : 'none';
27032         this.frame.dom.style.display = value ?  'none' : '';
27033         
27034     },
27035     
27036     
27037     focus: function(tag)
27038     {
27039         if (this.frame.dom.style.display == 'none') {
27040             return Roo.form.FCKeditor.superclass.focus.call(this);
27041         }
27042         if(!this.el || !this.getEditor()) {
27043             this.focus.defer(100,this, [tag]); 
27044             return;
27045         }
27046         
27047         
27048         
27049         
27050         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27051         this.getEditor().Focus();
27052         if (tgs.length) {
27053             if (!this.getEditor().Selection.GetSelection()) {
27054                 this.focus.defer(100,this, [tag]); 
27055                 return;
27056             }
27057             
27058             
27059             var r = this.getEditor().EditorDocument.createRange();
27060             r.setStart(tgs[0],0);
27061             r.setEnd(tgs[0],0);
27062             this.getEditor().Selection.GetSelection().removeAllRanges();
27063             this.getEditor().Selection.GetSelection().addRange(r);
27064             this.getEditor().Focus();
27065         }
27066         
27067     },
27068     
27069     
27070     
27071     replaceTextarea : function()
27072     {
27073         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27074             return ;
27075         }
27076         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27077         //{
27078             // We must check the elements firstly using the Id and then the name.
27079         var oTextarea = document.getElementById( this.getId() );
27080         
27081         var colElementsByName = document.getElementsByName( this.getId() ) ;
27082          
27083         oTextarea.style.display = 'none' ;
27084
27085         if ( oTextarea.tabIndex ) {            
27086             this.TabIndex = oTextarea.tabIndex ;
27087         }
27088         
27089         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27090         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27091         this.frame = Roo.get(this.getId() + '___Frame')
27092     },
27093     
27094     _getConfigHtml : function()
27095     {
27096         var sConfig = '' ;
27097
27098         for ( var o in this.fckconfig ) {
27099             sConfig += sConfig.length > 0  ? '&amp;' : '';
27100             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27101         }
27102
27103         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27104     },
27105     
27106     
27107     _getIFrameHtml : function()
27108     {
27109         var sFile = 'fckeditor.html' ;
27110         /* no idea what this is about..
27111         try
27112         {
27113             if ( (/fcksource=true/i).test( window.top.location.search ) )
27114                 sFile = 'fckeditor.original.html' ;
27115         }
27116         catch (e) { 
27117         */
27118
27119         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27120         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27121         
27122         
27123         var html = '<iframe id="' + this.getId() +
27124             '___Frame" src="' + sLink +
27125             '" width="' + this.width +
27126             '" height="' + this.height + '"' +
27127             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27128             ' frameborder="0" scrolling="no"></iframe>' ;
27129
27130         return html ;
27131     },
27132     
27133     _insertHtmlBefore : function( html, element )
27134     {
27135         if ( element.insertAdjacentHTML )       {
27136             // IE
27137             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27138         } else { // Gecko
27139             var oRange = document.createRange() ;
27140             oRange.setStartBefore( element ) ;
27141             var oFragment = oRange.createContextualFragment( html );
27142             element.parentNode.insertBefore( oFragment, element ) ;
27143         }
27144     }
27145     
27146     
27147   
27148     
27149     
27150     
27151     
27152
27153 });
27154
27155 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27156
27157 function FCKeditor_OnComplete(editorInstance){
27158     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27159     f.fckEditor = editorInstance;
27160     //console.log("loaded");
27161     f.fireEvent('editorinit', f, editorInstance);
27162
27163   
27164
27165  
27166
27167
27168
27169
27170
27171
27172
27173
27174
27175
27176
27177
27178
27179
27180
27181 //<script type="text/javascript">
27182 /**
27183  * @class Roo.form.GridField
27184  * @extends Roo.form.Field
27185  * Embed a grid (or editable grid into a form)
27186  * STATUS ALPHA
27187  * 
27188  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27189  * it needs 
27190  * xgrid.store = Roo.data.Store
27191  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27192  * xgrid.store.reader = Roo.data.JsonReader 
27193  * 
27194  * 
27195  * @constructor
27196  * Creates a new GridField
27197  * @param {Object} config Configuration options
27198  */
27199 Roo.form.GridField = function(config){
27200     Roo.form.GridField.superclass.constructor.call(this, config);
27201      
27202 };
27203
27204 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27205     /**
27206      * @cfg {Number} width  - used to restrict width of grid..
27207      */
27208     width : 100,
27209     /**
27210      * @cfg {Number} height - used to restrict height of grid..
27211      */
27212     height : 50,
27213      /**
27214      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27215          * 
27216          *}
27217      */
27218     xgrid : false, 
27219     /**
27220      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27221      * {tag: "input", type: "checkbox", autocomplete: "off"})
27222      */
27223    // defaultAutoCreate : { tag: 'div' },
27224     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27225     /**
27226      * @cfg {String} addTitle Text to include for adding a title.
27227      */
27228     addTitle : false,
27229     //
27230     onResize : function(){
27231         Roo.form.Field.superclass.onResize.apply(this, arguments);
27232     },
27233
27234     initEvents : function(){
27235         // Roo.form.Checkbox.superclass.initEvents.call(this);
27236         // has no events...
27237        
27238     },
27239
27240
27241     getResizeEl : function(){
27242         return this.wrap;
27243     },
27244
27245     getPositionEl : function(){
27246         return this.wrap;
27247     },
27248
27249     // private
27250     onRender : function(ct, position){
27251         
27252         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27253         var style = this.style;
27254         delete this.style;
27255         
27256         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27257         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27258         this.viewEl = this.wrap.createChild({ tag: 'div' });
27259         if (style) {
27260             this.viewEl.applyStyles(style);
27261         }
27262         if (this.width) {
27263             this.viewEl.setWidth(this.width);
27264         }
27265         if (this.height) {
27266             this.viewEl.setHeight(this.height);
27267         }
27268         //if(this.inputValue !== undefined){
27269         //this.setValue(this.value);
27270         
27271         
27272         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27273         
27274         
27275         this.grid.render();
27276         this.grid.getDataSource().on('remove', this.refreshValue, this);
27277         this.grid.getDataSource().on('update', this.refreshValue, this);
27278         this.grid.on('afteredit', this.refreshValue, this);
27279  
27280     },
27281      
27282     
27283     /**
27284      * Sets the value of the item. 
27285      * @param {String} either an object  or a string..
27286      */
27287     setValue : function(v){
27288         //this.value = v;
27289         v = v || []; // empty set..
27290         // this does not seem smart - it really only affects memoryproxy grids..
27291         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27292             var ds = this.grid.getDataSource();
27293             // assumes a json reader..
27294             var data = {}
27295             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27296             ds.loadData( data);
27297         }
27298         // clear selection so it does not get stale.
27299         if (this.grid.sm) { 
27300             this.grid.sm.clearSelections();
27301         }
27302         
27303         Roo.form.GridField.superclass.setValue.call(this, v);
27304         this.refreshValue();
27305         // should load data in the grid really....
27306     },
27307     
27308     // private
27309     refreshValue: function() {
27310          var val = [];
27311         this.grid.getDataSource().each(function(r) {
27312             val.push(r.data);
27313         });
27314         this.el.dom.value = Roo.encode(val);
27315     }
27316     
27317      
27318     
27319     
27320 });/*
27321  * Based on:
27322  * Ext JS Library 1.1.1
27323  * Copyright(c) 2006-2007, Ext JS, LLC.
27324  *
27325  * Originally Released Under LGPL - original licence link has changed is not relivant.
27326  *
27327  * Fork - LGPL
27328  * <script type="text/javascript">
27329  */
27330 /**
27331  * @class Roo.form.DisplayField
27332  * @extends Roo.form.Field
27333  * A generic Field to display non-editable data.
27334  * @cfg {Boolean} closable (true|false) default false
27335  * @constructor
27336  * Creates a new Display Field item.
27337  * @param {Object} config Configuration options
27338  */
27339 Roo.form.DisplayField = function(config){
27340     Roo.form.DisplayField.superclass.constructor.call(this, config);
27341     
27342     this.addEvents({
27343         /**
27344          * @event close
27345          * Fires after the click the close btn
27346              * @param {Roo.form.DisplayField} this
27347              */
27348         close : true
27349     });
27350 };
27351
27352 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27353     inputType:      'hidden',
27354     allowBlank:     true,
27355     readOnly:         true,
27356     
27357  
27358     /**
27359      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27360      */
27361     focusClass : undefined,
27362     /**
27363      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27364      */
27365     fieldClass: 'x-form-field',
27366     
27367      /**
27368      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27369      */
27370     valueRenderer: undefined,
27371     
27372     width: 100,
27373     /**
27374      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27375      * {tag: "input", type: "checkbox", autocomplete: "off"})
27376      */
27377      
27378  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27379  
27380     closable : false,
27381     
27382     onResize : function(){
27383         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27384         
27385     },
27386
27387     initEvents : function(){
27388         // Roo.form.Checkbox.superclass.initEvents.call(this);
27389         // has no events...
27390         
27391         if(this.closable){
27392             this.closeEl.on('click', this.onClose, this);
27393         }
27394        
27395     },
27396
27397
27398     getResizeEl : function(){
27399         return this.wrap;
27400     },
27401
27402     getPositionEl : function(){
27403         return this.wrap;
27404     },
27405
27406     // private
27407     onRender : function(ct, position){
27408         
27409         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27410         //if(this.inputValue !== undefined){
27411         this.wrap = this.el.wrap();
27412         
27413         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27414         
27415         if(this.closable){
27416             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27417         }
27418         
27419         if (this.bodyStyle) {
27420             this.viewEl.applyStyles(this.bodyStyle);
27421         }
27422         //this.viewEl.setStyle('padding', '2px');
27423         
27424         this.setValue(this.value);
27425         
27426     },
27427 /*
27428     // private
27429     initValue : Roo.emptyFn,
27430
27431   */
27432
27433         // private
27434     onClick : function(){
27435         
27436     },
27437
27438     /**
27439      * Sets the checked state of the checkbox.
27440      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27441      */
27442     setValue : function(v){
27443         this.value = v;
27444         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27445         // this might be called before we have a dom element..
27446         if (!this.viewEl) {
27447             return;
27448         }
27449         this.viewEl.dom.innerHTML = html;
27450         Roo.form.DisplayField.superclass.setValue.call(this, v);
27451
27452     },
27453     
27454     onClose : function(e)
27455     {
27456         e.preventDefault();
27457         
27458         this.fireEvent('close', this);
27459     }
27460 });/*
27461  * 
27462  * Licence- LGPL
27463  * 
27464  */
27465
27466 /**
27467  * @class Roo.form.DayPicker
27468  * @extends Roo.form.Field
27469  * A Day picker show [M] [T] [W] ....
27470  * @constructor
27471  * Creates a new Day Picker
27472  * @param {Object} config Configuration options
27473  */
27474 Roo.form.DayPicker= function(config){
27475     Roo.form.DayPicker.superclass.constructor.call(this, config);
27476      
27477 };
27478
27479 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27480     /**
27481      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27482      */
27483     focusClass : undefined,
27484     /**
27485      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27486      */
27487     fieldClass: "x-form-field",
27488    
27489     /**
27490      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27491      * {tag: "input", type: "checkbox", autocomplete: "off"})
27492      */
27493     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27494     
27495    
27496     actionMode : 'viewEl', 
27497     //
27498     // private
27499  
27500     inputType : 'hidden',
27501     
27502      
27503     inputElement: false, // real input element?
27504     basedOn: false, // ????
27505     
27506     isFormField: true, // not sure where this is needed!!!!
27507
27508     onResize : function(){
27509         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27510         if(!this.boxLabel){
27511             this.el.alignTo(this.wrap, 'c-c');
27512         }
27513     },
27514
27515     initEvents : function(){
27516         Roo.form.Checkbox.superclass.initEvents.call(this);
27517         this.el.on("click", this.onClick,  this);
27518         this.el.on("change", this.onClick,  this);
27519     },
27520
27521
27522     getResizeEl : function(){
27523         return this.wrap;
27524     },
27525
27526     getPositionEl : function(){
27527         return this.wrap;
27528     },
27529
27530     
27531     // private
27532     onRender : function(ct, position){
27533         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27534        
27535         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27536         
27537         var r1 = '<table><tr>';
27538         var r2 = '<tr class="x-form-daypick-icons">';
27539         for (var i=0; i < 7; i++) {
27540             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27541             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27542         }
27543         
27544         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27545         viewEl.select('img').on('click', this.onClick, this);
27546         this.viewEl = viewEl;   
27547         
27548         
27549         // this will not work on Chrome!!!
27550         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27551         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27552         
27553         
27554           
27555
27556     },
27557
27558     // private
27559     initValue : Roo.emptyFn,
27560
27561     /**
27562      * Returns the checked state of the checkbox.
27563      * @return {Boolean} True if checked, else false
27564      */
27565     getValue : function(){
27566         return this.el.dom.value;
27567         
27568     },
27569
27570         // private
27571     onClick : function(e){ 
27572         //this.setChecked(!this.checked);
27573         Roo.get(e.target).toggleClass('x-menu-item-checked');
27574         this.refreshValue();
27575         //if(this.el.dom.checked != this.checked){
27576         //    this.setValue(this.el.dom.checked);
27577        // }
27578     },
27579     
27580     // private
27581     refreshValue : function()
27582     {
27583         var val = '';
27584         this.viewEl.select('img',true).each(function(e,i,n)  {
27585             val += e.is(".x-menu-item-checked") ? String(n) : '';
27586         });
27587         this.setValue(val, true);
27588     },
27589
27590     /**
27591      * Sets the checked state of the checkbox.
27592      * On is always based on a string comparison between inputValue and the param.
27593      * @param {Boolean/String} value - the value to set 
27594      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27595      */
27596     setValue : function(v,suppressEvent){
27597         if (!this.el.dom) {
27598             return;
27599         }
27600         var old = this.el.dom.value ;
27601         this.el.dom.value = v;
27602         if (suppressEvent) {
27603             return ;
27604         }
27605          
27606         // update display..
27607         this.viewEl.select('img',true).each(function(e,i,n)  {
27608             
27609             var on = e.is(".x-menu-item-checked");
27610             var newv = v.indexOf(String(n)) > -1;
27611             if (on != newv) {
27612                 e.toggleClass('x-menu-item-checked');
27613             }
27614             
27615         });
27616         
27617         
27618         this.fireEvent('change', this, v, old);
27619         
27620         
27621     },
27622    
27623     // handle setting of hidden value by some other method!!?!?
27624     setFromHidden: function()
27625     {
27626         if(!this.el){
27627             return;
27628         }
27629         //console.log("SET FROM HIDDEN");
27630         //alert('setFrom hidden');
27631         this.setValue(this.el.dom.value);
27632     },
27633     
27634     onDestroy : function()
27635     {
27636         if(this.viewEl){
27637             Roo.get(this.viewEl).remove();
27638         }
27639          
27640         Roo.form.DayPicker.superclass.onDestroy.call(this);
27641     }
27642
27643 });/*
27644  * RooJS Library 1.1.1
27645  * Copyright(c) 2008-2011  Alan Knowles
27646  *
27647  * License - LGPL
27648  */
27649  
27650
27651 /**
27652  * @class Roo.form.ComboCheck
27653  * @extends Roo.form.ComboBox
27654  * A combobox for multiple select items.
27655  *
27656  * FIXME - could do with a reset button..
27657  * 
27658  * @constructor
27659  * Create a new ComboCheck
27660  * @param {Object} config Configuration options
27661  */
27662 Roo.form.ComboCheck = function(config){
27663     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27664     // should verify some data...
27665     // like
27666     // hiddenName = required..
27667     // displayField = required
27668     // valudField == required
27669     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27670     var _t = this;
27671     Roo.each(req, function(e) {
27672         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27673             throw "Roo.form.ComboCheck : missing value for: " + e;
27674         }
27675     });
27676     
27677     
27678 };
27679
27680 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27681      
27682      
27683     editable : false,
27684      
27685     selectedClass: 'x-menu-item-checked', 
27686     
27687     // private
27688     onRender : function(ct, position){
27689         var _t = this;
27690         
27691         
27692         
27693         if(!this.tpl){
27694             var cls = 'x-combo-list';
27695
27696             
27697             this.tpl =  new Roo.Template({
27698                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27699                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27700                    '<span>{' + this.displayField + '}</span>' +
27701                     '</div>' 
27702                 
27703             });
27704         }
27705  
27706         
27707         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27708         this.view.singleSelect = false;
27709         this.view.multiSelect = true;
27710         this.view.toggleSelect = true;
27711         this.pageTb.add(new Roo.Toolbar.Fill(), {
27712             
27713             text: 'Done',
27714             handler: function()
27715             {
27716                 _t.collapse();
27717             }
27718         });
27719     },
27720     
27721     onViewOver : function(e, t){
27722         // do nothing...
27723         return;
27724         
27725     },
27726     
27727     onViewClick : function(doFocus,index){
27728         return;
27729         
27730     },
27731     select: function () {
27732         //Roo.log("SELECT CALLED");
27733     },
27734      
27735     selectByValue : function(xv, scrollIntoView){
27736         var ar = this.getValueArray();
27737         var sels = [];
27738         
27739         Roo.each(ar, function(v) {
27740             if(v === undefined || v === null){
27741                 return;
27742             }
27743             var r = this.findRecord(this.valueField, v);
27744             if(r){
27745                 sels.push(this.store.indexOf(r))
27746                 
27747             }
27748         },this);
27749         this.view.select(sels);
27750         return false;
27751     },
27752     
27753     
27754     
27755     onSelect : function(record, index){
27756        // Roo.log("onselect Called");
27757        // this is only called by the clear button now..
27758         this.view.clearSelections();
27759         this.setValue('[]');
27760         if (this.value != this.valueBefore) {
27761             this.fireEvent('change', this, this.value, this.valueBefore);
27762             this.valueBefore = this.value;
27763         }
27764     },
27765     getValueArray : function()
27766     {
27767         var ar = [] ;
27768         
27769         try {
27770             //Roo.log(this.value);
27771             if (typeof(this.value) == 'undefined') {
27772                 return [];
27773             }
27774             var ar = Roo.decode(this.value);
27775             return  ar instanceof Array ? ar : []; //?? valid?
27776             
27777         } catch(e) {
27778             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27779             return [];
27780         }
27781          
27782     },
27783     expand : function ()
27784     {
27785         
27786         Roo.form.ComboCheck.superclass.expand.call(this);
27787         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27788         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27789         
27790
27791     },
27792     
27793     collapse : function(){
27794         Roo.form.ComboCheck.superclass.collapse.call(this);
27795         var sl = this.view.getSelectedIndexes();
27796         var st = this.store;
27797         var nv = [];
27798         var tv = [];
27799         var r;
27800         Roo.each(sl, function(i) {
27801             r = st.getAt(i);
27802             nv.push(r.get(this.valueField));
27803         },this);
27804         this.setValue(Roo.encode(nv));
27805         if (this.value != this.valueBefore) {
27806
27807             this.fireEvent('change', this, this.value, this.valueBefore);
27808             this.valueBefore = this.value;
27809         }
27810         
27811     },
27812     
27813     setValue : function(v){
27814         // Roo.log(v);
27815         this.value = v;
27816         
27817         var vals = this.getValueArray();
27818         var tv = [];
27819         Roo.each(vals, function(k) {
27820             var r = this.findRecord(this.valueField, k);
27821             if(r){
27822                 tv.push(r.data[this.displayField]);
27823             }else if(this.valueNotFoundText !== undefined){
27824                 tv.push( this.valueNotFoundText );
27825             }
27826         },this);
27827        // Roo.log(tv);
27828         
27829         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27830         this.hiddenField.value = v;
27831         this.value = v;
27832     }
27833     
27834 });/*
27835  * Based on:
27836  * Ext JS Library 1.1.1
27837  * Copyright(c) 2006-2007, Ext JS, LLC.
27838  *
27839  * Originally Released Under LGPL - original licence link has changed is not relivant.
27840  *
27841  * Fork - LGPL
27842  * <script type="text/javascript">
27843  */
27844  
27845 /**
27846  * @class Roo.form.Signature
27847  * @extends Roo.form.Field
27848  * Signature field.  
27849  * @constructor
27850  * 
27851  * @param {Object} config Configuration options
27852  */
27853
27854 Roo.form.Signature = function(config){
27855     Roo.form.Signature.superclass.constructor.call(this, config);
27856     
27857     this.addEvents({// not in used??
27858          /**
27859          * @event confirm
27860          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27861              * @param {Roo.form.Signature} combo This combo box
27862              */
27863         'confirm' : true,
27864         /**
27865          * @event reset
27866          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27867              * @param {Roo.form.ComboBox} combo This combo box
27868              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27869              */
27870         'reset' : true
27871     });
27872 };
27873
27874 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27875     /**
27876      * @cfg {Object} labels Label to use when rendering a form.
27877      * defaults to 
27878      * labels : { 
27879      *      clear : "Clear",
27880      *      confirm : "Confirm"
27881      *  }
27882      */
27883     labels : { 
27884         clear : "Clear",
27885         confirm : "Confirm"
27886     },
27887     /**
27888      * @cfg {Number} width The signature panel width (defaults to 300)
27889      */
27890     width: 300,
27891     /**
27892      * @cfg {Number} height The signature panel height (defaults to 100)
27893      */
27894     height : 100,
27895     /**
27896      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27897      */
27898     allowBlank : false,
27899     
27900     //private
27901     // {Object} signPanel The signature SVG panel element (defaults to {})
27902     signPanel : {},
27903     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27904     isMouseDown : false,
27905     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27906     isConfirmed : false,
27907     // {String} signatureTmp SVG mapping string (defaults to empty string)
27908     signatureTmp : '',
27909     
27910     
27911     defaultAutoCreate : { // modified by initCompnoent..
27912         tag: "input",
27913         type:"hidden"
27914     },
27915
27916     // private
27917     onRender : function(ct, position){
27918         
27919         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27920         
27921         this.wrap = this.el.wrap({
27922             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27923         });
27924         
27925         this.createToolbar(this);
27926         this.signPanel = this.wrap.createChild({
27927                 tag: 'div',
27928                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27929             }, this.el
27930         );
27931             
27932         this.svgID = Roo.id();
27933         this.svgEl = this.signPanel.createChild({
27934               xmlns : 'http://www.w3.org/2000/svg',
27935               tag : 'svg',
27936               id : this.svgID + "-svg",
27937               width: this.width,
27938               height: this.height,
27939               viewBox: '0 0 '+this.width+' '+this.height,
27940               cn : [
27941                 {
27942                     tag: "rect",
27943                     id: this.svgID + "-svg-r",
27944                     width: this.width,
27945                     height: this.height,
27946                     fill: "#ffa"
27947                 },
27948                 {
27949                     tag: "line",
27950                     id: this.svgID + "-svg-l",
27951                     x1: "0", // start
27952                     y1: (this.height*0.8), // start set the line in 80% of height
27953                     x2: this.width, // end
27954                     y2: (this.height*0.8), // end set the line in 80% of height
27955                     'stroke': "#666",
27956                     'stroke-width': "1",
27957                     'stroke-dasharray': "3",
27958                     'shape-rendering': "crispEdges",
27959                     'pointer-events': "none"
27960                 },
27961                 {
27962                     tag: "path",
27963                     id: this.svgID + "-svg-p",
27964                     'stroke': "navy",
27965                     'stroke-width': "3",
27966                     'fill': "none",
27967                     'pointer-events': 'none'
27968                 }
27969               ]
27970         });
27971         this.createSVG();
27972         this.svgBox = this.svgEl.dom.getScreenCTM();
27973     },
27974     createSVG : function(){ 
27975         var svg = this.signPanel;
27976         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27977         var t = this;
27978
27979         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27980         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27981         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27982         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27983         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27984         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27985         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27986         
27987     },
27988     isTouchEvent : function(e){
27989         return e.type.match(/^touch/);
27990     },
27991     getCoords : function (e) {
27992         var pt    = this.svgEl.dom.createSVGPoint();
27993         pt.x = e.clientX; 
27994         pt.y = e.clientY;
27995         if (this.isTouchEvent(e)) {
27996             pt.x =  e.targetTouches[0].clientX;
27997             pt.y = e.targetTouches[0].clientY;
27998         }
27999         var a = this.svgEl.dom.getScreenCTM();
28000         var b = a.inverse();
28001         var mx = pt.matrixTransform(b);
28002         return mx.x + ',' + mx.y;
28003     },
28004     //mouse event headler 
28005     down : function (e) {
28006         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
28007         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
28008         
28009         this.isMouseDown = true;
28010         
28011         e.preventDefault();
28012     },
28013     move : function (e) {
28014         if (this.isMouseDown) {
28015             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
28016             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
28017         }
28018         
28019         e.preventDefault();
28020     },
28021     up : function (e) {
28022         this.isMouseDown = false;
28023         var sp = this.signatureTmp.split(' ');
28024         
28025         if(sp.length > 1){
28026             if(!sp[sp.length-2].match(/^L/)){
28027                 sp.pop();
28028                 sp.pop();
28029                 sp.push("");
28030                 this.signatureTmp = sp.join(" ");
28031             }
28032         }
28033         if(this.getValue() != this.signatureTmp){
28034             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28035             this.isConfirmed = false;
28036         }
28037         e.preventDefault();
28038     },
28039     
28040     /**
28041      * Protected method that will not generally be called directly. It
28042      * is called when the editor creates its toolbar. Override this method if you need to
28043      * add custom toolbar buttons.
28044      * @param {HtmlEditor} editor
28045      */
28046     createToolbar : function(editor){
28047          function btn(id, toggle, handler){
28048             var xid = fid + '-'+ id ;
28049             return {
28050                 id : xid,
28051                 cmd : id,
28052                 cls : 'x-btn-icon x-edit-'+id,
28053                 enableToggle:toggle !== false,
28054                 scope: editor, // was editor...
28055                 handler:handler||editor.relayBtnCmd,
28056                 clickEvent:'mousedown',
28057                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28058                 tabIndex:-1
28059             };
28060         }
28061         
28062         
28063         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28064         this.tb = tb;
28065         this.tb.add(
28066            {
28067                 cls : ' x-signature-btn x-signature-'+id,
28068                 scope: editor, // was editor...
28069                 handler: this.reset,
28070                 clickEvent:'mousedown',
28071                 text: this.labels.clear
28072             },
28073             {
28074                  xtype : 'Fill',
28075                  xns: Roo.Toolbar
28076             }, 
28077             {
28078                 cls : '  x-signature-btn x-signature-'+id,
28079                 scope: editor, // was editor...
28080                 handler: this.confirmHandler,
28081                 clickEvent:'mousedown',
28082                 text: this.labels.confirm
28083             }
28084         );
28085     
28086     },
28087     //public
28088     /**
28089      * when user is clicked confirm then show this image.....
28090      * 
28091      * @return {String} Image Data URI
28092      */
28093     getImageDataURI : function(){
28094         var svg = this.svgEl.dom.parentNode.innerHTML;
28095         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28096         return src; 
28097     },
28098     /**
28099      * 
28100      * @return {Boolean} this.isConfirmed
28101      */
28102     getConfirmed : function(){
28103         return this.isConfirmed;
28104     },
28105     /**
28106      * 
28107      * @return {Number} this.width
28108      */
28109     getWidth : function(){
28110         return this.width;
28111     },
28112     /**
28113      * 
28114      * @return {Number} this.height
28115      */
28116     getHeight : function(){
28117         return this.height;
28118     },
28119     // private
28120     getSignature : function(){
28121         return this.signatureTmp;
28122     },
28123     // private
28124     reset : function(){
28125         this.signatureTmp = '';
28126         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28127         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28128         this.isConfirmed = false;
28129         Roo.form.Signature.superclass.reset.call(this);
28130     },
28131     setSignature : function(s){
28132         this.signatureTmp = s;
28133         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28134         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28135         this.setValue(s);
28136         this.isConfirmed = false;
28137         Roo.form.Signature.superclass.reset.call(this);
28138     }, 
28139     test : function(){
28140 //        Roo.log(this.signPanel.dom.contentWindow.up())
28141     },
28142     //private
28143     setConfirmed : function(){
28144         
28145         
28146         
28147 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28148     },
28149     // private
28150     confirmHandler : function(){
28151         if(!this.getSignature()){
28152             return;
28153         }
28154         
28155         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28156         this.setValue(this.getSignature());
28157         this.isConfirmed = true;
28158         
28159         this.fireEvent('confirm', this);
28160     },
28161     // private
28162     // Subclasses should provide the validation implementation by overriding this
28163     validateValue : function(value){
28164         if(this.allowBlank){
28165             return true;
28166         }
28167         
28168         if(this.isConfirmed){
28169             return true;
28170         }
28171         return false;
28172     }
28173 });/*
28174  * Based on:
28175  * Ext JS Library 1.1.1
28176  * Copyright(c) 2006-2007, Ext JS, LLC.
28177  *
28178  * Originally Released Under LGPL - original licence link has changed is not relivant.
28179  *
28180  * Fork - LGPL
28181  * <script type="text/javascript">
28182  */
28183  
28184
28185 /**
28186  * @class Roo.form.ComboBox
28187  * @extends Roo.form.TriggerField
28188  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28189  * @constructor
28190  * Create a new ComboBox.
28191  * @param {Object} config Configuration options
28192  */
28193 Roo.form.Select = function(config){
28194     Roo.form.Select.superclass.constructor.call(this, config);
28195      
28196 };
28197
28198 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28199     /**
28200      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28201      */
28202     /**
28203      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28204      * rendering into an Roo.Editor, defaults to false)
28205      */
28206     /**
28207      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28208      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28209      */
28210     /**
28211      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28212      */
28213     /**
28214      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28215      * the dropdown list (defaults to undefined, with no header element)
28216      */
28217
28218      /**
28219      * @cfg {String/Roo.Template} tpl The template to use to render the output
28220      */
28221      
28222     // private
28223     defaultAutoCreate : {tag: "select"  },
28224     /**
28225      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28226      */
28227     listWidth: undefined,
28228     /**
28229      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28230      * mode = 'remote' or 'text' if mode = 'local')
28231      */
28232     displayField: undefined,
28233     /**
28234      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28235      * mode = 'remote' or 'value' if mode = 'local'). 
28236      * Note: use of a valueField requires the user make a selection
28237      * in order for a value to be mapped.
28238      */
28239     valueField: undefined,
28240     
28241     
28242     /**
28243      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28244      * field's data value (defaults to the underlying DOM element's name)
28245      */
28246     hiddenName: undefined,
28247     /**
28248      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28249      */
28250     listClass: '',
28251     /**
28252      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28253      */
28254     selectedClass: 'x-combo-selected',
28255     /**
28256      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28257      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28258      * which displays a downward arrow icon).
28259      */
28260     triggerClass : 'x-form-arrow-trigger',
28261     /**
28262      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28263      */
28264     shadow:'sides',
28265     /**
28266      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28267      * anchor positions (defaults to 'tl-bl')
28268      */
28269     listAlign: 'tl-bl?',
28270     /**
28271      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28272      */
28273     maxHeight: 300,
28274     /**
28275      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28276      * query specified by the allQuery config option (defaults to 'query')
28277      */
28278     triggerAction: 'query',
28279     /**
28280      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28281      * (defaults to 4, does not apply if editable = false)
28282      */
28283     minChars : 4,
28284     /**
28285      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28286      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28287      */
28288     typeAhead: false,
28289     /**
28290      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28291      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28292      */
28293     queryDelay: 500,
28294     /**
28295      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28296      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28297      */
28298     pageSize: 0,
28299     /**
28300      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28301      * when editable = true (defaults to false)
28302      */
28303     selectOnFocus:false,
28304     /**
28305      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28306      */
28307     queryParam: 'query',
28308     /**
28309      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28310      * when mode = 'remote' (defaults to 'Loading...')
28311      */
28312     loadingText: 'Loading...',
28313     /**
28314      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28315      */
28316     resizable: false,
28317     /**
28318      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28319      */
28320     handleHeight : 8,
28321     /**
28322      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28323      * traditional select (defaults to true)
28324      */
28325     editable: true,
28326     /**
28327      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28328      */
28329     allQuery: '',
28330     /**
28331      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28332      */
28333     mode: 'remote',
28334     /**
28335      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28336      * listWidth has a higher value)
28337      */
28338     minListWidth : 70,
28339     /**
28340      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28341      * allow the user to set arbitrary text into the field (defaults to false)
28342      */
28343     forceSelection:false,
28344     /**
28345      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28346      * if typeAhead = true (defaults to 250)
28347      */
28348     typeAheadDelay : 250,
28349     /**
28350      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28351      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28352      */
28353     valueNotFoundText : undefined,
28354     
28355     /**
28356      * @cfg {String} defaultValue The value displayed after loading the store.
28357      */
28358     defaultValue: '',
28359     
28360     /**
28361      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28362      */
28363     blockFocus : false,
28364     
28365     /**
28366      * @cfg {Boolean} disableClear Disable showing of clear button.
28367      */
28368     disableClear : false,
28369     /**
28370      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28371      */
28372     alwaysQuery : false,
28373     
28374     //private
28375     addicon : false,
28376     editicon: false,
28377     
28378     // element that contains real text value.. (when hidden is used..)
28379      
28380     // private
28381     onRender : function(ct, position){
28382         Roo.form.Field.prototype.onRender.call(this, ct, position);
28383         
28384         if(this.store){
28385             this.store.on('beforeload', this.onBeforeLoad, this);
28386             this.store.on('load', this.onLoad, this);
28387             this.store.on('loadexception', this.onLoadException, this);
28388             this.store.load({});
28389         }
28390         
28391         
28392         
28393     },
28394
28395     // private
28396     initEvents : function(){
28397         //Roo.form.ComboBox.superclass.initEvents.call(this);
28398  
28399     },
28400
28401     onDestroy : function(){
28402        
28403         if(this.store){
28404             this.store.un('beforeload', this.onBeforeLoad, this);
28405             this.store.un('load', this.onLoad, this);
28406             this.store.un('loadexception', this.onLoadException, this);
28407         }
28408         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28409     },
28410
28411     // private
28412     fireKey : function(e){
28413         if(e.isNavKeyPress() && !this.list.isVisible()){
28414             this.fireEvent("specialkey", this, e);
28415         }
28416     },
28417
28418     // private
28419     onResize: function(w, h){
28420         
28421         return; 
28422     
28423         
28424     },
28425
28426     /**
28427      * Allow or prevent the user from directly editing the field text.  If false is passed,
28428      * the user will only be able to select from the items defined in the dropdown list.  This method
28429      * is the runtime equivalent of setting the 'editable' config option at config time.
28430      * @param {Boolean} value True to allow the user to directly edit the field text
28431      */
28432     setEditable : function(value){
28433          
28434     },
28435
28436     // private
28437     onBeforeLoad : function(){
28438         
28439         Roo.log("Select before load");
28440         return;
28441     
28442         this.innerList.update(this.loadingText ?
28443                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28444         //this.restrictHeight();
28445         this.selectedIndex = -1;
28446     },
28447
28448     // private
28449     onLoad : function(){
28450
28451     
28452         var dom = this.el.dom;
28453         dom.innerHTML = '';
28454          var od = dom.ownerDocument;
28455          
28456         if (this.emptyText) {
28457             var op = od.createElement('option');
28458             op.setAttribute('value', '');
28459             op.innerHTML = String.format('{0}', this.emptyText);
28460             dom.appendChild(op);
28461         }
28462         if(this.store.getCount() > 0){
28463            
28464             var vf = this.valueField;
28465             var df = this.displayField;
28466             this.store.data.each(function(r) {
28467                 // which colmsn to use... testing - cdoe / title..
28468                 var op = od.createElement('option');
28469                 op.setAttribute('value', r.data[vf]);
28470                 op.innerHTML = String.format('{0}', r.data[df]);
28471                 dom.appendChild(op);
28472             });
28473             if (typeof(this.defaultValue != 'undefined')) {
28474                 this.setValue(this.defaultValue);
28475             }
28476             
28477              
28478         }else{
28479             //this.onEmptyResults();
28480         }
28481         //this.el.focus();
28482     },
28483     // private
28484     onLoadException : function()
28485     {
28486         dom.innerHTML = '';
28487             
28488         Roo.log("Select on load exception");
28489         return;
28490     
28491         this.collapse();
28492         Roo.log(this.store.reader.jsonData);
28493         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28494             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28495         }
28496         
28497         
28498     },
28499     // private
28500     onTypeAhead : function(){
28501          
28502     },
28503
28504     // private
28505     onSelect : function(record, index){
28506         Roo.log('on select?');
28507         return;
28508         if(this.fireEvent('beforeselect', this, record, index) !== false){
28509             this.setFromData(index > -1 ? record.data : false);
28510             this.collapse();
28511             this.fireEvent('select', this, record, index);
28512         }
28513     },
28514
28515     /**
28516      * Returns the currently selected field value or empty string if no value is set.
28517      * @return {String} value The selected value
28518      */
28519     getValue : function(){
28520         var dom = this.el.dom;
28521         this.value = dom.options[dom.selectedIndex].value;
28522         return this.value;
28523         
28524     },
28525
28526     /**
28527      * Clears any text/value currently set in the field
28528      */
28529     clearValue : function(){
28530         this.value = '';
28531         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28532         
28533     },
28534
28535     /**
28536      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28537      * will be displayed in the field.  If the value does not match the data value of an existing item,
28538      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28539      * Otherwise the field will be blank (although the value will still be set).
28540      * @param {String} value The value to match
28541      */
28542     setValue : function(v){
28543         var d = this.el.dom;
28544         for (var i =0; i < d.options.length;i++) {
28545             if (v == d.options[i].value) {
28546                 d.selectedIndex = i;
28547                 this.value = v;
28548                 return;
28549             }
28550         }
28551         this.clearValue();
28552     },
28553     /**
28554      * @property {Object} the last set data for the element
28555      */
28556     
28557     lastData : false,
28558     /**
28559      * Sets the value of the field based on a object which is related to the record format for the store.
28560      * @param {Object} value the value to set as. or false on reset?
28561      */
28562     setFromData : function(o){
28563         Roo.log('setfrom data?');
28564          
28565         
28566         
28567     },
28568     // private
28569     reset : function(){
28570         this.clearValue();
28571     },
28572     // private
28573     findRecord : function(prop, value){
28574         
28575         return false;
28576     
28577         var record;
28578         if(this.store.getCount() > 0){
28579             this.store.each(function(r){
28580                 if(r.data[prop] == value){
28581                     record = r;
28582                     return false;
28583                 }
28584                 return true;
28585             });
28586         }
28587         return record;
28588     },
28589     
28590     getName: function()
28591     {
28592         // returns hidden if it's set..
28593         if (!this.rendered) {return ''};
28594         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28595         
28596     },
28597      
28598
28599     
28600
28601     // private
28602     onEmptyResults : function(){
28603         Roo.log('empty results');
28604         //this.collapse();
28605     },
28606
28607     /**
28608      * Returns true if the dropdown list is expanded, else false.
28609      */
28610     isExpanded : function(){
28611         return false;
28612     },
28613
28614     /**
28615      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28616      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28617      * @param {String} value The data value of the item to select
28618      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28619      * selected item if it is not currently in view (defaults to true)
28620      * @return {Boolean} True if the value matched an item in the list, else false
28621      */
28622     selectByValue : function(v, scrollIntoView){
28623         Roo.log('select By Value');
28624         return false;
28625     
28626         if(v !== undefined && v !== null){
28627             var r = this.findRecord(this.valueField || this.displayField, v);
28628             if(r){
28629                 this.select(this.store.indexOf(r), scrollIntoView);
28630                 return true;
28631             }
28632         }
28633         return false;
28634     },
28635
28636     /**
28637      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28638      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28639      * @param {Number} index The zero-based index of the list item to select
28640      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28641      * selected item if it is not currently in view (defaults to true)
28642      */
28643     select : function(index, scrollIntoView){
28644         Roo.log('select ');
28645         return  ;
28646         
28647         this.selectedIndex = index;
28648         this.view.select(index);
28649         if(scrollIntoView !== false){
28650             var el = this.view.getNode(index);
28651             if(el){
28652                 this.innerList.scrollChildIntoView(el, false);
28653             }
28654         }
28655     },
28656
28657       
28658
28659     // private
28660     validateBlur : function(){
28661         
28662         return;
28663         
28664     },
28665
28666     // private
28667     initQuery : function(){
28668         this.doQuery(this.getRawValue());
28669     },
28670
28671     // private
28672     doForce : function(){
28673         if(this.el.dom.value.length > 0){
28674             this.el.dom.value =
28675                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28676              
28677         }
28678     },
28679
28680     /**
28681      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28682      * query allowing the query action to be canceled if needed.
28683      * @param {String} query The SQL query to execute
28684      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28685      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28686      * saved in the current store (defaults to false)
28687      */
28688     doQuery : function(q, forceAll){
28689         
28690         Roo.log('doQuery?');
28691         if(q === undefined || q === null){
28692             q = '';
28693         }
28694         var qe = {
28695             query: q,
28696             forceAll: forceAll,
28697             combo: this,
28698             cancel:false
28699         };
28700         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28701             return false;
28702         }
28703         q = qe.query;
28704         forceAll = qe.forceAll;
28705         if(forceAll === true || (q.length >= this.minChars)){
28706             if(this.lastQuery != q || this.alwaysQuery){
28707                 this.lastQuery = q;
28708                 if(this.mode == 'local'){
28709                     this.selectedIndex = -1;
28710                     if(forceAll){
28711                         this.store.clearFilter();
28712                     }else{
28713                         this.store.filter(this.displayField, q);
28714                     }
28715                     this.onLoad();
28716                 }else{
28717                     this.store.baseParams[this.queryParam] = q;
28718                     this.store.load({
28719                         params: this.getParams(q)
28720                     });
28721                     this.expand();
28722                 }
28723             }else{
28724                 this.selectedIndex = -1;
28725                 this.onLoad();   
28726             }
28727         }
28728     },
28729
28730     // private
28731     getParams : function(q){
28732         var p = {};
28733         //p[this.queryParam] = q;
28734         if(this.pageSize){
28735             p.start = 0;
28736             p.limit = this.pageSize;
28737         }
28738         return p;
28739     },
28740
28741     /**
28742      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28743      */
28744     collapse : function(){
28745         
28746     },
28747
28748     // private
28749     collapseIf : function(e){
28750         
28751     },
28752
28753     /**
28754      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28755      */
28756     expand : function(){
28757         
28758     } ,
28759
28760     // private
28761      
28762
28763     /** 
28764     * @cfg {Boolean} grow 
28765     * @hide 
28766     */
28767     /** 
28768     * @cfg {Number} growMin 
28769     * @hide 
28770     */
28771     /** 
28772     * @cfg {Number} growMax 
28773     * @hide 
28774     */
28775     /**
28776      * @hide
28777      * @method autoSize
28778      */
28779     
28780     setWidth : function()
28781     {
28782         
28783     },
28784     getResizeEl : function(){
28785         return this.el;
28786     }
28787 });//<script type="text/javasscript">
28788  
28789
28790 /**
28791  * @class Roo.DDView
28792  * A DnD enabled version of Roo.View.
28793  * @param {Element/String} container The Element in which to create the View.
28794  * @param {String} tpl The template string used to create the markup for each element of the View
28795  * @param {Object} config The configuration properties. These include all the config options of
28796  * {@link Roo.View} plus some specific to this class.<br>
28797  * <p>
28798  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28799  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28800  * <p>
28801  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28802 .x-view-drag-insert-above {
28803         border-top:1px dotted #3366cc;
28804 }
28805 .x-view-drag-insert-below {
28806         border-bottom:1px dotted #3366cc;
28807 }
28808 </code></pre>
28809  * 
28810  */
28811  
28812 Roo.DDView = function(container, tpl, config) {
28813     Roo.DDView.superclass.constructor.apply(this, arguments);
28814     this.getEl().setStyle("outline", "0px none");
28815     this.getEl().unselectable();
28816     if (this.dragGroup) {
28817                 this.setDraggable(this.dragGroup.split(","));
28818     }
28819     if (this.dropGroup) {
28820                 this.setDroppable(this.dropGroup.split(","));
28821     }
28822     if (this.deletable) {
28823         this.setDeletable();
28824     }
28825     this.isDirtyFlag = false;
28826         this.addEvents({
28827                 "drop" : true
28828         });
28829 };
28830
28831 Roo.extend(Roo.DDView, Roo.View, {
28832 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28833 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28834 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28835 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28836
28837         isFormField: true,
28838
28839         reset: Roo.emptyFn,
28840         
28841         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28842
28843         validate: function() {
28844                 return true;
28845         },
28846         
28847         destroy: function() {
28848                 this.purgeListeners();
28849                 this.getEl.removeAllListeners();
28850                 this.getEl().remove();
28851                 if (this.dragZone) {
28852                         if (this.dragZone.destroy) {
28853                                 this.dragZone.destroy();
28854                         }
28855                 }
28856                 if (this.dropZone) {
28857                         if (this.dropZone.destroy) {
28858                                 this.dropZone.destroy();
28859                         }
28860                 }
28861         },
28862
28863 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28864         getName: function() {
28865                 return this.name;
28866         },
28867
28868 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28869         setValue: function(v) {
28870                 if (!this.store) {
28871                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28872                 }
28873                 var data = {};
28874                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28875                 this.store.proxy = new Roo.data.MemoryProxy(data);
28876                 this.store.load();
28877         },
28878
28879 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28880         getValue: function() {
28881                 var result = '(';
28882                 this.store.each(function(rec) {
28883                         result += rec.id + ',';
28884                 });
28885                 return result.substr(0, result.length - 1) + ')';
28886         },
28887         
28888         getIds: function() {
28889                 var i = 0, result = new Array(this.store.getCount());
28890                 this.store.each(function(rec) {
28891                         result[i++] = rec.id;
28892                 });
28893                 return result;
28894         },
28895         
28896         isDirty: function() {
28897                 return this.isDirtyFlag;
28898         },
28899
28900 /**
28901  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28902  *      whole Element becomes the target, and this causes the drop gesture to append.
28903  */
28904     getTargetFromEvent : function(e) {
28905                 var target = e.getTarget();
28906                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28907                 target = target.parentNode;
28908                 }
28909                 if (!target) {
28910                         target = this.el.dom.lastChild || this.el.dom;
28911                 }
28912                 return target;
28913     },
28914
28915 /**
28916  *      Create the drag data which consists of an object which has the property "ddel" as
28917  *      the drag proxy element. 
28918  */
28919     getDragData : function(e) {
28920         var target = this.findItemFromChild(e.getTarget());
28921                 if(target) {
28922                         this.handleSelection(e);
28923                         var selNodes = this.getSelectedNodes();
28924             var dragData = {
28925                 source: this,
28926                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28927                 nodes: selNodes,
28928                 records: []
28929                         };
28930                         var selectedIndices = this.getSelectedIndexes();
28931                         for (var i = 0; i < selectedIndices.length; i++) {
28932                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28933                         }
28934                         if (selNodes.length == 1) {
28935                                 dragData.ddel = target.cloneNode(true); // the div element
28936                         } else {
28937                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28938                                 div.className = 'multi-proxy';
28939                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28940                                         div.appendChild(selNodes[i].cloneNode(true));
28941                                 }
28942                                 dragData.ddel = div;
28943                         }
28944             //console.log(dragData)
28945             //console.log(dragData.ddel.innerHTML)
28946                         return dragData;
28947                 }
28948         //console.log('nodragData')
28949                 return false;
28950     },
28951     
28952 /**     Specify to which ddGroup items in this DDView may be dragged. */
28953     setDraggable: function(ddGroup) {
28954         if (ddGroup instanceof Array) {
28955                 Roo.each(ddGroup, this.setDraggable, this);
28956                 return;
28957         }
28958         if (this.dragZone) {
28959                 this.dragZone.addToGroup(ddGroup);
28960         } else {
28961                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28962                                 containerScroll: true,
28963                                 ddGroup: ddGroup 
28964
28965                         });
28966 //                      Draggability implies selection. DragZone's mousedown selects the element.
28967                         if (!this.multiSelect) { this.singleSelect = true; }
28968
28969 //                      Wire the DragZone's handlers up to methods in *this*
28970                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28971                 }
28972     },
28973
28974 /**     Specify from which ddGroup this DDView accepts drops. */
28975     setDroppable: function(ddGroup) {
28976         if (ddGroup instanceof Array) {
28977                 Roo.each(ddGroup, this.setDroppable, this);
28978                 return;
28979         }
28980         if (this.dropZone) {
28981                 this.dropZone.addToGroup(ddGroup);
28982         } else {
28983                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28984                                 containerScroll: true,
28985                                 ddGroup: ddGroup
28986                         });
28987
28988 //                      Wire the DropZone's handlers up to methods in *this*
28989                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28990                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28991                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28992                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28993                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28994                 }
28995     },
28996
28997 /**     Decide whether to drop above or below a View node. */
28998     getDropPoint : function(e, n, dd){
28999         if (n == this.el.dom) { return "above"; }
29000                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29001                 var c = t + (b - t) / 2;
29002                 var y = Roo.lib.Event.getPageY(e);
29003                 if(y <= c) {
29004                         return "above";
29005                 }else{
29006                         return "below";
29007                 }
29008     },
29009
29010     onNodeEnter : function(n, dd, e, data){
29011                 return false;
29012     },
29013     
29014     onNodeOver : function(n, dd, e, data){
29015                 var pt = this.getDropPoint(e, n, dd);
29016                 // set the insert point style on the target node
29017                 var dragElClass = this.dropNotAllowed;
29018                 if (pt) {
29019                         var targetElClass;
29020                         if (pt == "above"){
29021                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29022                                 targetElClass = "x-view-drag-insert-above";
29023                         } else {
29024                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29025                                 targetElClass = "x-view-drag-insert-below";
29026                         }
29027                         if (this.lastInsertClass != targetElClass){
29028                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29029                                 this.lastInsertClass = targetElClass;
29030                         }
29031                 }
29032                 return dragElClass;
29033         },
29034
29035     onNodeOut : function(n, dd, e, data){
29036                 this.removeDropIndicators(n);
29037     },
29038
29039     onNodeDrop : function(n, dd, e, data){
29040         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29041                 return false;
29042         }
29043         var pt = this.getDropPoint(e, n, dd);
29044                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29045                 if (pt == "below") { insertAt++; }
29046                 for (var i = 0; i < data.records.length; i++) {
29047                         var r = data.records[i];
29048                         var dup = this.store.getById(r.id);
29049                         if (dup && (dd != this.dragZone)) {
29050                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29051                         } else {
29052                                 if (data.copy) {
29053                                         this.store.insert(insertAt++, r.copy());
29054                                 } else {
29055                                         data.source.isDirtyFlag = true;
29056                                         r.store.remove(r);
29057                                         this.store.insert(insertAt++, r);
29058                                 }
29059                                 this.isDirtyFlag = true;
29060                         }
29061                 }
29062                 this.dragZone.cachedTarget = null;
29063                 return true;
29064     },
29065
29066     removeDropIndicators : function(n){
29067                 if(n){
29068                         Roo.fly(n).removeClass([
29069                                 "x-view-drag-insert-above",
29070                                 "x-view-drag-insert-below"]);
29071                         this.lastInsertClass = "_noclass";
29072                 }
29073     },
29074
29075 /**
29076  *      Utility method. Add a delete option to the DDView's context menu.
29077  *      @param {String} imageUrl The URL of the "delete" icon image.
29078  */
29079         setDeletable: function(imageUrl) {
29080                 if (!this.singleSelect && !this.multiSelect) {
29081                         this.singleSelect = true;
29082                 }
29083                 var c = this.getContextMenu();
29084                 this.contextMenu.on("itemclick", function(item) {
29085                         switch (item.id) {
29086                                 case "delete":
29087                                         this.remove(this.getSelectedIndexes());
29088                                         break;
29089                         }
29090                 }, this);
29091                 this.contextMenu.add({
29092                         icon: imageUrl,
29093                         id: "delete",
29094                         text: 'Delete'
29095                 });
29096         },
29097         
29098 /**     Return the context menu for this DDView. */
29099         getContextMenu: function() {
29100                 if (!this.contextMenu) {
29101 //                      Create the View's context menu
29102                         this.contextMenu = new Roo.menu.Menu({
29103                                 id: this.id + "-contextmenu"
29104                         });
29105                         this.el.on("contextmenu", this.showContextMenu, this);
29106                 }
29107                 return this.contextMenu;
29108         },
29109         
29110         disableContextMenu: function() {
29111                 if (this.contextMenu) {
29112                         this.el.un("contextmenu", this.showContextMenu, this);
29113                 }
29114         },
29115
29116         showContextMenu: function(e, item) {
29117         item = this.findItemFromChild(e.getTarget());
29118                 if (item) {
29119                         e.stopEvent();
29120                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29121                         this.contextMenu.showAt(e.getXY());
29122             }
29123     },
29124
29125 /**
29126  *      Remove {@link Roo.data.Record}s at the specified indices.
29127  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29128  */
29129     remove: function(selectedIndices) {
29130                 selectedIndices = [].concat(selectedIndices);
29131                 for (var i = 0; i < selectedIndices.length; i++) {
29132                         var rec = this.store.getAt(selectedIndices[i]);
29133                         this.store.remove(rec);
29134                 }
29135     },
29136
29137 /**
29138  *      Double click fires the event, but also, if this is draggable, and there is only one other
29139  *      related DropZone, it transfers the selected node.
29140  */
29141     onDblClick : function(e){
29142         var item = this.findItemFromChild(e.getTarget());
29143         if(item){
29144             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29145                 return false;
29146             }
29147             if (this.dragGroup) {
29148                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29149                     while (targets.indexOf(this.dropZone) > -1) {
29150                             targets.remove(this.dropZone);
29151                                 }
29152                     if (targets.length == 1) {
29153                                         this.dragZone.cachedTarget = null;
29154                         var el = Roo.get(targets[0].getEl());
29155                         var box = el.getBox(true);
29156                         targets[0].onNodeDrop(el.dom, {
29157                                 target: el.dom,
29158                                 xy: [box.x, box.y + box.height - 1]
29159                         }, null, this.getDragData(e));
29160                     }
29161                 }
29162         }
29163     },
29164     
29165     handleSelection: function(e) {
29166                 this.dragZone.cachedTarget = null;
29167         var item = this.findItemFromChild(e.getTarget());
29168         if (!item) {
29169                 this.clearSelections(true);
29170                 return;
29171         }
29172                 if (item && (this.multiSelect || this.singleSelect)){
29173                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29174                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29175                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29176                                 this.unselect(item);
29177                         } else {
29178                                 this.select(item, this.multiSelect && e.ctrlKey);
29179                                 this.lastSelection = item;
29180                         }
29181                 }
29182     },
29183
29184     onItemClick : function(item, index, e){
29185                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29186                         return false;
29187                 }
29188                 return true;
29189     },
29190
29191     unselect : function(nodeInfo, suppressEvent){
29192                 var node = this.getNode(nodeInfo);
29193                 if(node && this.isSelected(node)){
29194                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29195                                 Roo.fly(node).removeClass(this.selectedClass);
29196                                 this.selections.remove(node);
29197                                 if(!suppressEvent){
29198                                         this.fireEvent("selectionchange", this, this.selections);
29199                                 }
29200                         }
29201                 }
29202     }
29203 });
29204 /*
29205  * Based on:
29206  * Ext JS Library 1.1.1
29207  * Copyright(c) 2006-2007, Ext JS, LLC.
29208  *
29209  * Originally Released Under LGPL - original licence link has changed is not relivant.
29210  *
29211  * Fork - LGPL
29212  * <script type="text/javascript">
29213  */
29214  
29215 /**
29216  * @class Roo.LayoutManager
29217  * @extends Roo.util.Observable
29218  * Base class for layout managers.
29219  */
29220 Roo.LayoutManager = function(container, config){
29221     Roo.LayoutManager.superclass.constructor.call(this);
29222     this.el = Roo.get(container);
29223     // ie scrollbar fix
29224     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29225         document.body.scroll = "no";
29226     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29227         this.el.position('relative');
29228     }
29229     this.id = this.el.id;
29230     this.el.addClass("x-layout-container");
29231     /** false to disable window resize monitoring @type Boolean */
29232     this.monitorWindowResize = true;
29233     this.regions = {};
29234     this.addEvents({
29235         /**
29236          * @event layout
29237          * Fires when a layout is performed. 
29238          * @param {Roo.LayoutManager} this
29239          */
29240         "layout" : true,
29241         /**
29242          * @event regionresized
29243          * Fires when the user resizes a region. 
29244          * @param {Roo.LayoutRegion} region The resized region
29245          * @param {Number} newSize The new size (width for east/west, height for north/south)
29246          */
29247         "regionresized" : true,
29248         /**
29249          * @event regioncollapsed
29250          * Fires when a region is collapsed. 
29251          * @param {Roo.LayoutRegion} region The collapsed region
29252          */
29253         "regioncollapsed" : true,
29254         /**
29255          * @event regionexpanded
29256          * Fires when a region is expanded.  
29257          * @param {Roo.LayoutRegion} region The expanded region
29258          */
29259         "regionexpanded" : true
29260     });
29261     this.updating = false;
29262     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29263 };
29264
29265 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29266     /**
29267      * Returns true if this layout is currently being updated
29268      * @return {Boolean}
29269      */
29270     isUpdating : function(){
29271         return this.updating; 
29272     },
29273     
29274     /**
29275      * Suspend the LayoutManager from doing auto-layouts while
29276      * making multiple add or remove calls
29277      */
29278     beginUpdate : function(){
29279         this.updating = true;    
29280     },
29281     
29282     /**
29283      * Restore auto-layouts and optionally disable the manager from performing a layout
29284      * @param {Boolean} noLayout true to disable a layout update 
29285      */
29286     endUpdate : function(noLayout){
29287         this.updating = false;
29288         if(!noLayout){
29289             this.layout();
29290         }    
29291     },
29292     
29293     layout: function(){
29294         
29295     },
29296     
29297     onRegionResized : function(region, newSize){
29298         this.fireEvent("regionresized", region, newSize);
29299         this.layout();
29300     },
29301     
29302     onRegionCollapsed : function(region){
29303         this.fireEvent("regioncollapsed", region);
29304     },
29305     
29306     onRegionExpanded : function(region){
29307         this.fireEvent("regionexpanded", region);
29308     },
29309         
29310     /**
29311      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29312      * performs box-model adjustments.
29313      * @return {Object} The size as an object {width: (the width), height: (the height)}
29314      */
29315     getViewSize : function(){
29316         var size;
29317         if(this.el.dom != document.body){
29318             size = this.el.getSize();
29319         }else{
29320             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29321         }
29322         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29323         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29324         return size;
29325     },
29326     
29327     /**
29328      * Returns the Element this layout is bound to.
29329      * @return {Roo.Element}
29330      */
29331     getEl : function(){
29332         return this.el;
29333     },
29334     
29335     /**
29336      * Returns the specified region.
29337      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29338      * @return {Roo.LayoutRegion}
29339      */
29340     getRegion : function(target){
29341         return this.regions[target.toLowerCase()];
29342     },
29343     
29344     onWindowResize : function(){
29345         if(this.monitorWindowResize){
29346             this.layout();
29347         }
29348     }
29349 });/*
29350  * Based on:
29351  * Ext JS Library 1.1.1
29352  * Copyright(c) 2006-2007, Ext JS, LLC.
29353  *
29354  * Originally Released Under LGPL - original licence link has changed is not relivant.
29355  *
29356  * Fork - LGPL
29357  * <script type="text/javascript">
29358  */
29359 /**
29360  * @class Roo.BorderLayout
29361  * @extends Roo.LayoutManager
29362  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29363  * please see: <br><br>
29364  * <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>
29365  * <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>
29366  * Example:
29367  <pre><code>
29368  var layout = new Roo.BorderLayout(document.body, {
29369     north: {
29370         initialSize: 25,
29371         titlebar: false
29372     },
29373     west: {
29374         split:true,
29375         initialSize: 200,
29376         minSize: 175,
29377         maxSize: 400,
29378         titlebar: true,
29379         collapsible: true
29380     },
29381     east: {
29382         split:true,
29383         initialSize: 202,
29384         minSize: 175,
29385         maxSize: 400,
29386         titlebar: true,
29387         collapsible: true
29388     },
29389     south: {
29390         split:true,
29391         initialSize: 100,
29392         minSize: 100,
29393         maxSize: 200,
29394         titlebar: true,
29395         collapsible: true
29396     },
29397     center: {
29398         titlebar: true,
29399         autoScroll:true,
29400         resizeTabs: true,
29401         minTabWidth: 50,
29402         preferredTabWidth: 150
29403     }
29404 });
29405
29406 // shorthand
29407 var CP = Roo.ContentPanel;
29408
29409 layout.beginUpdate();
29410 layout.add("north", new CP("north", "North"));
29411 layout.add("south", new CP("south", {title: "South", closable: true}));
29412 layout.add("west", new CP("west", {title: "West"}));
29413 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29414 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29415 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29416 layout.getRegion("center").showPanel("center1");
29417 layout.endUpdate();
29418 </code></pre>
29419
29420 <b>The container the layout is rendered into can be either the body element or any other element.
29421 If it is not the body element, the container needs to either be an absolute positioned element,
29422 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29423 the container size if it is not the body element.</b>
29424
29425 * @constructor
29426 * Create a new BorderLayout
29427 * @param {String/HTMLElement/Element} container The container this layout is bound to
29428 * @param {Object} config Configuration options
29429  */
29430 Roo.BorderLayout = function(container, config){
29431     config = config || {};
29432     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29433     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29434     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29435         var target = this.factory.validRegions[i];
29436         if(config[target]){
29437             this.addRegion(target, config[target]);
29438         }
29439     }
29440 };
29441
29442 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29443     /**
29444      * Creates and adds a new region if it doesn't already exist.
29445      * @param {String} target The target region key (north, south, east, west or center).
29446      * @param {Object} config The regions config object
29447      * @return {BorderLayoutRegion} The new region
29448      */
29449     addRegion : function(target, config){
29450         if(!this.regions[target]){
29451             var r = this.factory.create(target, this, config);
29452             this.bindRegion(target, r);
29453         }
29454         return this.regions[target];
29455     },
29456
29457     // private (kinda)
29458     bindRegion : function(name, r){
29459         this.regions[name] = r;
29460         r.on("visibilitychange", this.layout, this);
29461         r.on("paneladded", this.layout, this);
29462         r.on("panelremoved", this.layout, this);
29463         r.on("invalidated", this.layout, this);
29464         r.on("resized", this.onRegionResized, this);
29465         r.on("collapsed", this.onRegionCollapsed, this);
29466         r.on("expanded", this.onRegionExpanded, this);
29467     },
29468
29469     /**
29470      * Performs a layout update.
29471      */
29472     layout : function(){
29473         if(this.updating) {
29474             return;
29475         }
29476         var size = this.getViewSize();
29477         var w = size.width;
29478         var h = size.height;
29479         var centerW = w;
29480         var centerH = h;
29481         var centerY = 0;
29482         var centerX = 0;
29483         //var x = 0, y = 0;
29484
29485         var rs = this.regions;
29486         var north = rs["north"];
29487         var south = rs["south"]; 
29488         var west = rs["west"];
29489         var east = rs["east"];
29490         var center = rs["center"];
29491         //if(this.hideOnLayout){ // not supported anymore
29492             //c.el.setStyle("display", "none");
29493         //}
29494         if(north && north.isVisible()){
29495             var b = north.getBox();
29496             var m = north.getMargins();
29497             b.width = w - (m.left+m.right);
29498             b.x = m.left;
29499             b.y = m.top;
29500             centerY = b.height + b.y + m.bottom;
29501             centerH -= centerY;
29502             north.updateBox(this.safeBox(b));
29503         }
29504         if(south && south.isVisible()){
29505             var b = south.getBox();
29506             var m = south.getMargins();
29507             b.width = w - (m.left+m.right);
29508             b.x = m.left;
29509             var totalHeight = (b.height + m.top + m.bottom);
29510             b.y = h - totalHeight + m.top;
29511             centerH -= totalHeight;
29512             south.updateBox(this.safeBox(b));
29513         }
29514         if(west && west.isVisible()){
29515             var b = west.getBox();
29516             var m = west.getMargins();
29517             b.height = centerH - (m.top+m.bottom);
29518             b.x = m.left;
29519             b.y = centerY + m.top;
29520             var totalWidth = (b.width + m.left + m.right);
29521             centerX += totalWidth;
29522             centerW -= totalWidth;
29523             west.updateBox(this.safeBox(b));
29524         }
29525         if(east && east.isVisible()){
29526             var b = east.getBox();
29527             var m = east.getMargins();
29528             b.height = centerH - (m.top+m.bottom);
29529             var totalWidth = (b.width + m.left + m.right);
29530             b.x = w - totalWidth + m.left;
29531             b.y = centerY + m.top;
29532             centerW -= totalWidth;
29533             east.updateBox(this.safeBox(b));
29534         }
29535         if(center){
29536             var m = center.getMargins();
29537             var centerBox = {
29538                 x: centerX + m.left,
29539                 y: centerY + m.top,
29540                 width: centerW - (m.left+m.right),
29541                 height: centerH - (m.top+m.bottom)
29542             };
29543             //if(this.hideOnLayout){
29544                 //center.el.setStyle("display", "block");
29545             //}
29546             center.updateBox(this.safeBox(centerBox));
29547         }
29548         this.el.repaint();
29549         this.fireEvent("layout", this);
29550     },
29551
29552     // private
29553     safeBox : function(box){
29554         box.width = Math.max(0, box.width);
29555         box.height = Math.max(0, box.height);
29556         return box;
29557     },
29558
29559     /**
29560      * Adds a ContentPanel (or subclass) to this layout.
29561      * @param {String} target The target region key (north, south, east, west or center).
29562      * @param {Roo.ContentPanel} panel The panel to add
29563      * @return {Roo.ContentPanel} The added panel
29564      */
29565     add : function(target, panel){
29566          
29567         target = target.toLowerCase();
29568         return this.regions[target].add(panel);
29569     },
29570
29571     /**
29572      * Remove a ContentPanel (or subclass) to this layout.
29573      * @param {String} target The target region key (north, south, east, west or center).
29574      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29575      * @return {Roo.ContentPanel} The removed panel
29576      */
29577     remove : function(target, panel){
29578         target = target.toLowerCase();
29579         return this.regions[target].remove(panel);
29580     },
29581
29582     /**
29583      * Searches all regions for a panel with the specified id
29584      * @param {String} panelId
29585      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29586      */
29587     findPanel : function(panelId){
29588         var rs = this.regions;
29589         for(var target in rs){
29590             if(typeof rs[target] != "function"){
29591                 var p = rs[target].getPanel(panelId);
29592                 if(p){
29593                     return p;
29594                 }
29595             }
29596         }
29597         return null;
29598     },
29599
29600     /**
29601      * Searches all regions for a panel with the specified id and activates (shows) it.
29602      * @param {String/ContentPanel} panelId The panels id or the panel itself
29603      * @return {Roo.ContentPanel} The shown panel or null
29604      */
29605     showPanel : function(panelId) {
29606       var rs = this.regions;
29607       for(var target in rs){
29608          var r = rs[target];
29609          if(typeof r != "function"){
29610             if(r.hasPanel(panelId)){
29611                return r.showPanel(panelId);
29612             }
29613          }
29614       }
29615       return null;
29616    },
29617
29618    /**
29619      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29620      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29621      */
29622     restoreState : function(provider){
29623         if(!provider){
29624             provider = Roo.state.Manager;
29625         }
29626         var sm = new Roo.LayoutStateManager();
29627         sm.init(this, provider);
29628     },
29629
29630     /**
29631      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29632      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29633      * a valid ContentPanel config object.  Example:
29634      * <pre><code>
29635 // Create the main layout
29636 var layout = new Roo.BorderLayout('main-ct', {
29637     west: {
29638         split:true,
29639         minSize: 175,
29640         titlebar: true
29641     },
29642     center: {
29643         title:'Components'
29644     }
29645 }, 'main-ct');
29646
29647 // Create and add multiple ContentPanels at once via configs
29648 layout.batchAdd({
29649    west: {
29650        id: 'source-files',
29651        autoCreate:true,
29652        title:'Ext Source Files',
29653        autoScroll:true,
29654        fitToFrame:true
29655    },
29656    center : {
29657        el: cview,
29658        autoScroll:true,
29659        fitToFrame:true,
29660        toolbar: tb,
29661        resizeEl:'cbody'
29662    }
29663 });
29664 </code></pre>
29665      * @param {Object} regions An object containing ContentPanel configs by region name
29666      */
29667     batchAdd : function(regions){
29668         this.beginUpdate();
29669         for(var rname in regions){
29670             var lr = this.regions[rname];
29671             if(lr){
29672                 this.addTypedPanels(lr, regions[rname]);
29673             }
29674         }
29675         this.endUpdate();
29676     },
29677
29678     // private
29679     addTypedPanels : function(lr, ps){
29680         if(typeof ps == 'string'){
29681             lr.add(new Roo.ContentPanel(ps));
29682         }
29683         else if(ps instanceof Array){
29684             for(var i =0, len = ps.length; i < len; i++){
29685                 this.addTypedPanels(lr, ps[i]);
29686             }
29687         }
29688         else if(!ps.events){ // raw config?
29689             var el = ps.el;
29690             delete ps.el; // prevent conflict
29691             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29692         }
29693         else {  // panel object assumed!
29694             lr.add(ps);
29695         }
29696     },
29697     /**
29698      * Adds a xtype elements to the layout.
29699      * <pre><code>
29700
29701 layout.addxtype({
29702        xtype : 'ContentPanel',
29703        region: 'west',
29704        items: [ .... ]
29705    }
29706 );
29707
29708 layout.addxtype({
29709         xtype : 'NestedLayoutPanel',
29710         region: 'west',
29711         layout: {
29712            center: { },
29713            west: { }   
29714         },
29715         items : [ ... list of content panels or nested layout panels.. ]
29716    }
29717 );
29718 </code></pre>
29719      * @param {Object} cfg Xtype definition of item to add.
29720      */
29721     addxtype : function(cfg)
29722     {
29723         // basically accepts a pannel...
29724         // can accept a layout region..!?!?
29725         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29726         
29727         if (!cfg.xtype.match(/Panel$/)) {
29728             return false;
29729         }
29730         var ret = false;
29731         
29732         if (typeof(cfg.region) == 'undefined') {
29733             Roo.log("Failed to add Panel, region was not set");
29734             Roo.log(cfg);
29735             return false;
29736         }
29737         var region = cfg.region;
29738         delete cfg.region;
29739         
29740           
29741         var xitems = [];
29742         if (cfg.items) {
29743             xitems = cfg.items;
29744             delete cfg.items;
29745         }
29746         var nb = false;
29747         
29748         switch(cfg.xtype) 
29749         {
29750             case 'ContentPanel':  // ContentPanel (el, cfg)
29751             case 'ScrollPanel':  // ContentPanel (el, cfg)
29752             case 'ViewPanel': 
29753                 if(cfg.autoCreate) {
29754                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29755                 } else {
29756                     var el = this.el.createChild();
29757                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29758                 }
29759                 
29760                 this.add(region, ret);
29761                 break;
29762             
29763             
29764             case 'TreePanel': // our new panel!
29765                 cfg.el = this.el.createChild();
29766                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29767                 this.add(region, ret);
29768                 break;
29769             
29770             case 'NestedLayoutPanel': 
29771                 // create a new Layout (which is  a Border Layout...
29772                 var el = this.el.createChild();
29773                 var clayout = cfg.layout;
29774                 delete cfg.layout;
29775                 clayout.items   = clayout.items  || [];
29776                 // replace this exitems with the clayout ones..
29777                 xitems = clayout.items;
29778                  
29779                 
29780                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29781                     cfg.background = false;
29782                 }
29783                 var layout = new Roo.BorderLayout(el, clayout);
29784                 
29785                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29786                 //console.log('adding nested layout panel '  + cfg.toSource());
29787                 this.add(region, ret);
29788                 nb = {}; /// find first...
29789                 break;
29790                 
29791             case 'GridPanel': 
29792             
29793                 // needs grid and region
29794                 
29795                 //var el = this.getRegion(region).el.createChild();
29796                 var el = this.el.createChild();
29797                 // create the grid first...
29798                 
29799                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29800                 delete cfg.grid;
29801                 if (region == 'center' && this.active ) {
29802                     cfg.background = false;
29803                 }
29804                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29805                 
29806                 this.add(region, ret);
29807                 if (cfg.background) {
29808                     ret.on('activate', function(gp) {
29809                         if (!gp.grid.rendered) {
29810                             gp.grid.render();
29811                         }
29812                     });
29813                 } else {
29814                     grid.render();
29815                 }
29816                 break;
29817            
29818            
29819            
29820                 
29821                 
29822                 
29823             default:
29824                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29825                     
29826                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29827                     this.add(region, ret);
29828                 } else {
29829                 
29830                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29831                     return null;
29832                 }
29833                 
29834              // GridPanel (grid, cfg)
29835             
29836         }
29837         this.beginUpdate();
29838         // add children..
29839         var region = '';
29840         var abn = {};
29841         Roo.each(xitems, function(i)  {
29842             region = nb && i.region ? i.region : false;
29843             
29844             var add = ret.addxtype(i);
29845            
29846             if (region) {
29847                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29848                 if (!i.background) {
29849                     abn[region] = nb[region] ;
29850                 }
29851             }
29852             
29853         });
29854         this.endUpdate();
29855
29856         // make the last non-background panel active..
29857         //if (nb) { Roo.log(abn); }
29858         if (nb) {
29859             
29860             for(var r in abn) {
29861                 region = this.getRegion(r);
29862                 if (region) {
29863                     // tried using nb[r], but it does not work..
29864                      
29865                     region.showPanel(abn[r]);
29866                    
29867                 }
29868             }
29869         }
29870         return ret;
29871         
29872     }
29873 });
29874
29875 /**
29876  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29877  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29878  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29879  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29880  * <pre><code>
29881 // shorthand
29882 var CP = Roo.ContentPanel;
29883
29884 var layout = Roo.BorderLayout.create({
29885     north: {
29886         initialSize: 25,
29887         titlebar: false,
29888         panels: [new CP("north", "North")]
29889     },
29890     west: {
29891         split:true,
29892         initialSize: 200,
29893         minSize: 175,
29894         maxSize: 400,
29895         titlebar: true,
29896         collapsible: true,
29897         panels: [new CP("west", {title: "West"})]
29898     },
29899     east: {
29900         split:true,
29901         initialSize: 202,
29902         minSize: 175,
29903         maxSize: 400,
29904         titlebar: true,
29905         collapsible: true,
29906         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29907     },
29908     south: {
29909         split:true,
29910         initialSize: 100,
29911         minSize: 100,
29912         maxSize: 200,
29913         titlebar: true,
29914         collapsible: true,
29915         panels: [new CP("south", {title: "South", closable: true})]
29916     },
29917     center: {
29918         titlebar: true,
29919         autoScroll:true,
29920         resizeTabs: true,
29921         minTabWidth: 50,
29922         preferredTabWidth: 150,
29923         panels: [
29924             new CP("center1", {title: "Close Me", closable: true}),
29925             new CP("center2", {title: "Center Panel", closable: false})
29926         ]
29927     }
29928 }, document.body);
29929
29930 layout.getRegion("center").showPanel("center1");
29931 </code></pre>
29932  * @param config
29933  * @param targetEl
29934  */
29935 Roo.BorderLayout.create = function(config, targetEl){
29936     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29937     layout.beginUpdate();
29938     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29939     for(var j = 0, jlen = regions.length; j < jlen; j++){
29940         var lr = regions[j];
29941         if(layout.regions[lr] && config[lr].panels){
29942             var r = layout.regions[lr];
29943             var ps = config[lr].panels;
29944             layout.addTypedPanels(r, ps);
29945         }
29946     }
29947     layout.endUpdate();
29948     return layout;
29949 };
29950
29951 // private
29952 Roo.BorderLayout.RegionFactory = {
29953     // private
29954     validRegions : ["north","south","east","west","center"],
29955
29956     // private
29957     create : function(target, mgr, config){
29958         target = target.toLowerCase();
29959         if(config.lightweight || config.basic){
29960             return new Roo.BasicLayoutRegion(mgr, config, target);
29961         }
29962         switch(target){
29963             case "north":
29964                 return new Roo.NorthLayoutRegion(mgr, config);
29965             case "south":
29966                 return new Roo.SouthLayoutRegion(mgr, config);
29967             case "east":
29968                 return new Roo.EastLayoutRegion(mgr, config);
29969             case "west":
29970                 return new Roo.WestLayoutRegion(mgr, config);
29971             case "center":
29972                 return new Roo.CenterLayoutRegion(mgr, config);
29973         }
29974         throw 'Layout region "'+target+'" not supported.';
29975     }
29976 };/*
29977  * Based on:
29978  * Ext JS Library 1.1.1
29979  * Copyright(c) 2006-2007, Ext JS, LLC.
29980  *
29981  * Originally Released Under LGPL - original licence link has changed is not relivant.
29982  *
29983  * Fork - LGPL
29984  * <script type="text/javascript">
29985  */
29986  
29987 /**
29988  * @class Roo.BasicLayoutRegion
29989  * @extends Roo.util.Observable
29990  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29991  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29992  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29993  */
29994 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29995     this.mgr = mgr;
29996     this.position  = pos;
29997     this.events = {
29998         /**
29999          * @scope Roo.BasicLayoutRegion
30000          */
30001         
30002         /**
30003          * @event beforeremove
30004          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30005          * @param {Roo.LayoutRegion} this
30006          * @param {Roo.ContentPanel} panel The panel
30007          * @param {Object} e The cancel event object
30008          */
30009         "beforeremove" : true,
30010         /**
30011          * @event invalidated
30012          * Fires when the layout for this region is changed.
30013          * @param {Roo.LayoutRegion} this
30014          */
30015         "invalidated" : true,
30016         /**
30017          * @event visibilitychange
30018          * Fires when this region is shown or hidden 
30019          * @param {Roo.LayoutRegion} this
30020          * @param {Boolean} visibility true or false
30021          */
30022         "visibilitychange" : true,
30023         /**
30024          * @event paneladded
30025          * Fires when a panel is added. 
30026          * @param {Roo.LayoutRegion} this
30027          * @param {Roo.ContentPanel} panel The panel
30028          */
30029         "paneladded" : true,
30030         /**
30031          * @event panelremoved
30032          * Fires when a panel is removed. 
30033          * @param {Roo.LayoutRegion} this
30034          * @param {Roo.ContentPanel} panel The panel
30035          */
30036         "panelremoved" : true,
30037         /**
30038          * @event beforecollapse
30039          * Fires when this region before collapse.
30040          * @param {Roo.LayoutRegion} this
30041          */
30042         "beforecollapse" : true,
30043         /**
30044          * @event collapsed
30045          * Fires when this region is collapsed.
30046          * @param {Roo.LayoutRegion} this
30047          */
30048         "collapsed" : true,
30049         /**
30050          * @event expanded
30051          * Fires when this region is expanded.
30052          * @param {Roo.LayoutRegion} this
30053          */
30054         "expanded" : true,
30055         /**
30056          * @event slideshow
30057          * Fires when this region is slid into view.
30058          * @param {Roo.LayoutRegion} this
30059          */
30060         "slideshow" : true,
30061         /**
30062          * @event slidehide
30063          * Fires when this region slides out of view. 
30064          * @param {Roo.LayoutRegion} this
30065          */
30066         "slidehide" : true,
30067         /**
30068          * @event panelactivated
30069          * Fires when a panel is activated. 
30070          * @param {Roo.LayoutRegion} this
30071          * @param {Roo.ContentPanel} panel The activated panel
30072          */
30073         "panelactivated" : true,
30074         /**
30075          * @event resized
30076          * Fires when the user resizes this region. 
30077          * @param {Roo.LayoutRegion} this
30078          * @param {Number} newSize The new size (width for east/west, height for north/south)
30079          */
30080         "resized" : true
30081     };
30082     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30083     this.panels = new Roo.util.MixedCollection();
30084     this.panels.getKey = this.getPanelId.createDelegate(this);
30085     this.box = null;
30086     this.activePanel = null;
30087     // ensure listeners are added...
30088     
30089     if (config.listeners || config.events) {
30090         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30091             listeners : config.listeners || {},
30092             events : config.events || {}
30093         });
30094     }
30095     
30096     if(skipConfig !== true){
30097         this.applyConfig(config);
30098     }
30099 };
30100
30101 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30102     getPanelId : function(p){
30103         return p.getId();
30104     },
30105     
30106     applyConfig : function(config){
30107         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30108         this.config = config;
30109         
30110     },
30111     
30112     /**
30113      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30114      * the width, for horizontal (north, south) the height.
30115      * @param {Number} newSize The new width or height
30116      */
30117     resizeTo : function(newSize){
30118         var el = this.el ? this.el :
30119                  (this.activePanel ? this.activePanel.getEl() : null);
30120         if(el){
30121             switch(this.position){
30122                 case "east":
30123                 case "west":
30124                     el.setWidth(newSize);
30125                     this.fireEvent("resized", this, newSize);
30126                 break;
30127                 case "north":
30128                 case "south":
30129                     el.setHeight(newSize);
30130                     this.fireEvent("resized", this, newSize);
30131                 break;                
30132             }
30133         }
30134     },
30135     
30136     getBox : function(){
30137         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30138     },
30139     
30140     getMargins : function(){
30141         return this.margins;
30142     },
30143     
30144     updateBox : function(box){
30145         this.box = box;
30146         var el = this.activePanel.getEl();
30147         el.dom.style.left = box.x + "px";
30148         el.dom.style.top = box.y + "px";
30149         this.activePanel.setSize(box.width, box.height);
30150     },
30151     
30152     /**
30153      * Returns the container element for this region.
30154      * @return {Roo.Element}
30155      */
30156     getEl : function(){
30157         return this.activePanel;
30158     },
30159     
30160     /**
30161      * Returns true if this region is currently visible.
30162      * @return {Boolean}
30163      */
30164     isVisible : function(){
30165         return this.activePanel ? true : false;
30166     },
30167     
30168     setActivePanel : function(panel){
30169         panel = this.getPanel(panel);
30170         if(this.activePanel && this.activePanel != panel){
30171             this.activePanel.setActiveState(false);
30172             this.activePanel.getEl().setLeftTop(-10000,-10000);
30173         }
30174         this.activePanel = panel;
30175         panel.setActiveState(true);
30176         if(this.box){
30177             panel.setSize(this.box.width, this.box.height);
30178         }
30179         this.fireEvent("panelactivated", this, panel);
30180         this.fireEvent("invalidated");
30181     },
30182     
30183     /**
30184      * Show the specified panel.
30185      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30186      * @return {Roo.ContentPanel} The shown panel or null
30187      */
30188     showPanel : function(panel){
30189         if(panel = this.getPanel(panel)){
30190             this.setActivePanel(panel);
30191         }
30192         return panel;
30193     },
30194     
30195     /**
30196      * Get the active panel for this region.
30197      * @return {Roo.ContentPanel} The active panel or null
30198      */
30199     getActivePanel : function(){
30200         return this.activePanel;
30201     },
30202     
30203     /**
30204      * Add the passed ContentPanel(s)
30205      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30206      * @return {Roo.ContentPanel} The panel added (if only one was added)
30207      */
30208     add : function(panel){
30209         if(arguments.length > 1){
30210             for(var i = 0, len = arguments.length; i < len; i++) {
30211                 this.add(arguments[i]);
30212             }
30213             return null;
30214         }
30215         if(this.hasPanel(panel)){
30216             this.showPanel(panel);
30217             return panel;
30218         }
30219         var el = panel.getEl();
30220         if(el.dom.parentNode != this.mgr.el.dom){
30221             this.mgr.el.dom.appendChild(el.dom);
30222         }
30223         if(panel.setRegion){
30224             panel.setRegion(this);
30225         }
30226         this.panels.add(panel);
30227         el.setStyle("position", "absolute");
30228         if(!panel.background){
30229             this.setActivePanel(panel);
30230             if(this.config.initialSize && this.panels.getCount()==1){
30231                 this.resizeTo(this.config.initialSize);
30232             }
30233         }
30234         this.fireEvent("paneladded", this, panel);
30235         return panel;
30236     },
30237     
30238     /**
30239      * Returns true if the panel is in this region.
30240      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30241      * @return {Boolean}
30242      */
30243     hasPanel : function(panel){
30244         if(typeof panel == "object"){ // must be panel obj
30245             panel = panel.getId();
30246         }
30247         return this.getPanel(panel) ? true : false;
30248     },
30249     
30250     /**
30251      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30252      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30253      * @param {Boolean} preservePanel Overrides the config preservePanel option
30254      * @return {Roo.ContentPanel} The panel that was removed
30255      */
30256     remove : function(panel, preservePanel){
30257         panel = this.getPanel(panel);
30258         if(!panel){
30259             return null;
30260         }
30261         var e = {};
30262         this.fireEvent("beforeremove", this, panel, e);
30263         if(e.cancel === true){
30264             return null;
30265         }
30266         var panelId = panel.getId();
30267         this.panels.removeKey(panelId);
30268         return panel;
30269     },
30270     
30271     /**
30272      * Returns the panel specified or null if it's not in this region.
30273      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30274      * @return {Roo.ContentPanel}
30275      */
30276     getPanel : function(id){
30277         if(typeof id == "object"){ // must be panel obj
30278             return id;
30279         }
30280         return this.panels.get(id);
30281     },
30282     
30283     /**
30284      * Returns this regions position (north/south/east/west/center).
30285      * @return {String} 
30286      */
30287     getPosition: function(){
30288         return this.position;    
30289     }
30290 });/*
30291  * Based on:
30292  * Ext JS Library 1.1.1
30293  * Copyright(c) 2006-2007, Ext JS, LLC.
30294  *
30295  * Originally Released Under LGPL - original licence link has changed is not relivant.
30296  *
30297  * Fork - LGPL
30298  * <script type="text/javascript">
30299  */
30300  
30301 /**
30302  * @class Roo.LayoutRegion
30303  * @extends Roo.BasicLayoutRegion
30304  * This class represents a region in a layout manager.
30305  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30306  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30307  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30308  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30309  * @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})
30310  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30311  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30312  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30313  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30314  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30315  * @cfg {String}    title           The title for the region (overrides panel titles)
30316  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30317  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30318  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30319  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30320  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30321  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30322  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30323  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30324  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30325  * @cfg {Boolean}   showPin         True to show a pin button
30326  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30327  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30328  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30329  * @cfg {Number}    width           For East/West panels
30330  * @cfg {Number}    height          For North/South panels
30331  * @cfg {Boolean}   split           To show the splitter
30332  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30333  */
30334 Roo.LayoutRegion = function(mgr, config, pos){
30335     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30336     var dh = Roo.DomHelper;
30337     /** This region's container element 
30338     * @type Roo.Element */
30339     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30340     /** This region's title element 
30341     * @type Roo.Element */
30342
30343     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30344         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30345         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30346     ]}, true);
30347     this.titleEl.enableDisplayMode();
30348     /** This region's title text element 
30349     * @type HTMLElement */
30350     this.titleTextEl = this.titleEl.dom.firstChild;
30351     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30352     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30353     this.closeBtn.enableDisplayMode();
30354     this.closeBtn.on("click", this.closeClicked, this);
30355     this.closeBtn.hide();
30356
30357     this.createBody(config);
30358     this.visible = true;
30359     this.collapsed = false;
30360
30361     if(config.hideWhenEmpty){
30362         this.hide();
30363         this.on("paneladded", this.validateVisibility, this);
30364         this.on("panelremoved", this.validateVisibility, this);
30365     }
30366     this.applyConfig(config);
30367 };
30368
30369 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30370
30371     createBody : function(){
30372         /** This region's body element 
30373         * @type Roo.Element */
30374         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30375     },
30376
30377     applyConfig : function(c){
30378         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30379             var dh = Roo.DomHelper;
30380             if(c.titlebar !== false){
30381                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30382                 this.collapseBtn.on("click", this.collapse, this);
30383                 this.collapseBtn.enableDisplayMode();
30384
30385                 if(c.showPin === true || this.showPin){
30386                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30387                     this.stickBtn.enableDisplayMode();
30388                     this.stickBtn.on("click", this.expand, this);
30389                     this.stickBtn.hide();
30390                 }
30391             }
30392             /** This region's collapsed element
30393             * @type Roo.Element */
30394             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30395                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30396             ]}, true);
30397             if(c.floatable !== false){
30398                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30399                this.collapsedEl.on("click", this.collapseClick, this);
30400             }
30401
30402             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30403                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30404                    id: "message", unselectable: "on", style:{"float":"left"}});
30405                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30406              }
30407             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30408             this.expandBtn.on("click", this.expand, this);
30409         }
30410         if(this.collapseBtn){
30411             this.collapseBtn.setVisible(c.collapsible == true);
30412         }
30413         this.cmargins = c.cmargins || this.cmargins ||
30414                          (this.position == "west" || this.position == "east" ?
30415                              {top: 0, left: 2, right:2, bottom: 0} :
30416                              {top: 2, left: 0, right:0, bottom: 2});
30417         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30418         this.bottomTabs = c.tabPosition != "top";
30419         this.autoScroll = c.autoScroll || false;
30420         if(this.autoScroll){
30421             this.bodyEl.setStyle("overflow", "auto");
30422         }else{
30423             this.bodyEl.setStyle("overflow", "hidden");
30424         }
30425         //if(c.titlebar !== false){
30426             if((!c.titlebar && !c.title) || c.titlebar === false){
30427                 this.titleEl.hide();
30428             }else{
30429                 this.titleEl.show();
30430                 if(c.title){
30431                     this.titleTextEl.innerHTML = c.title;
30432                 }
30433             }
30434         //}
30435         this.duration = c.duration || .30;
30436         this.slideDuration = c.slideDuration || .45;
30437         this.config = c;
30438         if(c.collapsed){
30439             this.collapse(true);
30440         }
30441         if(c.hidden){
30442             this.hide();
30443         }
30444     },
30445     /**
30446      * Returns true if this region is currently visible.
30447      * @return {Boolean}
30448      */
30449     isVisible : function(){
30450         return this.visible;
30451     },
30452
30453     /**
30454      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30455      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30456      */
30457     setCollapsedTitle : function(title){
30458         title = title || "&#160;";
30459         if(this.collapsedTitleTextEl){
30460             this.collapsedTitleTextEl.innerHTML = title;
30461         }
30462     },
30463
30464     getBox : function(){
30465         var b;
30466         if(!this.collapsed){
30467             b = this.el.getBox(false, true);
30468         }else{
30469             b = this.collapsedEl.getBox(false, true);
30470         }
30471         return b;
30472     },
30473
30474     getMargins : function(){
30475         return this.collapsed ? this.cmargins : this.margins;
30476     },
30477
30478     highlight : function(){
30479         this.el.addClass("x-layout-panel-dragover");
30480     },
30481
30482     unhighlight : function(){
30483         this.el.removeClass("x-layout-panel-dragover");
30484     },
30485
30486     updateBox : function(box){
30487         this.box = box;
30488         if(!this.collapsed){
30489             this.el.dom.style.left = box.x + "px";
30490             this.el.dom.style.top = box.y + "px";
30491             this.updateBody(box.width, box.height);
30492         }else{
30493             this.collapsedEl.dom.style.left = box.x + "px";
30494             this.collapsedEl.dom.style.top = box.y + "px";
30495             this.collapsedEl.setSize(box.width, box.height);
30496         }
30497         if(this.tabs){
30498             this.tabs.autoSizeTabs();
30499         }
30500     },
30501
30502     updateBody : function(w, h){
30503         if(w !== null){
30504             this.el.setWidth(w);
30505             w -= this.el.getBorderWidth("rl");
30506             if(this.config.adjustments){
30507                 w += this.config.adjustments[0];
30508             }
30509         }
30510         if(h !== null){
30511             this.el.setHeight(h);
30512             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30513             h -= this.el.getBorderWidth("tb");
30514             if(this.config.adjustments){
30515                 h += this.config.adjustments[1];
30516             }
30517             this.bodyEl.setHeight(h);
30518             if(this.tabs){
30519                 h = this.tabs.syncHeight(h);
30520             }
30521         }
30522         if(this.panelSize){
30523             w = w !== null ? w : this.panelSize.width;
30524             h = h !== null ? h : this.panelSize.height;
30525         }
30526         if(this.activePanel){
30527             var el = this.activePanel.getEl();
30528             w = w !== null ? w : el.getWidth();
30529             h = h !== null ? h : el.getHeight();
30530             this.panelSize = {width: w, height: h};
30531             this.activePanel.setSize(w, h);
30532         }
30533         if(Roo.isIE && this.tabs){
30534             this.tabs.el.repaint();
30535         }
30536     },
30537
30538     /**
30539      * Returns the container element for this region.
30540      * @return {Roo.Element}
30541      */
30542     getEl : function(){
30543         return this.el;
30544     },
30545
30546     /**
30547      * Hides this region.
30548      */
30549     hide : function(){
30550         if(!this.collapsed){
30551             this.el.dom.style.left = "-2000px";
30552             this.el.hide();
30553         }else{
30554             this.collapsedEl.dom.style.left = "-2000px";
30555             this.collapsedEl.hide();
30556         }
30557         this.visible = false;
30558         this.fireEvent("visibilitychange", this, false);
30559     },
30560
30561     /**
30562      * Shows this region if it was previously hidden.
30563      */
30564     show : function(){
30565         if(!this.collapsed){
30566             this.el.show();
30567         }else{
30568             this.collapsedEl.show();
30569         }
30570         this.visible = true;
30571         this.fireEvent("visibilitychange", this, true);
30572     },
30573
30574     closeClicked : function(){
30575         if(this.activePanel){
30576             this.remove(this.activePanel);
30577         }
30578     },
30579
30580     collapseClick : function(e){
30581         if(this.isSlid){
30582            e.stopPropagation();
30583            this.slideIn();
30584         }else{
30585            e.stopPropagation();
30586            this.slideOut();
30587         }
30588     },
30589
30590     /**
30591      * Collapses this region.
30592      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30593      */
30594     collapse : function(skipAnim, skipCheck){
30595         if(this.collapsed) {
30596             return;
30597         }
30598         
30599         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30600             
30601             this.collapsed = true;
30602             if(this.split){
30603                 this.split.el.hide();
30604             }
30605             if(this.config.animate && skipAnim !== true){
30606                 this.fireEvent("invalidated", this);
30607                 this.animateCollapse();
30608             }else{
30609                 this.el.setLocation(-20000,-20000);
30610                 this.el.hide();
30611                 this.collapsedEl.show();
30612                 this.fireEvent("collapsed", this);
30613                 this.fireEvent("invalidated", this);
30614             }
30615         }
30616         
30617     },
30618
30619     animateCollapse : function(){
30620         // overridden
30621     },
30622
30623     /**
30624      * Expands this region if it was previously collapsed.
30625      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30626      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30627      */
30628     expand : function(e, skipAnim){
30629         if(e) {
30630             e.stopPropagation();
30631         }
30632         if(!this.collapsed || this.el.hasActiveFx()) {
30633             return;
30634         }
30635         if(this.isSlid){
30636             this.afterSlideIn();
30637             skipAnim = true;
30638         }
30639         this.collapsed = false;
30640         if(this.config.animate && skipAnim !== true){
30641             this.animateExpand();
30642         }else{
30643             this.el.show();
30644             if(this.split){
30645                 this.split.el.show();
30646             }
30647             this.collapsedEl.setLocation(-2000,-2000);
30648             this.collapsedEl.hide();
30649             this.fireEvent("invalidated", this);
30650             this.fireEvent("expanded", this);
30651         }
30652     },
30653
30654     animateExpand : function(){
30655         // overridden
30656     },
30657
30658     initTabs : function()
30659     {
30660         this.bodyEl.setStyle("overflow", "hidden");
30661         var ts = new Roo.TabPanel(
30662                 this.bodyEl.dom,
30663                 {
30664                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30665                     disableTooltips: this.config.disableTabTips,
30666                     toolbar : this.config.toolbar
30667                 }
30668         );
30669         if(this.config.hideTabs){
30670             ts.stripWrap.setDisplayed(false);
30671         }
30672         this.tabs = ts;
30673         ts.resizeTabs = this.config.resizeTabs === true;
30674         ts.minTabWidth = this.config.minTabWidth || 40;
30675         ts.maxTabWidth = this.config.maxTabWidth || 250;
30676         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30677         ts.monitorResize = false;
30678         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30679         ts.bodyEl.addClass('x-layout-tabs-body');
30680         this.panels.each(this.initPanelAsTab, this);
30681     },
30682
30683     initPanelAsTab : function(panel){
30684         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30685                     this.config.closeOnTab && panel.isClosable());
30686         if(panel.tabTip !== undefined){
30687             ti.setTooltip(panel.tabTip);
30688         }
30689         ti.on("activate", function(){
30690               this.setActivePanel(panel);
30691         }, this);
30692         if(this.config.closeOnTab){
30693             ti.on("beforeclose", function(t, e){
30694                 e.cancel = true;
30695                 this.remove(panel);
30696             }, this);
30697         }
30698         return ti;
30699     },
30700
30701     updatePanelTitle : function(panel, title){
30702         if(this.activePanel == panel){
30703             this.updateTitle(title);
30704         }
30705         if(this.tabs){
30706             var ti = this.tabs.getTab(panel.getEl().id);
30707             ti.setText(title);
30708             if(panel.tabTip !== undefined){
30709                 ti.setTooltip(panel.tabTip);
30710             }
30711         }
30712     },
30713
30714     updateTitle : function(title){
30715         if(this.titleTextEl && !this.config.title){
30716             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30717         }
30718     },
30719
30720     setActivePanel : function(panel){
30721         panel = this.getPanel(panel);
30722         if(this.activePanel && this.activePanel != panel){
30723             this.activePanel.setActiveState(false);
30724         }
30725         this.activePanel = panel;
30726         panel.setActiveState(true);
30727         if(this.panelSize){
30728             panel.setSize(this.panelSize.width, this.panelSize.height);
30729         }
30730         if(this.closeBtn){
30731             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30732         }
30733         this.updateTitle(panel.getTitle());
30734         if(this.tabs){
30735             this.fireEvent("invalidated", this);
30736         }
30737         this.fireEvent("panelactivated", this, panel);
30738     },
30739
30740     /**
30741      * Shows the specified panel.
30742      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30743      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30744      */
30745     showPanel : function(panel)
30746     {
30747         panel = this.getPanel(panel);
30748         if(panel){
30749             if(this.tabs){
30750                 var tab = this.tabs.getTab(panel.getEl().id);
30751                 if(tab.isHidden()){
30752                     this.tabs.unhideTab(tab.id);
30753                 }
30754                 tab.activate();
30755             }else{
30756                 this.setActivePanel(panel);
30757             }
30758         }
30759         return panel;
30760     },
30761
30762     /**
30763      * Get the active panel for this region.
30764      * @return {Roo.ContentPanel} The active panel or null
30765      */
30766     getActivePanel : function(){
30767         return this.activePanel;
30768     },
30769
30770     validateVisibility : function(){
30771         if(this.panels.getCount() < 1){
30772             this.updateTitle("&#160;");
30773             this.closeBtn.hide();
30774             this.hide();
30775         }else{
30776             if(!this.isVisible()){
30777                 this.show();
30778             }
30779         }
30780     },
30781
30782     /**
30783      * Adds the passed ContentPanel(s) to this region.
30784      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30785      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30786      */
30787     add : function(panel){
30788         if(arguments.length > 1){
30789             for(var i = 0, len = arguments.length; i < len; i++) {
30790                 this.add(arguments[i]);
30791             }
30792             return null;
30793         }
30794         if(this.hasPanel(panel)){
30795             this.showPanel(panel);
30796             return panel;
30797         }
30798         panel.setRegion(this);
30799         this.panels.add(panel);
30800         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30801             this.bodyEl.dom.appendChild(panel.getEl().dom);
30802             if(panel.background !== true){
30803                 this.setActivePanel(panel);
30804             }
30805             this.fireEvent("paneladded", this, panel);
30806             return panel;
30807         }
30808         if(!this.tabs){
30809             this.initTabs();
30810         }else{
30811             this.initPanelAsTab(panel);
30812         }
30813         if(panel.background !== true){
30814             this.tabs.activate(panel.getEl().id);
30815         }
30816         this.fireEvent("paneladded", this, panel);
30817         return panel;
30818     },
30819
30820     /**
30821      * Hides the tab for the specified panel.
30822      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30823      */
30824     hidePanel : function(panel){
30825         if(this.tabs && (panel = this.getPanel(panel))){
30826             this.tabs.hideTab(panel.getEl().id);
30827         }
30828     },
30829
30830     /**
30831      * Unhides the tab for a previously hidden panel.
30832      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30833      */
30834     unhidePanel : function(panel){
30835         if(this.tabs && (panel = this.getPanel(panel))){
30836             this.tabs.unhideTab(panel.getEl().id);
30837         }
30838     },
30839
30840     clearPanels : function(){
30841         while(this.panels.getCount() > 0){
30842              this.remove(this.panels.first());
30843         }
30844     },
30845
30846     /**
30847      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30848      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30849      * @param {Boolean} preservePanel Overrides the config preservePanel option
30850      * @return {Roo.ContentPanel} The panel that was removed
30851      */
30852     remove : function(panel, preservePanel){
30853         panel = this.getPanel(panel);
30854         if(!panel){
30855             return null;
30856         }
30857         var e = {};
30858         this.fireEvent("beforeremove", this, panel, e);
30859         if(e.cancel === true){
30860             return null;
30861         }
30862         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30863         var panelId = panel.getId();
30864         this.panels.removeKey(panelId);
30865         if(preservePanel){
30866             document.body.appendChild(panel.getEl().dom);
30867         }
30868         if(this.tabs){
30869             this.tabs.removeTab(panel.getEl().id);
30870         }else if (!preservePanel){
30871             this.bodyEl.dom.removeChild(panel.getEl().dom);
30872         }
30873         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30874             var p = this.panels.first();
30875             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30876             tempEl.appendChild(p.getEl().dom);
30877             this.bodyEl.update("");
30878             this.bodyEl.dom.appendChild(p.getEl().dom);
30879             tempEl = null;
30880             this.updateTitle(p.getTitle());
30881             this.tabs = null;
30882             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30883             this.setActivePanel(p);
30884         }
30885         panel.setRegion(null);
30886         if(this.activePanel == panel){
30887             this.activePanel = null;
30888         }
30889         if(this.config.autoDestroy !== false && preservePanel !== true){
30890             try{panel.destroy();}catch(e){}
30891         }
30892         this.fireEvent("panelremoved", this, panel);
30893         return panel;
30894     },
30895
30896     /**
30897      * Returns the TabPanel component used by this region
30898      * @return {Roo.TabPanel}
30899      */
30900     getTabs : function(){
30901         return this.tabs;
30902     },
30903
30904     createTool : function(parentEl, className){
30905         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30906             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30907         btn.addClassOnOver("x-layout-tools-button-over");
30908         return btn;
30909     }
30910 });/*
30911  * Based on:
30912  * Ext JS Library 1.1.1
30913  * Copyright(c) 2006-2007, Ext JS, LLC.
30914  *
30915  * Originally Released Under LGPL - original licence link has changed is not relivant.
30916  *
30917  * Fork - LGPL
30918  * <script type="text/javascript">
30919  */
30920  
30921
30922
30923 /**
30924  * @class Roo.SplitLayoutRegion
30925  * @extends Roo.LayoutRegion
30926  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30927  */
30928 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30929     this.cursor = cursor;
30930     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30931 };
30932
30933 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30934     splitTip : "Drag to resize.",
30935     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30936     useSplitTips : false,
30937
30938     applyConfig : function(config){
30939         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30940         if(config.split){
30941             if(!this.split){
30942                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30943                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30944                 /** The SplitBar for this region 
30945                 * @type Roo.SplitBar */
30946                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30947                 this.split.on("moved", this.onSplitMove, this);
30948                 this.split.useShim = config.useShim === true;
30949                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30950                 if(this.useSplitTips){
30951                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30952                 }
30953                 if(config.collapsible){
30954                     this.split.el.on("dblclick", this.collapse,  this);
30955                 }
30956             }
30957             if(typeof config.minSize != "undefined"){
30958                 this.split.minSize = config.minSize;
30959             }
30960             if(typeof config.maxSize != "undefined"){
30961                 this.split.maxSize = config.maxSize;
30962             }
30963             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30964                 this.hideSplitter();
30965             }
30966         }
30967     },
30968
30969     getHMaxSize : function(){
30970          var cmax = this.config.maxSize || 10000;
30971          var center = this.mgr.getRegion("center");
30972          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30973     },
30974
30975     getVMaxSize : function(){
30976          var cmax = this.config.maxSize || 10000;
30977          var center = this.mgr.getRegion("center");
30978          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30979     },
30980
30981     onSplitMove : function(split, newSize){
30982         this.fireEvent("resized", this, newSize);
30983     },
30984     
30985     /** 
30986      * Returns the {@link Roo.SplitBar} for this region.
30987      * @return {Roo.SplitBar}
30988      */
30989     getSplitBar : function(){
30990         return this.split;
30991     },
30992     
30993     hide : function(){
30994         this.hideSplitter();
30995         Roo.SplitLayoutRegion.superclass.hide.call(this);
30996     },
30997
30998     hideSplitter : function(){
30999         if(this.split){
31000             this.split.el.setLocation(-2000,-2000);
31001             this.split.el.hide();
31002         }
31003     },
31004
31005     show : function(){
31006         if(this.split){
31007             this.split.el.show();
31008         }
31009         Roo.SplitLayoutRegion.superclass.show.call(this);
31010     },
31011     
31012     beforeSlide: function(){
31013         if(Roo.isGecko){// firefox overflow auto bug workaround
31014             this.bodyEl.clip();
31015             if(this.tabs) {
31016                 this.tabs.bodyEl.clip();
31017             }
31018             if(this.activePanel){
31019                 this.activePanel.getEl().clip();
31020                 
31021                 if(this.activePanel.beforeSlide){
31022                     this.activePanel.beforeSlide();
31023                 }
31024             }
31025         }
31026     },
31027     
31028     afterSlide : function(){
31029         if(Roo.isGecko){// firefox overflow auto bug workaround
31030             this.bodyEl.unclip();
31031             if(this.tabs) {
31032                 this.tabs.bodyEl.unclip();
31033             }
31034             if(this.activePanel){
31035                 this.activePanel.getEl().unclip();
31036                 if(this.activePanel.afterSlide){
31037                     this.activePanel.afterSlide();
31038                 }
31039             }
31040         }
31041     },
31042
31043     initAutoHide : function(){
31044         if(this.autoHide !== false){
31045             if(!this.autoHideHd){
31046                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31047                 this.autoHideHd = {
31048                     "mouseout": function(e){
31049                         if(!e.within(this.el, true)){
31050                             st.delay(500);
31051                         }
31052                     },
31053                     "mouseover" : function(e){
31054                         st.cancel();
31055                     },
31056                     scope : this
31057                 };
31058             }
31059             this.el.on(this.autoHideHd);
31060         }
31061     },
31062
31063     clearAutoHide : function(){
31064         if(this.autoHide !== false){
31065             this.el.un("mouseout", this.autoHideHd.mouseout);
31066             this.el.un("mouseover", this.autoHideHd.mouseover);
31067         }
31068     },
31069
31070     clearMonitor : function(){
31071         Roo.get(document).un("click", this.slideInIf, this);
31072     },
31073
31074     // these names are backwards but not changed for compat
31075     slideOut : function(){
31076         if(this.isSlid || this.el.hasActiveFx()){
31077             return;
31078         }
31079         this.isSlid = true;
31080         if(this.collapseBtn){
31081             this.collapseBtn.hide();
31082         }
31083         this.closeBtnState = this.closeBtn.getStyle('display');
31084         this.closeBtn.hide();
31085         if(this.stickBtn){
31086             this.stickBtn.show();
31087         }
31088         this.el.show();
31089         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31090         this.beforeSlide();
31091         this.el.setStyle("z-index", 10001);
31092         this.el.slideIn(this.getSlideAnchor(), {
31093             callback: function(){
31094                 this.afterSlide();
31095                 this.initAutoHide();
31096                 Roo.get(document).on("click", this.slideInIf, this);
31097                 this.fireEvent("slideshow", this);
31098             },
31099             scope: this,
31100             block: true
31101         });
31102     },
31103
31104     afterSlideIn : function(){
31105         this.clearAutoHide();
31106         this.isSlid = false;
31107         this.clearMonitor();
31108         this.el.setStyle("z-index", "");
31109         if(this.collapseBtn){
31110             this.collapseBtn.show();
31111         }
31112         this.closeBtn.setStyle('display', this.closeBtnState);
31113         if(this.stickBtn){
31114             this.stickBtn.hide();
31115         }
31116         this.fireEvent("slidehide", this);
31117     },
31118
31119     slideIn : function(cb){
31120         if(!this.isSlid || this.el.hasActiveFx()){
31121             Roo.callback(cb);
31122             return;
31123         }
31124         this.isSlid = false;
31125         this.beforeSlide();
31126         this.el.slideOut(this.getSlideAnchor(), {
31127             callback: function(){
31128                 this.el.setLeftTop(-10000, -10000);
31129                 this.afterSlide();
31130                 this.afterSlideIn();
31131                 Roo.callback(cb);
31132             },
31133             scope: this,
31134             block: true
31135         });
31136     },
31137     
31138     slideInIf : function(e){
31139         if(!e.within(this.el)){
31140             this.slideIn();
31141         }
31142     },
31143
31144     animateCollapse : function(){
31145         this.beforeSlide();
31146         this.el.setStyle("z-index", 20000);
31147         var anchor = this.getSlideAnchor();
31148         this.el.slideOut(anchor, {
31149             callback : function(){
31150                 this.el.setStyle("z-index", "");
31151                 this.collapsedEl.slideIn(anchor, {duration:.3});
31152                 this.afterSlide();
31153                 this.el.setLocation(-10000,-10000);
31154                 this.el.hide();
31155                 this.fireEvent("collapsed", this);
31156             },
31157             scope: this,
31158             block: true
31159         });
31160     },
31161
31162     animateExpand : function(){
31163         this.beforeSlide();
31164         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31165         this.el.setStyle("z-index", 20000);
31166         this.collapsedEl.hide({
31167             duration:.1
31168         });
31169         this.el.slideIn(this.getSlideAnchor(), {
31170             callback : function(){
31171                 this.el.setStyle("z-index", "");
31172                 this.afterSlide();
31173                 if(this.split){
31174                     this.split.el.show();
31175                 }
31176                 this.fireEvent("invalidated", this);
31177                 this.fireEvent("expanded", this);
31178             },
31179             scope: this,
31180             block: true
31181         });
31182     },
31183
31184     anchors : {
31185         "west" : "left",
31186         "east" : "right",
31187         "north" : "top",
31188         "south" : "bottom"
31189     },
31190
31191     sanchors : {
31192         "west" : "l",
31193         "east" : "r",
31194         "north" : "t",
31195         "south" : "b"
31196     },
31197
31198     canchors : {
31199         "west" : "tl-tr",
31200         "east" : "tr-tl",
31201         "north" : "tl-bl",
31202         "south" : "bl-tl"
31203     },
31204
31205     getAnchor : function(){
31206         return this.anchors[this.position];
31207     },
31208
31209     getCollapseAnchor : function(){
31210         return this.canchors[this.position];
31211     },
31212
31213     getSlideAnchor : function(){
31214         return this.sanchors[this.position];
31215     },
31216
31217     getAlignAdj : function(){
31218         var cm = this.cmargins;
31219         switch(this.position){
31220             case "west":
31221                 return [0, 0];
31222             break;
31223             case "east":
31224                 return [0, 0];
31225             break;
31226             case "north":
31227                 return [0, 0];
31228             break;
31229             case "south":
31230                 return [0, 0];
31231             break;
31232         }
31233     },
31234
31235     getExpandAdj : function(){
31236         var c = this.collapsedEl, cm = this.cmargins;
31237         switch(this.position){
31238             case "west":
31239                 return [-(cm.right+c.getWidth()+cm.left), 0];
31240             break;
31241             case "east":
31242                 return [cm.right+c.getWidth()+cm.left, 0];
31243             break;
31244             case "north":
31245                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31246             break;
31247             case "south":
31248                 return [0, cm.top+cm.bottom+c.getHeight()];
31249             break;
31250         }
31251     }
31252 });/*
31253  * Based on:
31254  * Ext JS Library 1.1.1
31255  * Copyright(c) 2006-2007, Ext JS, LLC.
31256  *
31257  * Originally Released Under LGPL - original licence link has changed is not relivant.
31258  *
31259  * Fork - LGPL
31260  * <script type="text/javascript">
31261  */
31262 /*
31263  * These classes are private internal classes
31264  */
31265 Roo.CenterLayoutRegion = function(mgr, config){
31266     Roo.LayoutRegion.call(this, mgr, config, "center");
31267     this.visible = true;
31268     this.minWidth = config.minWidth || 20;
31269     this.minHeight = config.minHeight || 20;
31270 };
31271
31272 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31273     hide : function(){
31274         // center panel can't be hidden
31275     },
31276     
31277     show : function(){
31278         // center panel can't be hidden
31279     },
31280     
31281     getMinWidth: function(){
31282         return this.minWidth;
31283     },
31284     
31285     getMinHeight: function(){
31286         return this.minHeight;
31287     }
31288 });
31289
31290
31291 Roo.NorthLayoutRegion = function(mgr, config){
31292     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31293     if(this.split){
31294         this.split.placement = Roo.SplitBar.TOP;
31295         this.split.orientation = Roo.SplitBar.VERTICAL;
31296         this.split.el.addClass("x-layout-split-v");
31297     }
31298     var size = config.initialSize || config.height;
31299     if(typeof size != "undefined"){
31300         this.el.setHeight(size);
31301     }
31302 };
31303 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31304     orientation: Roo.SplitBar.VERTICAL,
31305     getBox : function(){
31306         if(this.collapsed){
31307             return this.collapsedEl.getBox();
31308         }
31309         var box = this.el.getBox();
31310         if(this.split){
31311             box.height += this.split.el.getHeight();
31312         }
31313         return box;
31314     },
31315     
31316     updateBox : function(box){
31317         if(this.split && !this.collapsed){
31318             box.height -= this.split.el.getHeight();
31319             this.split.el.setLeft(box.x);
31320             this.split.el.setTop(box.y+box.height);
31321             this.split.el.setWidth(box.width);
31322         }
31323         if(this.collapsed){
31324             this.updateBody(box.width, null);
31325         }
31326         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31327     }
31328 });
31329
31330 Roo.SouthLayoutRegion = function(mgr, config){
31331     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31332     if(this.split){
31333         this.split.placement = Roo.SplitBar.BOTTOM;
31334         this.split.orientation = Roo.SplitBar.VERTICAL;
31335         this.split.el.addClass("x-layout-split-v");
31336     }
31337     var size = config.initialSize || config.height;
31338     if(typeof size != "undefined"){
31339         this.el.setHeight(size);
31340     }
31341 };
31342 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31343     orientation: Roo.SplitBar.VERTICAL,
31344     getBox : function(){
31345         if(this.collapsed){
31346             return this.collapsedEl.getBox();
31347         }
31348         var box = this.el.getBox();
31349         if(this.split){
31350             var sh = this.split.el.getHeight();
31351             box.height += sh;
31352             box.y -= sh;
31353         }
31354         return box;
31355     },
31356     
31357     updateBox : function(box){
31358         if(this.split && !this.collapsed){
31359             var sh = this.split.el.getHeight();
31360             box.height -= sh;
31361             box.y += sh;
31362             this.split.el.setLeft(box.x);
31363             this.split.el.setTop(box.y-sh);
31364             this.split.el.setWidth(box.width);
31365         }
31366         if(this.collapsed){
31367             this.updateBody(box.width, null);
31368         }
31369         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31370     }
31371 });
31372
31373 Roo.EastLayoutRegion = function(mgr, config){
31374     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31375     if(this.split){
31376         this.split.placement = Roo.SplitBar.RIGHT;
31377         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31378         this.split.el.addClass("x-layout-split-h");
31379     }
31380     var size = config.initialSize || config.width;
31381     if(typeof size != "undefined"){
31382         this.el.setWidth(size);
31383     }
31384 };
31385 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31386     orientation: Roo.SplitBar.HORIZONTAL,
31387     getBox : function(){
31388         if(this.collapsed){
31389             return this.collapsedEl.getBox();
31390         }
31391         var box = this.el.getBox();
31392         if(this.split){
31393             var sw = this.split.el.getWidth();
31394             box.width += sw;
31395             box.x -= sw;
31396         }
31397         return box;
31398     },
31399
31400     updateBox : function(box){
31401         if(this.split && !this.collapsed){
31402             var sw = this.split.el.getWidth();
31403             box.width -= sw;
31404             this.split.el.setLeft(box.x);
31405             this.split.el.setTop(box.y);
31406             this.split.el.setHeight(box.height);
31407             box.x += sw;
31408         }
31409         if(this.collapsed){
31410             this.updateBody(null, box.height);
31411         }
31412         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31413     }
31414 });
31415
31416 Roo.WestLayoutRegion = function(mgr, config){
31417     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31418     if(this.split){
31419         this.split.placement = Roo.SplitBar.LEFT;
31420         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31421         this.split.el.addClass("x-layout-split-h");
31422     }
31423     var size = config.initialSize || config.width;
31424     if(typeof size != "undefined"){
31425         this.el.setWidth(size);
31426     }
31427 };
31428 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31429     orientation: Roo.SplitBar.HORIZONTAL,
31430     getBox : function(){
31431         if(this.collapsed){
31432             return this.collapsedEl.getBox();
31433         }
31434         var box = this.el.getBox();
31435         if(this.split){
31436             box.width += this.split.el.getWidth();
31437         }
31438         return box;
31439     },
31440     
31441     updateBox : function(box){
31442         if(this.split && !this.collapsed){
31443             var sw = this.split.el.getWidth();
31444             box.width -= sw;
31445             this.split.el.setLeft(box.x+box.width);
31446             this.split.el.setTop(box.y);
31447             this.split.el.setHeight(box.height);
31448         }
31449         if(this.collapsed){
31450             this.updateBody(null, box.height);
31451         }
31452         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31453     }
31454 });
31455 /*
31456  * Based on:
31457  * Ext JS Library 1.1.1
31458  * Copyright(c) 2006-2007, Ext JS, LLC.
31459  *
31460  * Originally Released Under LGPL - original licence link has changed is not relivant.
31461  *
31462  * Fork - LGPL
31463  * <script type="text/javascript">
31464  */
31465  
31466  
31467 /*
31468  * Private internal class for reading and applying state
31469  */
31470 Roo.LayoutStateManager = function(layout){
31471      // default empty state
31472      this.state = {
31473         north: {},
31474         south: {},
31475         east: {},
31476         west: {}       
31477     };
31478 };
31479
31480 Roo.LayoutStateManager.prototype = {
31481     init : function(layout, provider){
31482         this.provider = provider;
31483         var state = provider.get(layout.id+"-layout-state");
31484         if(state){
31485             var wasUpdating = layout.isUpdating();
31486             if(!wasUpdating){
31487                 layout.beginUpdate();
31488             }
31489             for(var key in state){
31490                 if(typeof state[key] != "function"){
31491                     var rstate = state[key];
31492                     var r = layout.getRegion(key);
31493                     if(r && rstate){
31494                         if(rstate.size){
31495                             r.resizeTo(rstate.size);
31496                         }
31497                         if(rstate.collapsed == true){
31498                             r.collapse(true);
31499                         }else{
31500                             r.expand(null, true);
31501                         }
31502                     }
31503                 }
31504             }
31505             if(!wasUpdating){
31506                 layout.endUpdate();
31507             }
31508             this.state = state; 
31509         }
31510         this.layout = layout;
31511         layout.on("regionresized", this.onRegionResized, this);
31512         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31513         layout.on("regionexpanded", this.onRegionExpanded, this);
31514     },
31515     
31516     storeState : function(){
31517         this.provider.set(this.layout.id+"-layout-state", this.state);
31518     },
31519     
31520     onRegionResized : function(region, newSize){
31521         this.state[region.getPosition()].size = newSize;
31522         this.storeState();
31523     },
31524     
31525     onRegionCollapsed : function(region){
31526         this.state[region.getPosition()].collapsed = true;
31527         this.storeState();
31528     },
31529     
31530     onRegionExpanded : function(region){
31531         this.state[region.getPosition()].collapsed = false;
31532         this.storeState();
31533     }
31534 };/*
31535  * Based on:
31536  * Ext JS Library 1.1.1
31537  * Copyright(c) 2006-2007, Ext JS, LLC.
31538  *
31539  * Originally Released Under LGPL - original licence link has changed is not relivant.
31540  *
31541  * Fork - LGPL
31542  * <script type="text/javascript">
31543  */
31544 /**
31545  * @class Roo.ContentPanel
31546  * @extends Roo.util.Observable
31547  * A basic ContentPanel element.
31548  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31549  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31550  * @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
31551  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31552  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31553  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31554  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31555  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31556  * @cfg {String} title          The title for this panel
31557  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31558  * @cfg {String} url            Calls {@link #setUrl} with this value
31559  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31560  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31561  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31562  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31563
31564  * @constructor
31565  * Create a new ContentPanel.
31566  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31567  * @param {String/Object} config A string to set only the title or a config object
31568  * @param {String} content (optional) Set the HTML content for this panel
31569  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31570  */
31571 Roo.ContentPanel = function(el, config, content){
31572     
31573      
31574     /*
31575     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31576         config = el;
31577         el = Roo.id();
31578     }
31579     if (config && config.parentLayout) { 
31580         el = config.parentLayout.el.createChild(); 
31581     }
31582     */
31583     if(el.autoCreate){ // xtype is available if this is called from factory
31584         config = el;
31585         el = Roo.id();
31586     }
31587     this.el = Roo.get(el);
31588     if(!this.el && config && config.autoCreate){
31589         if(typeof config.autoCreate == "object"){
31590             if(!config.autoCreate.id){
31591                 config.autoCreate.id = config.id||el;
31592             }
31593             this.el = Roo.DomHelper.append(document.body,
31594                         config.autoCreate, true);
31595         }else{
31596             this.el = Roo.DomHelper.append(document.body,
31597                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31598         }
31599     }
31600     this.closable = false;
31601     this.loaded = false;
31602     this.active = false;
31603     if(typeof config == "string"){
31604         this.title = config;
31605     }else{
31606         Roo.apply(this, config);
31607     }
31608     
31609     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31610         this.wrapEl = this.el.wrap();
31611         this.toolbar.container = this.el.insertSibling(false, 'before');
31612         this.toolbar = new Roo.Toolbar(this.toolbar);
31613     }
31614     
31615     // xtype created footer. - not sure if will work as we normally have to render first..
31616     if (this.footer && !this.footer.el && this.footer.xtype) {
31617         if (!this.wrapEl) {
31618             this.wrapEl = this.el.wrap();
31619         }
31620     
31621         this.footer.container = this.wrapEl.createChild();
31622          
31623         this.footer = Roo.factory(this.footer, Roo);
31624         
31625     }
31626     
31627     if(this.resizeEl){
31628         this.resizeEl = Roo.get(this.resizeEl, true);
31629     }else{
31630         this.resizeEl = this.el;
31631     }
31632     // handle view.xtype
31633     
31634  
31635     
31636     
31637     this.addEvents({
31638         /**
31639          * @event activate
31640          * Fires when this panel is activated. 
31641          * @param {Roo.ContentPanel} this
31642          */
31643         "activate" : true,
31644         /**
31645          * @event deactivate
31646          * Fires when this panel is activated. 
31647          * @param {Roo.ContentPanel} this
31648          */
31649         "deactivate" : true,
31650
31651         /**
31652          * @event resize
31653          * Fires when this panel is resized if fitToFrame is true.
31654          * @param {Roo.ContentPanel} this
31655          * @param {Number} width The width after any component adjustments
31656          * @param {Number} height The height after any component adjustments
31657          */
31658         "resize" : true,
31659         
31660          /**
31661          * @event render
31662          * Fires when this tab is created
31663          * @param {Roo.ContentPanel} this
31664          */
31665         "render" : true
31666          
31667         
31668     });
31669     
31670
31671     
31672     
31673     if(this.autoScroll){
31674         this.resizeEl.setStyle("overflow", "auto");
31675     } else {
31676         // fix randome scrolling
31677         this.el.on('scroll', function() {
31678             Roo.log('fix random scolling');
31679             this.scrollTo('top',0); 
31680         });
31681     }
31682     content = content || this.content;
31683     if(content){
31684         this.setContent(content);
31685     }
31686     if(config && config.url){
31687         this.setUrl(this.url, this.params, this.loadOnce);
31688     }
31689     
31690     
31691     
31692     Roo.ContentPanel.superclass.constructor.call(this);
31693     
31694     if (this.view && typeof(this.view.xtype) != 'undefined') {
31695         this.view.el = this.el.appendChild(document.createElement("div"));
31696         this.view = Roo.factory(this.view); 
31697         this.view.render  &&  this.view.render(false, '');  
31698     }
31699     
31700     
31701     this.fireEvent('render', this);
31702 };
31703
31704 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31705     tabTip:'',
31706     setRegion : function(region){
31707         this.region = region;
31708         if(region){
31709            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31710         }else{
31711            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31712         } 
31713     },
31714     
31715     /**
31716      * Returns the toolbar for this Panel if one was configured. 
31717      * @return {Roo.Toolbar} 
31718      */
31719     getToolbar : function(){
31720         return this.toolbar;
31721     },
31722     
31723     setActiveState : function(active){
31724         this.active = active;
31725         if(!active){
31726             this.fireEvent("deactivate", this);
31727         }else{
31728             this.fireEvent("activate", this);
31729         }
31730     },
31731     /**
31732      * Updates this panel's element
31733      * @param {String} content The new content
31734      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31735     */
31736     setContent : function(content, loadScripts){
31737         this.el.update(content, loadScripts);
31738     },
31739
31740     ignoreResize : function(w, h){
31741         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31742             return true;
31743         }else{
31744             this.lastSize = {width: w, height: h};
31745             return false;
31746         }
31747     },
31748     /**
31749      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31750      * @return {Roo.UpdateManager} The UpdateManager
31751      */
31752     getUpdateManager : function(){
31753         return this.el.getUpdateManager();
31754     },
31755      /**
31756      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31757      * @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:
31758 <pre><code>
31759 panel.load({
31760     url: "your-url.php",
31761     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31762     callback: yourFunction,
31763     scope: yourObject, //(optional scope)
31764     discardUrl: false,
31765     nocache: false,
31766     text: "Loading...",
31767     timeout: 30,
31768     scripts: false
31769 });
31770 </code></pre>
31771      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31772      * 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.
31773      * @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}
31774      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31775      * @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.
31776      * @return {Roo.ContentPanel} this
31777      */
31778     load : function(){
31779         var um = this.el.getUpdateManager();
31780         um.update.apply(um, arguments);
31781         return this;
31782     },
31783
31784
31785     /**
31786      * 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.
31787      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31788      * @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)
31789      * @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)
31790      * @return {Roo.UpdateManager} The UpdateManager
31791      */
31792     setUrl : function(url, params, loadOnce){
31793         if(this.refreshDelegate){
31794             this.removeListener("activate", this.refreshDelegate);
31795         }
31796         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31797         this.on("activate", this.refreshDelegate);
31798         return this.el.getUpdateManager();
31799     },
31800     
31801     _handleRefresh : function(url, params, loadOnce){
31802         if(!loadOnce || !this.loaded){
31803             var updater = this.el.getUpdateManager();
31804             updater.update(url, params, this._setLoaded.createDelegate(this));
31805         }
31806     },
31807     
31808     _setLoaded : function(){
31809         this.loaded = true;
31810     }, 
31811     
31812     /**
31813      * Returns this panel's id
31814      * @return {String} 
31815      */
31816     getId : function(){
31817         return this.el.id;
31818     },
31819     
31820     /** 
31821      * Returns this panel's element - used by regiosn to add.
31822      * @return {Roo.Element} 
31823      */
31824     getEl : function(){
31825         return this.wrapEl || this.el;
31826     },
31827     
31828     adjustForComponents : function(width, height)
31829     {
31830         //Roo.log('adjustForComponents ');
31831         if(this.resizeEl != this.el){
31832             width -= this.el.getFrameWidth('lr');
31833             height -= this.el.getFrameWidth('tb');
31834         }
31835         if(this.toolbar){
31836             var te = this.toolbar.getEl();
31837             height -= te.getHeight();
31838             te.setWidth(width);
31839         }
31840         if(this.footer){
31841             var te = this.footer.getEl();
31842             //Roo.log("footer:" + te.getHeight());
31843             
31844             height -= te.getHeight();
31845             te.setWidth(width);
31846         }
31847         
31848         
31849         if(this.adjustments){
31850             width += this.adjustments[0];
31851             height += this.adjustments[1];
31852         }
31853         return {"width": width, "height": height};
31854     },
31855     
31856     setSize : function(width, height){
31857         if(this.fitToFrame && !this.ignoreResize(width, height)){
31858             if(this.fitContainer && this.resizeEl != this.el){
31859                 this.el.setSize(width, height);
31860             }
31861             var size = this.adjustForComponents(width, height);
31862             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31863             this.fireEvent('resize', this, size.width, size.height);
31864         }
31865     },
31866     
31867     /**
31868      * Returns this panel's title
31869      * @return {String} 
31870      */
31871     getTitle : function(){
31872         return this.title;
31873     },
31874     
31875     /**
31876      * Set this panel's title
31877      * @param {String} title
31878      */
31879     setTitle : function(title){
31880         this.title = title;
31881         if(this.region){
31882             this.region.updatePanelTitle(this, title);
31883         }
31884     },
31885     
31886     /**
31887      * Returns true is this panel was configured to be closable
31888      * @return {Boolean} 
31889      */
31890     isClosable : function(){
31891         return this.closable;
31892     },
31893     
31894     beforeSlide : function(){
31895         this.el.clip();
31896         this.resizeEl.clip();
31897     },
31898     
31899     afterSlide : function(){
31900         this.el.unclip();
31901         this.resizeEl.unclip();
31902     },
31903     
31904     /**
31905      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31906      *   Will fail silently if the {@link #setUrl} method has not been called.
31907      *   This does not activate the panel, just updates its content.
31908      */
31909     refresh : function(){
31910         if(this.refreshDelegate){
31911            this.loaded = false;
31912            this.refreshDelegate();
31913         }
31914     },
31915     
31916     /**
31917      * Destroys this panel
31918      */
31919     destroy : function(){
31920         this.el.removeAllListeners();
31921         var tempEl = document.createElement("span");
31922         tempEl.appendChild(this.el.dom);
31923         tempEl.innerHTML = "";
31924         this.el.remove();
31925         this.el = null;
31926     },
31927     
31928     /**
31929      * form - if the content panel contains a form - this is a reference to it.
31930      * @type {Roo.form.Form}
31931      */
31932     form : false,
31933     /**
31934      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31935      *    This contains a reference to it.
31936      * @type {Roo.View}
31937      */
31938     view : false,
31939     
31940       /**
31941      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31942      * <pre><code>
31943
31944 layout.addxtype({
31945        xtype : 'Form',
31946        items: [ .... ]
31947    }
31948 );
31949
31950 </code></pre>
31951      * @param {Object} cfg Xtype definition of item to add.
31952      */
31953     
31954     addxtype : function(cfg) {
31955         // add form..
31956         if (cfg.xtype.match(/^Form$/)) {
31957             
31958             var el;
31959             //if (this.footer) {
31960             //    el = this.footer.container.insertSibling(false, 'before');
31961             //} else {
31962                 el = this.el.createChild();
31963             //}
31964
31965             this.form = new  Roo.form.Form(cfg);
31966             
31967             
31968             if ( this.form.allItems.length) {
31969                 this.form.render(el.dom);
31970             }
31971             return this.form;
31972         }
31973         // should only have one of theses..
31974         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31975             // views.. should not be just added - used named prop 'view''
31976             
31977             cfg.el = this.el.appendChild(document.createElement("div"));
31978             // factory?
31979             
31980             var ret = new Roo.factory(cfg);
31981              
31982              ret.render && ret.render(false, ''); // render blank..
31983             this.view = ret;
31984             return ret;
31985         }
31986         return false;
31987     }
31988 });
31989
31990 /**
31991  * @class Roo.GridPanel
31992  * @extends Roo.ContentPanel
31993  * @constructor
31994  * Create a new GridPanel.
31995  * @param {Roo.grid.Grid} grid The grid for this panel
31996  * @param {String/Object} config A string to set only the panel's title, or a config object
31997  */
31998 Roo.GridPanel = function(grid, config){
31999     
32000   
32001     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32002         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32003         
32004     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32005     
32006     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32007     
32008     if(this.toolbar){
32009         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32010     }
32011     // xtype created footer. - not sure if will work as we normally have to render first..
32012     if (this.footer && !this.footer.el && this.footer.xtype) {
32013         
32014         this.footer.container = this.grid.getView().getFooterPanel(true);
32015         this.footer.dataSource = this.grid.dataSource;
32016         this.footer = Roo.factory(this.footer, Roo);
32017         
32018     }
32019     
32020     grid.monitorWindowResize = false; // turn off autosizing
32021     grid.autoHeight = false;
32022     grid.autoWidth = false;
32023     this.grid = grid;
32024     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32025 };
32026
32027 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32028     getId : function(){
32029         return this.grid.id;
32030     },
32031     
32032     /**
32033      * Returns the grid for this panel
32034      * @return {Roo.grid.Grid} 
32035      */
32036     getGrid : function(){
32037         return this.grid;    
32038     },
32039     
32040     setSize : function(width, height){
32041         if(!this.ignoreResize(width, height)){
32042             var grid = this.grid;
32043             var size = this.adjustForComponents(width, height);
32044             grid.getGridEl().setSize(size.width, size.height);
32045             grid.autoSize();
32046         }
32047     },
32048     
32049     beforeSlide : function(){
32050         this.grid.getView().scroller.clip();
32051     },
32052     
32053     afterSlide : function(){
32054         this.grid.getView().scroller.unclip();
32055     },
32056     
32057     destroy : function(){
32058         this.grid.destroy();
32059         delete this.grid;
32060         Roo.GridPanel.superclass.destroy.call(this); 
32061     }
32062 });
32063
32064
32065 /**
32066  * @class Roo.NestedLayoutPanel
32067  * @extends Roo.ContentPanel
32068  * @constructor
32069  * Create a new NestedLayoutPanel.
32070  * 
32071  * 
32072  * @param {Roo.BorderLayout} layout The layout for this panel
32073  * @param {String/Object} config A string to set only the title or a config object
32074  */
32075 Roo.NestedLayoutPanel = function(layout, config)
32076 {
32077     // construct with only one argument..
32078     /* FIXME - implement nicer consturctors
32079     if (layout.layout) {
32080         config = layout;
32081         layout = config.layout;
32082         delete config.layout;
32083     }
32084     if (layout.xtype && !layout.getEl) {
32085         // then layout needs constructing..
32086         layout = Roo.factory(layout, Roo);
32087     }
32088     */
32089     
32090     
32091     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32092     
32093     layout.monitorWindowResize = false; // turn off autosizing
32094     this.layout = layout;
32095     this.layout.getEl().addClass("x-layout-nested-layout");
32096     
32097     
32098     
32099     
32100 };
32101
32102 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32103
32104     setSize : function(width, height){
32105         if(!this.ignoreResize(width, height)){
32106             var size = this.adjustForComponents(width, height);
32107             var el = this.layout.getEl();
32108             el.setSize(size.width, size.height);
32109             var touch = el.dom.offsetWidth;
32110             this.layout.layout();
32111             // ie requires a double layout on the first pass
32112             if(Roo.isIE && !this.initialized){
32113                 this.initialized = true;
32114                 this.layout.layout();
32115             }
32116         }
32117     },
32118     
32119     // activate all subpanels if not currently active..
32120     
32121     setActiveState : function(active){
32122         this.active = active;
32123         if(!active){
32124             this.fireEvent("deactivate", this);
32125             return;
32126         }
32127         
32128         this.fireEvent("activate", this);
32129         // not sure if this should happen before or after..
32130         if (!this.layout) {
32131             return; // should not happen..
32132         }
32133         var reg = false;
32134         for (var r in this.layout.regions) {
32135             reg = this.layout.getRegion(r);
32136             if (reg.getActivePanel()) {
32137                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32138                 reg.setActivePanel(reg.getActivePanel());
32139                 continue;
32140             }
32141             if (!reg.panels.length) {
32142                 continue;
32143             }
32144             reg.showPanel(reg.getPanel(0));
32145         }
32146         
32147         
32148         
32149         
32150     },
32151     
32152     /**
32153      * Returns the nested BorderLayout for this panel
32154      * @return {Roo.BorderLayout} 
32155      */
32156     getLayout : function(){
32157         return this.layout;
32158     },
32159     
32160      /**
32161      * Adds a xtype elements to the layout of the nested panel
32162      * <pre><code>
32163
32164 panel.addxtype({
32165        xtype : 'ContentPanel',
32166        region: 'west',
32167        items: [ .... ]
32168    }
32169 );
32170
32171 panel.addxtype({
32172         xtype : 'NestedLayoutPanel',
32173         region: 'west',
32174         layout: {
32175            center: { },
32176            west: { }   
32177         },
32178         items : [ ... list of content panels or nested layout panels.. ]
32179    }
32180 );
32181 </code></pre>
32182      * @param {Object} cfg Xtype definition of item to add.
32183      */
32184     addxtype : function(cfg) {
32185         return this.layout.addxtype(cfg);
32186     
32187     }
32188 });
32189
32190 Roo.ScrollPanel = function(el, config, content){
32191     config = config || {};
32192     config.fitToFrame = true;
32193     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32194     
32195     this.el.dom.style.overflow = "hidden";
32196     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32197     this.el.removeClass("x-layout-inactive-content");
32198     this.el.on("mousewheel", this.onWheel, this);
32199
32200     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32201     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32202     up.unselectable(); down.unselectable();
32203     up.on("click", this.scrollUp, this);
32204     down.on("click", this.scrollDown, this);
32205     up.addClassOnOver("x-scroller-btn-over");
32206     down.addClassOnOver("x-scroller-btn-over");
32207     up.addClassOnClick("x-scroller-btn-click");
32208     down.addClassOnClick("x-scroller-btn-click");
32209     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32210
32211     this.resizeEl = this.el;
32212     this.el = wrap; this.up = up; this.down = down;
32213 };
32214
32215 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32216     increment : 100,
32217     wheelIncrement : 5,
32218     scrollUp : function(){
32219         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32220     },
32221
32222     scrollDown : function(){
32223         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32224     },
32225
32226     afterScroll : function(){
32227         var el = this.resizeEl;
32228         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32229         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32230         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32231     },
32232
32233     setSize : function(){
32234         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32235         this.afterScroll();
32236     },
32237
32238     onWheel : function(e){
32239         var d = e.getWheelDelta();
32240         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32241         this.afterScroll();
32242         e.stopEvent();
32243     },
32244
32245     setContent : function(content, loadScripts){
32246         this.resizeEl.update(content, loadScripts);
32247     }
32248
32249 });
32250
32251
32252
32253
32254
32255
32256
32257
32258
32259 /**
32260  * @class Roo.TreePanel
32261  * @extends Roo.ContentPanel
32262  * @constructor
32263  * Create a new TreePanel. - defaults to fit/scoll contents.
32264  * @param {String/Object} config A string to set only the panel's title, or a config object
32265  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32266  */
32267 Roo.TreePanel = function(config){
32268     var el = config.el;
32269     var tree = config.tree;
32270     delete config.tree; 
32271     delete config.el; // hopefull!
32272     
32273     // wrapper for IE7 strict & safari scroll issue
32274     
32275     var treeEl = el.createChild();
32276     config.resizeEl = treeEl;
32277     
32278     
32279     
32280     Roo.TreePanel.superclass.constructor.call(this, el, config);
32281  
32282  
32283     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32284     //console.log(tree);
32285     this.on('activate', function()
32286     {
32287         if (this.tree.rendered) {
32288             return;
32289         }
32290         //console.log('render tree');
32291         this.tree.render();
32292     });
32293     // this should not be needed.. - it's actually the 'el' that resizes?
32294     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32295     
32296     //this.on('resize',  function (cp, w, h) {
32297     //        this.tree.innerCt.setWidth(w);
32298     //        this.tree.innerCt.setHeight(h);
32299     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32300     //});
32301
32302         
32303     
32304 };
32305
32306 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32307     fitToFrame : true,
32308     autoScroll : true
32309 });
32310
32311
32312
32313
32314
32315
32316
32317
32318
32319
32320
32321 /*
32322  * Based on:
32323  * Ext JS Library 1.1.1
32324  * Copyright(c) 2006-2007, Ext JS, LLC.
32325  *
32326  * Originally Released Under LGPL - original licence link has changed is not relivant.
32327  *
32328  * Fork - LGPL
32329  * <script type="text/javascript">
32330  */
32331  
32332
32333 /**
32334  * @class Roo.ReaderLayout
32335  * @extends Roo.BorderLayout
32336  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32337  * center region containing two nested regions (a top one for a list view and one for item preview below),
32338  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32339  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32340  * expedites the setup of the overall layout and regions for this common application style.
32341  * Example:
32342  <pre><code>
32343 var reader = new Roo.ReaderLayout();
32344 var CP = Roo.ContentPanel;  // shortcut for adding
32345
32346 reader.beginUpdate();
32347 reader.add("north", new CP("north", "North"));
32348 reader.add("west", new CP("west", {title: "West"}));
32349 reader.add("east", new CP("east", {title: "East"}));
32350
32351 reader.regions.listView.add(new CP("listView", "List"));
32352 reader.regions.preview.add(new CP("preview", "Preview"));
32353 reader.endUpdate();
32354 </code></pre>
32355 * @constructor
32356 * Create a new ReaderLayout
32357 * @param {Object} config Configuration options
32358 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32359 * document.body if omitted)
32360 */
32361 Roo.ReaderLayout = function(config, renderTo){
32362     var c = config || {size:{}};
32363     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32364         north: c.north !== false ? Roo.apply({
32365             split:false,
32366             initialSize: 32,
32367             titlebar: false
32368         }, c.north) : false,
32369         west: c.west !== false ? Roo.apply({
32370             split:true,
32371             initialSize: 200,
32372             minSize: 175,
32373             maxSize: 400,
32374             titlebar: true,
32375             collapsible: true,
32376             animate: true,
32377             margins:{left:5,right:0,bottom:5,top:5},
32378             cmargins:{left:5,right:5,bottom:5,top:5}
32379         }, c.west) : false,
32380         east: c.east !== false ? Roo.apply({
32381             split:true,
32382             initialSize: 200,
32383             minSize: 175,
32384             maxSize: 400,
32385             titlebar: true,
32386             collapsible: true,
32387             animate: true,
32388             margins:{left:0,right:5,bottom:5,top:5},
32389             cmargins:{left:5,right:5,bottom:5,top:5}
32390         }, c.east) : false,
32391         center: Roo.apply({
32392             tabPosition: 'top',
32393             autoScroll:false,
32394             closeOnTab: true,
32395             titlebar:false,
32396             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32397         }, c.center)
32398     });
32399
32400     this.el.addClass('x-reader');
32401
32402     this.beginUpdate();
32403
32404     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32405         south: c.preview !== false ? Roo.apply({
32406             split:true,
32407             initialSize: 200,
32408             minSize: 100,
32409             autoScroll:true,
32410             collapsible:true,
32411             titlebar: true,
32412             cmargins:{top:5,left:0, right:0, bottom:0}
32413         }, c.preview) : false,
32414         center: Roo.apply({
32415             autoScroll:false,
32416             titlebar:false,
32417             minHeight:200
32418         }, c.listView)
32419     });
32420     this.add('center', new Roo.NestedLayoutPanel(inner,
32421             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32422
32423     this.endUpdate();
32424
32425     this.regions.preview = inner.getRegion('south');
32426     this.regions.listView = inner.getRegion('center');
32427 };
32428
32429 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32430  * Based on:
32431  * Ext JS Library 1.1.1
32432  * Copyright(c) 2006-2007, Ext JS, LLC.
32433  *
32434  * Originally Released Under LGPL - original licence link has changed is not relivant.
32435  *
32436  * Fork - LGPL
32437  * <script type="text/javascript">
32438  */
32439  
32440 /**
32441  * @class Roo.grid.Grid
32442  * @extends Roo.util.Observable
32443  * This class represents the primary interface of a component based grid control.
32444  * <br><br>Usage:<pre><code>
32445  var grid = new Roo.grid.Grid("my-container-id", {
32446      ds: myDataStore,
32447      cm: myColModel,
32448      selModel: mySelectionModel,
32449      autoSizeColumns: true,
32450      monitorWindowResize: false,
32451      trackMouseOver: true
32452  });
32453  // set any options
32454  grid.render();
32455  * </code></pre>
32456  * <b>Common Problems:</b><br/>
32457  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32458  * element will correct this<br/>
32459  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32460  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32461  * are unpredictable.<br/>
32462  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32463  * grid to calculate dimensions/offsets.<br/>
32464   * @constructor
32465  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32466  * The container MUST have some type of size defined for the grid to fill. The container will be
32467  * automatically set to position relative if it isn't already.
32468  * @param {Object} config A config object that sets properties on this grid.
32469  */
32470 Roo.grid.Grid = function(container, config){
32471         // initialize the container
32472         this.container = Roo.get(container);
32473         this.container.update("");
32474         this.container.setStyle("overflow", "hidden");
32475     this.container.addClass('x-grid-container');
32476
32477     this.id = this.container.id;
32478
32479     Roo.apply(this, config);
32480     // check and correct shorthanded configs
32481     if(this.ds){
32482         this.dataSource = this.ds;
32483         delete this.ds;
32484     }
32485     if(this.cm){
32486         this.colModel = this.cm;
32487         delete this.cm;
32488     }
32489     if(this.sm){
32490         this.selModel = this.sm;
32491         delete this.sm;
32492     }
32493
32494     if (this.selModel) {
32495         this.selModel = Roo.factory(this.selModel, Roo.grid);
32496         this.sm = this.selModel;
32497         this.sm.xmodule = this.xmodule || false;
32498     }
32499     if (typeof(this.colModel.config) == 'undefined') {
32500         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32501         this.cm = this.colModel;
32502         this.cm.xmodule = this.xmodule || false;
32503     }
32504     if (this.dataSource) {
32505         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32506         this.ds = this.dataSource;
32507         this.ds.xmodule = this.xmodule || false;
32508          
32509     }
32510     
32511     
32512     
32513     if(this.width){
32514         this.container.setWidth(this.width);
32515     }
32516
32517     if(this.height){
32518         this.container.setHeight(this.height);
32519     }
32520     /** @private */
32521         this.addEvents({
32522         // raw events
32523         /**
32524          * @event click
32525          * The raw click event for the entire grid.
32526          * @param {Roo.EventObject} e
32527          */
32528         "click" : true,
32529         /**
32530          * @event dblclick
32531          * The raw dblclick event for the entire grid.
32532          * @param {Roo.EventObject} e
32533          */
32534         "dblclick" : true,
32535         /**
32536          * @event contextmenu
32537          * The raw contextmenu event for the entire grid.
32538          * @param {Roo.EventObject} e
32539          */
32540         "contextmenu" : true,
32541         /**
32542          * @event mousedown
32543          * The raw mousedown event for the entire grid.
32544          * @param {Roo.EventObject} e
32545          */
32546         "mousedown" : true,
32547         /**
32548          * @event mouseup
32549          * The raw mouseup event for the entire grid.
32550          * @param {Roo.EventObject} e
32551          */
32552         "mouseup" : true,
32553         /**
32554          * @event mouseover
32555          * The raw mouseover event for the entire grid.
32556          * @param {Roo.EventObject} e
32557          */
32558         "mouseover" : true,
32559         /**
32560          * @event mouseout
32561          * The raw mouseout event for the entire grid.
32562          * @param {Roo.EventObject} e
32563          */
32564         "mouseout" : true,
32565         /**
32566          * @event keypress
32567          * The raw keypress event for the entire grid.
32568          * @param {Roo.EventObject} e
32569          */
32570         "keypress" : true,
32571         /**
32572          * @event keydown
32573          * The raw keydown event for the entire grid.
32574          * @param {Roo.EventObject} e
32575          */
32576         "keydown" : true,
32577
32578         // custom events
32579
32580         /**
32581          * @event cellclick
32582          * Fires when a cell is clicked
32583          * @param {Grid} this
32584          * @param {Number} rowIndex
32585          * @param {Number} columnIndex
32586          * @param {Roo.EventObject} e
32587          */
32588         "cellclick" : true,
32589         /**
32590          * @event celldblclick
32591          * Fires when a cell is double clicked
32592          * @param {Grid} this
32593          * @param {Number} rowIndex
32594          * @param {Number} columnIndex
32595          * @param {Roo.EventObject} e
32596          */
32597         "celldblclick" : true,
32598         /**
32599          * @event rowclick
32600          * Fires when a row is clicked
32601          * @param {Grid} this
32602          * @param {Number} rowIndex
32603          * @param {Roo.EventObject} e
32604          */
32605         "rowclick" : true,
32606         /**
32607          * @event rowdblclick
32608          * Fires when a row is double clicked
32609          * @param {Grid} this
32610          * @param {Number} rowIndex
32611          * @param {Roo.EventObject} e
32612          */
32613         "rowdblclick" : true,
32614         /**
32615          * @event headerclick
32616          * Fires when a header is clicked
32617          * @param {Grid} this
32618          * @param {Number} columnIndex
32619          * @param {Roo.EventObject} e
32620          */
32621         "headerclick" : true,
32622         /**
32623          * @event headerdblclick
32624          * Fires when a header cell is double clicked
32625          * @param {Grid} this
32626          * @param {Number} columnIndex
32627          * @param {Roo.EventObject} e
32628          */
32629         "headerdblclick" : true,
32630         /**
32631          * @event rowcontextmenu
32632          * Fires when a row is right clicked
32633          * @param {Grid} this
32634          * @param {Number} rowIndex
32635          * @param {Roo.EventObject} e
32636          */
32637         "rowcontextmenu" : true,
32638         /**
32639          * @event cellcontextmenu
32640          * Fires when a cell is right clicked
32641          * @param {Grid} this
32642          * @param {Number} rowIndex
32643          * @param {Number} cellIndex
32644          * @param {Roo.EventObject} e
32645          */
32646          "cellcontextmenu" : true,
32647         /**
32648          * @event headercontextmenu
32649          * Fires when a header is right clicked
32650          * @param {Grid} this
32651          * @param {Number} columnIndex
32652          * @param {Roo.EventObject} e
32653          */
32654         "headercontextmenu" : true,
32655         /**
32656          * @event bodyscroll
32657          * Fires when the body element is scrolled
32658          * @param {Number} scrollLeft
32659          * @param {Number} scrollTop
32660          */
32661         "bodyscroll" : true,
32662         /**
32663          * @event columnresize
32664          * Fires when the user resizes a column
32665          * @param {Number} columnIndex
32666          * @param {Number} newSize
32667          */
32668         "columnresize" : true,
32669         /**
32670          * @event columnmove
32671          * Fires when the user moves a column
32672          * @param {Number} oldIndex
32673          * @param {Number} newIndex
32674          */
32675         "columnmove" : true,
32676         /**
32677          * @event startdrag
32678          * Fires when row(s) start being dragged
32679          * @param {Grid} this
32680          * @param {Roo.GridDD} dd The drag drop object
32681          * @param {event} e The raw browser event
32682          */
32683         "startdrag" : true,
32684         /**
32685          * @event enddrag
32686          * Fires when a drag operation is complete
32687          * @param {Grid} this
32688          * @param {Roo.GridDD} dd The drag drop object
32689          * @param {event} e The raw browser event
32690          */
32691         "enddrag" : true,
32692         /**
32693          * @event dragdrop
32694          * Fires when dragged row(s) are dropped on a valid DD target
32695          * @param {Grid} this
32696          * @param {Roo.GridDD} dd The drag drop object
32697          * @param {String} targetId The target drag drop object
32698          * @param {event} e The raw browser event
32699          */
32700         "dragdrop" : true,
32701         /**
32702          * @event dragover
32703          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32704          * @param {Grid} this
32705          * @param {Roo.GridDD} dd The drag drop object
32706          * @param {String} targetId The target drag drop object
32707          * @param {event} e The raw browser event
32708          */
32709         "dragover" : true,
32710         /**
32711          * @event dragenter
32712          *  Fires when the dragged row(s) first cross another DD target while being dragged
32713          * @param {Grid} this
32714          * @param {Roo.GridDD} dd The drag drop object
32715          * @param {String} targetId The target drag drop object
32716          * @param {event} e The raw browser event
32717          */
32718         "dragenter" : true,
32719         /**
32720          * @event dragout
32721          * Fires when the dragged row(s) leave another DD target while being dragged
32722          * @param {Grid} this
32723          * @param {Roo.GridDD} dd The drag drop object
32724          * @param {String} targetId The target drag drop object
32725          * @param {event} e The raw browser event
32726          */
32727         "dragout" : true,
32728         /**
32729          * @event rowclass
32730          * Fires when a row is rendered, so you can change add a style to it.
32731          * @param {GridView} gridview   The grid view
32732          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32733          */
32734         'rowclass' : true,
32735
32736         /**
32737          * @event render
32738          * Fires when the grid is rendered
32739          * @param {Grid} grid
32740          */
32741         'render' : true
32742     });
32743
32744     Roo.grid.Grid.superclass.constructor.call(this);
32745 };
32746 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32747     
32748     /**
32749      * @cfg {String} ddGroup - drag drop group.
32750      */
32751
32752     /**
32753      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32754      */
32755     minColumnWidth : 25,
32756
32757     /**
32758      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32759      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32760      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32761      */
32762     autoSizeColumns : false,
32763
32764     /**
32765      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32766      */
32767     autoSizeHeaders : true,
32768
32769     /**
32770      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32771      */
32772     monitorWindowResize : true,
32773
32774     /**
32775      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32776      * rows measured to get a columns size. Default is 0 (all rows).
32777      */
32778     maxRowsToMeasure : 0,
32779
32780     /**
32781      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32782      */
32783     trackMouseOver : true,
32784
32785     /**
32786     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32787     */
32788     
32789     /**
32790     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32791     */
32792     enableDragDrop : false,
32793     
32794     /**
32795     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32796     */
32797     enableColumnMove : true,
32798     
32799     /**
32800     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32801     */
32802     enableColumnHide : true,
32803     
32804     /**
32805     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32806     */
32807     enableRowHeightSync : false,
32808     
32809     /**
32810     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32811     */
32812     stripeRows : true,
32813     
32814     /**
32815     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32816     */
32817     autoHeight : false,
32818
32819     /**
32820      * @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.
32821      */
32822     autoExpandColumn : false,
32823
32824     /**
32825     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32826     * Default is 50.
32827     */
32828     autoExpandMin : 50,
32829
32830     /**
32831     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32832     */
32833     autoExpandMax : 1000,
32834
32835     /**
32836     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32837     */
32838     view : null,
32839
32840     /**
32841     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32842     */
32843     loadMask : false,
32844     /**
32845     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32846     */
32847     dropTarget: false,
32848     
32849    
32850     
32851     // private
32852     rendered : false,
32853
32854     /**
32855     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32856     * of a fixed width. Default is false.
32857     */
32858     /**
32859     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32860     */
32861     /**
32862      * Called once after all setup has been completed and the grid is ready to be rendered.
32863      * @return {Roo.grid.Grid} this
32864      */
32865     render : function()
32866     {
32867         var c = this.container;
32868         // try to detect autoHeight/width mode
32869         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32870             this.autoHeight = true;
32871         }
32872         var view = this.getView();
32873         view.init(this);
32874
32875         c.on("click", this.onClick, this);
32876         c.on("dblclick", this.onDblClick, this);
32877         c.on("contextmenu", this.onContextMenu, this);
32878         c.on("keydown", this.onKeyDown, this);
32879         if (Roo.isTouch) {
32880             c.on("touchstart", this.onTouchStart, this);
32881         }
32882
32883         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32884
32885         this.getSelectionModel().init(this);
32886
32887         view.render();
32888
32889         if(this.loadMask){
32890             this.loadMask = new Roo.LoadMask(this.container,
32891                     Roo.apply({store:this.dataSource}, this.loadMask));
32892         }
32893         
32894         
32895         if (this.toolbar && this.toolbar.xtype) {
32896             this.toolbar.container = this.getView().getHeaderPanel(true);
32897             this.toolbar = new Roo.Toolbar(this.toolbar);
32898         }
32899         if (this.footer && this.footer.xtype) {
32900             this.footer.dataSource = this.getDataSource();
32901             this.footer.container = this.getView().getFooterPanel(true);
32902             this.footer = Roo.factory(this.footer, Roo);
32903         }
32904         if (this.dropTarget && this.dropTarget.xtype) {
32905             delete this.dropTarget.xtype;
32906             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32907         }
32908         
32909         
32910         this.rendered = true;
32911         this.fireEvent('render', this);
32912         return this;
32913     },
32914
32915         /**
32916          * Reconfigures the grid to use a different Store and Column Model.
32917          * The View will be bound to the new objects and refreshed.
32918          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32919          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32920          */
32921     reconfigure : function(dataSource, colModel){
32922         if(this.loadMask){
32923             this.loadMask.destroy();
32924             this.loadMask = new Roo.LoadMask(this.container,
32925                     Roo.apply({store:dataSource}, this.loadMask));
32926         }
32927         this.view.bind(dataSource, colModel);
32928         this.dataSource = dataSource;
32929         this.colModel = colModel;
32930         this.view.refresh(true);
32931     },
32932
32933     // private
32934     onKeyDown : function(e){
32935         this.fireEvent("keydown", e);
32936     },
32937
32938     /**
32939      * Destroy this grid.
32940      * @param {Boolean} removeEl True to remove the element
32941      */
32942     destroy : function(removeEl, keepListeners){
32943         if(this.loadMask){
32944             this.loadMask.destroy();
32945         }
32946         var c = this.container;
32947         c.removeAllListeners();
32948         this.view.destroy();
32949         this.colModel.purgeListeners();
32950         if(!keepListeners){
32951             this.purgeListeners();
32952         }
32953         c.update("");
32954         if(removeEl === true){
32955             c.remove();
32956         }
32957     },
32958
32959     // private
32960     processEvent : function(name, e){
32961         // does this fire select???
32962         //Roo.log('grid:processEvent '  + name);
32963         
32964         if (name != 'touchstart' ) {
32965             this.fireEvent(name, e);    
32966         }
32967         
32968         var t = e.getTarget();
32969         var v = this.view;
32970         var header = v.findHeaderIndex(t);
32971         if(header !== false){
32972             var ename = name == 'touchstart' ? 'click' : name;
32973              
32974             this.fireEvent("header" + ename, this, header, e);
32975         }else{
32976             var row = v.findRowIndex(t);
32977             var cell = v.findCellIndex(t);
32978             if (name == 'touchstart') {
32979                 // first touch is always a click.
32980                 // hopefull this happens after selection is updated.?
32981                 name = false;
32982                 
32983                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32984                     var cs = this.selModel.getSelectedCell();
32985                     if (row == cs[0] && cell == cs[1]){
32986                         name = 'dblclick';
32987                     }
32988                 }
32989                 if (typeof(this.selModel.getSelections) != 'undefined') {
32990                     var cs = this.selModel.getSelections();
32991                     var ds = this.dataSource;
32992                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32993                         name = 'dblclick';
32994                     }
32995                 }
32996                 if (!name) {
32997                     return;
32998                 }
32999             }
33000             
33001             
33002             if(row !== false){
33003                 this.fireEvent("row" + name, this, row, e);
33004                 if(cell !== false){
33005                     this.fireEvent("cell" + name, this, row, cell, e);
33006                 }
33007             }
33008         }
33009     },
33010
33011     // private
33012     onClick : function(e){
33013         this.processEvent("click", e);
33014     },
33015    // private
33016     onTouchStart : function(e){
33017         this.processEvent("touchstart", e);
33018     },
33019
33020     // private
33021     onContextMenu : function(e, t){
33022         this.processEvent("contextmenu", e);
33023     },
33024
33025     // private
33026     onDblClick : function(e){
33027         this.processEvent("dblclick", e);
33028     },
33029
33030     // private
33031     walkCells : function(row, col, step, fn, scope){
33032         var cm = this.colModel, clen = cm.getColumnCount();
33033         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33034         if(step < 0){
33035             if(col < 0){
33036                 row--;
33037                 first = false;
33038             }
33039             while(row >= 0){
33040                 if(!first){
33041                     col = clen-1;
33042                 }
33043                 first = false;
33044                 while(col >= 0){
33045                     if(fn.call(scope || this, row, col, cm) === true){
33046                         return [row, col];
33047                     }
33048                     col--;
33049                 }
33050                 row--;
33051             }
33052         } else {
33053             if(col >= clen){
33054                 row++;
33055                 first = false;
33056             }
33057             while(row < rlen){
33058                 if(!first){
33059                     col = 0;
33060                 }
33061                 first = false;
33062                 while(col < clen){
33063                     if(fn.call(scope || this, row, col, cm) === true){
33064                         return [row, col];
33065                     }
33066                     col++;
33067                 }
33068                 row++;
33069             }
33070         }
33071         return null;
33072     },
33073
33074     // private
33075     getSelections : function(){
33076         return this.selModel.getSelections();
33077     },
33078
33079     /**
33080      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33081      * but if manual update is required this method will initiate it.
33082      */
33083     autoSize : function(){
33084         if(this.rendered){
33085             this.view.layout();
33086             if(this.view.adjustForScroll){
33087                 this.view.adjustForScroll();
33088             }
33089         }
33090     },
33091
33092     /**
33093      * Returns the grid's underlying element.
33094      * @return {Element} The element
33095      */
33096     getGridEl : function(){
33097         return this.container;
33098     },
33099
33100     // private for compatibility, overridden by editor grid
33101     stopEditing : function(){},
33102
33103     /**
33104      * Returns the grid's SelectionModel.
33105      * @return {SelectionModel}
33106      */
33107     getSelectionModel : function(){
33108         if(!this.selModel){
33109             this.selModel = new Roo.grid.RowSelectionModel();
33110         }
33111         return this.selModel;
33112     },
33113
33114     /**
33115      * Returns the grid's DataSource.
33116      * @return {DataSource}
33117      */
33118     getDataSource : function(){
33119         return this.dataSource;
33120     },
33121
33122     /**
33123      * Returns the grid's ColumnModel.
33124      * @return {ColumnModel}
33125      */
33126     getColumnModel : function(){
33127         return this.colModel;
33128     },
33129
33130     /**
33131      * Returns the grid's GridView object.
33132      * @return {GridView}
33133      */
33134     getView : function(){
33135         if(!this.view){
33136             this.view = new Roo.grid.GridView(this.viewConfig);
33137         }
33138         return this.view;
33139     },
33140     /**
33141      * Called to get grid's drag proxy text, by default returns this.ddText.
33142      * @return {String}
33143      */
33144     getDragDropText : function(){
33145         var count = this.selModel.getCount();
33146         return String.format(this.ddText, count, count == 1 ? '' : 's');
33147     }
33148 });
33149 /**
33150  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33151  * %0 is replaced with the number of selected rows.
33152  * @type String
33153  */
33154 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33155  * Based on:
33156  * Ext JS Library 1.1.1
33157  * Copyright(c) 2006-2007, Ext JS, LLC.
33158  *
33159  * Originally Released Under LGPL - original licence link has changed is not relivant.
33160  *
33161  * Fork - LGPL
33162  * <script type="text/javascript">
33163  */
33164  
33165 Roo.grid.AbstractGridView = function(){
33166         this.grid = null;
33167         
33168         this.events = {
33169             "beforerowremoved" : true,
33170             "beforerowsinserted" : true,
33171             "beforerefresh" : true,
33172             "rowremoved" : true,
33173             "rowsinserted" : true,
33174             "rowupdated" : true,
33175             "refresh" : true
33176         };
33177     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33178 };
33179
33180 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33181     rowClass : "x-grid-row",
33182     cellClass : "x-grid-cell",
33183     tdClass : "x-grid-td",
33184     hdClass : "x-grid-hd",
33185     splitClass : "x-grid-hd-split",
33186     
33187     init: function(grid){
33188         this.grid = grid;
33189                 var cid = this.grid.getGridEl().id;
33190         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33191         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33192         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33193         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33194         },
33195         
33196     getColumnRenderers : function(){
33197         var renderers = [];
33198         var cm = this.grid.colModel;
33199         var colCount = cm.getColumnCount();
33200         for(var i = 0; i < colCount; i++){
33201             renderers[i] = cm.getRenderer(i);
33202         }
33203         return renderers;
33204     },
33205     
33206     getColumnIds : function(){
33207         var ids = [];
33208         var cm = this.grid.colModel;
33209         var colCount = cm.getColumnCount();
33210         for(var i = 0; i < colCount; i++){
33211             ids[i] = cm.getColumnId(i);
33212         }
33213         return ids;
33214     },
33215     
33216     getDataIndexes : function(){
33217         if(!this.indexMap){
33218             this.indexMap = this.buildIndexMap();
33219         }
33220         return this.indexMap.colToData;
33221     },
33222     
33223     getColumnIndexByDataIndex : function(dataIndex){
33224         if(!this.indexMap){
33225             this.indexMap = this.buildIndexMap();
33226         }
33227         return this.indexMap.dataToCol[dataIndex];
33228     },
33229     
33230     /**
33231      * Set a css style for a column dynamically. 
33232      * @param {Number} colIndex The index of the column
33233      * @param {String} name The css property name
33234      * @param {String} value The css value
33235      */
33236     setCSSStyle : function(colIndex, name, value){
33237         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33238         Roo.util.CSS.updateRule(selector, name, value);
33239     },
33240     
33241     generateRules : function(cm){
33242         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33243         Roo.util.CSS.removeStyleSheet(rulesId);
33244         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33245             var cid = cm.getColumnId(i);
33246             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33247                          this.tdSelector, cid, " {\n}\n",
33248                          this.hdSelector, cid, " {\n}\n",
33249                          this.splitSelector, cid, " {\n}\n");
33250         }
33251         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33252     }
33253 });/*
33254  * Based on:
33255  * Ext JS Library 1.1.1
33256  * Copyright(c) 2006-2007, Ext JS, LLC.
33257  *
33258  * Originally Released Under LGPL - original licence link has changed is not relivant.
33259  *
33260  * Fork - LGPL
33261  * <script type="text/javascript">
33262  */
33263
33264 // private
33265 // This is a support class used internally by the Grid components
33266 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33267     this.grid = grid;
33268     this.view = grid.getView();
33269     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33270     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33271     if(hd2){
33272         this.setHandleElId(Roo.id(hd));
33273         this.setOuterHandleElId(Roo.id(hd2));
33274     }
33275     this.scroll = false;
33276 };
33277 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33278     maxDragWidth: 120,
33279     getDragData : function(e){
33280         var t = Roo.lib.Event.getTarget(e);
33281         var h = this.view.findHeaderCell(t);
33282         if(h){
33283             return {ddel: h.firstChild, header:h};
33284         }
33285         return false;
33286     },
33287
33288     onInitDrag : function(e){
33289         this.view.headersDisabled = true;
33290         var clone = this.dragData.ddel.cloneNode(true);
33291         clone.id = Roo.id();
33292         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33293         this.proxy.update(clone);
33294         return true;
33295     },
33296
33297     afterValidDrop : function(){
33298         var v = this.view;
33299         setTimeout(function(){
33300             v.headersDisabled = false;
33301         }, 50);
33302     },
33303
33304     afterInvalidDrop : function(){
33305         var v = this.view;
33306         setTimeout(function(){
33307             v.headersDisabled = false;
33308         }, 50);
33309     }
33310 });
33311 /*
33312  * Based on:
33313  * Ext JS Library 1.1.1
33314  * Copyright(c) 2006-2007, Ext JS, LLC.
33315  *
33316  * Originally Released Under LGPL - original licence link has changed is not relivant.
33317  *
33318  * Fork - LGPL
33319  * <script type="text/javascript">
33320  */
33321 // private
33322 // This is a support class used internally by the Grid components
33323 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33324     this.grid = grid;
33325     this.view = grid.getView();
33326     // split the proxies so they don't interfere with mouse events
33327     this.proxyTop = Roo.DomHelper.append(document.body, {
33328         cls:"col-move-top", html:"&#160;"
33329     }, true);
33330     this.proxyBottom = Roo.DomHelper.append(document.body, {
33331         cls:"col-move-bottom", html:"&#160;"
33332     }, true);
33333     this.proxyTop.hide = this.proxyBottom.hide = function(){
33334         this.setLeftTop(-100,-100);
33335         this.setStyle("visibility", "hidden");
33336     };
33337     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33338     // temporarily disabled
33339     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33340     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33341 };
33342 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33343     proxyOffsets : [-4, -9],
33344     fly: Roo.Element.fly,
33345
33346     getTargetFromEvent : function(e){
33347         var t = Roo.lib.Event.getTarget(e);
33348         var cindex = this.view.findCellIndex(t);
33349         if(cindex !== false){
33350             return this.view.getHeaderCell(cindex);
33351         }
33352         return null;
33353     },
33354
33355     nextVisible : function(h){
33356         var v = this.view, cm = this.grid.colModel;
33357         h = h.nextSibling;
33358         while(h){
33359             if(!cm.isHidden(v.getCellIndex(h))){
33360                 return h;
33361             }
33362             h = h.nextSibling;
33363         }
33364         return null;
33365     },
33366
33367     prevVisible : function(h){
33368         var v = this.view, cm = this.grid.colModel;
33369         h = h.prevSibling;
33370         while(h){
33371             if(!cm.isHidden(v.getCellIndex(h))){
33372                 return h;
33373             }
33374             h = h.prevSibling;
33375         }
33376         return null;
33377     },
33378
33379     positionIndicator : function(h, n, e){
33380         var x = Roo.lib.Event.getPageX(e);
33381         var r = Roo.lib.Dom.getRegion(n.firstChild);
33382         var px, pt, py = r.top + this.proxyOffsets[1];
33383         if((r.right - x) <= (r.right-r.left)/2){
33384             px = r.right+this.view.borderWidth;
33385             pt = "after";
33386         }else{
33387             px = r.left;
33388             pt = "before";
33389         }
33390         var oldIndex = this.view.getCellIndex(h);
33391         var newIndex = this.view.getCellIndex(n);
33392
33393         if(this.grid.colModel.isFixed(newIndex)){
33394             return false;
33395         }
33396
33397         var locked = this.grid.colModel.isLocked(newIndex);
33398
33399         if(pt == "after"){
33400             newIndex++;
33401         }
33402         if(oldIndex < newIndex){
33403             newIndex--;
33404         }
33405         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33406             return false;
33407         }
33408         px +=  this.proxyOffsets[0];
33409         this.proxyTop.setLeftTop(px, py);
33410         this.proxyTop.show();
33411         if(!this.bottomOffset){
33412             this.bottomOffset = this.view.mainHd.getHeight();
33413         }
33414         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33415         this.proxyBottom.show();
33416         return pt;
33417     },
33418
33419     onNodeEnter : function(n, dd, e, data){
33420         if(data.header != n){
33421             this.positionIndicator(data.header, n, e);
33422         }
33423     },
33424
33425     onNodeOver : function(n, dd, e, data){
33426         var result = false;
33427         if(data.header != n){
33428             result = this.positionIndicator(data.header, n, e);
33429         }
33430         if(!result){
33431             this.proxyTop.hide();
33432             this.proxyBottom.hide();
33433         }
33434         return result ? this.dropAllowed : this.dropNotAllowed;
33435     },
33436
33437     onNodeOut : function(n, dd, e, data){
33438         this.proxyTop.hide();
33439         this.proxyBottom.hide();
33440     },
33441
33442     onNodeDrop : function(n, dd, e, data){
33443         var h = data.header;
33444         if(h != n){
33445             var cm = this.grid.colModel;
33446             var x = Roo.lib.Event.getPageX(e);
33447             var r = Roo.lib.Dom.getRegion(n.firstChild);
33448             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33449             var oldIndex = this.view.getCellIndex(h);
33450             var newIndex = this.view.getCellIndex(n);
33451             var locked = cm.isLocked(newIndex);
33452             if(pt == "after"){
33453                 newIndex++;
33454             }
33455             if(oldIndex < newIndex){
33456                 newIndex--;
33457             }
33458             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33459                 return false;
33460             }
33461             cm.setLocked(oldIndex, locked, true);
33462             cm.moveColumn(oldIndex, newIndex);
33463             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33464             return true;
33465         }
33466         return false;
33467     }
33468 });
33469 /*
33470  * Based on:
33471  * Ext JS Library 1.1.1
33472  * Copyright(c) 2006-2007, Ext JS, LLC.
33473  *
33474  * Originally Released Under LGPL - original licence link has changed is not relivant.
33475  *
33476  * Fork - LGPL
33477  * <script type="text/javascript">
33478  */
33479   
33480 /**
33481  * @class Roo.grid.GridView
33482  * @extends Roo.util.Observable
33483  *
33484  * @constructor
33485  * @param {Object} config
33486  */
33487 Roo.grid.GridView = function(config){
33488     Roo.grid.GridView.superclass.constructor.call(this);
33489     this.el = null;
33490
33491     Roo.apply(this, config);
33492 };
33493
33494 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33495
33496     unselectable :  'unselectable="on"',
33497     unselectableCls :  'x-unselectable',
33498     
33499     
33500     rowClass : "x-grid-row",
33501
33502     cellClass : "x-grid-col",
33503
33504     tdClass : "x-grid-td",
33505
33506     hdClass : "x-grid-hd",
33507
33508     splitClass : "x-grid-split",
33509
33510     sortClasses : ["sort-asc", "sort-desc"],
33511
33512     enableMoveAnim : false,
33513
33514     hlColor: "C3DAF9",
33515
33516     dh : Roo.DomHelper,
33517
33518     fly : Roo.Element.fly,
33519
33520     css : Roo.util.CSS,
33521
33522     borderWidth: 1,
33523
33524     splitOffset: 3,
33525
33526     scrollIncrement : 22,
33527
33528     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33529
33530     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33531
33532     bind : function(ds, cm){
33533         if(this.ds){
33534             this.ds.un("load", this.onLoad, this);
33535             this.ds.un("datachanged", this.onDataChange, this);
33536             this.ds.un("add", this.onAdd, this);
33537             this.ds.un("remove", this.onRemove, this);
33538             this.ds.un("update", this.onUpdate, this);
33539             this.ds.un("clear", this.onClear, this);
33540         }
33541         if(ds){
33542             ds.on("load", this.onLoad, this);
33543             ds.on("datachanged", this.onDataChange, this);
33544             ds.on("add", this.onAdd, this);
33545             ds.on("remove", this.onRemove, this);
33546             ds.on("update", this.onUpdate, this);
33547             ds.on("clear", this.onClear, this);
33548         }
33549         this.ds = ds;
33550
33551         if(this.cm){
33552             this.cm.un("widthchange", this.onColWidthChange, this);
33553             this.cm.un("headerchange", this.onHeaderChange, this);
33554             this.cm.un("hiddenchange", this.onHiddenChange, this);
33555             this.cm.un("columnmoved", this.onColumnMove, this);
33556             this.cm.un("columnlockchange", this.onColumnLock, this);
33557         }
33558         if(cm){
33559             this.generateRules(cm);
33560             cm.on("widthchange", this.onColWidthChange, this);
33561             cm.on("headerchange", this.onHeaderChange, this);
33562             cm.on("hiddenchange", this.onHiddenChange, this);
33563             cm.on("columnmoved", this.onColumnMove, this);
33564             cm.on("columnlockchange", this.onColumnLock, this);
33565         }
33566         this.cm = cm;
33567     },
33568
33569     init: function(grid){
33570         Roo.grid.GridView.superclass.init.call(this, grid);
33571
33572         this.bind(grid.dataSource, grid.colModel);
33573
33574         grid.on("headerclick", this.handleHeaderClick, this);
33575
33576         if(grid.trackMouseOver){
33577             grid.on("mouseover", this.onRowOver, this);
33578             grid.on("mouseout", this.onRowOut, this);
33579         }
33580         grid.cancelTextSelection = function(){};
33581         this.gridId = grid.id;
33582
33583         var tpls = this.templates || {};
33584
33585         if(!tpls.master){
33586             tpls.master = new Roo.Template(
33587                '<div class="x-grid" hidefocus="true">',
33588                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33589                   '<div class="x-grid-topbar"></div>',
33590                   '<div class="x-grid-scroller"><div></div></div>',
33591                   '<div class="x-grid-locked">',
33592                       '<div class="x-grid-header">{lockedHeader}</div>',
33593                       '<div class="x-grid-body">{lockedBody}</div>',
33594                   "</div>",
33595                   '<div class="x-grid-viewport">',
33596                       '<div class="x-grid-header">{header}</div>',
33597                       '<div class="x-grid-body">{body}</div>',
33598                   "</div>",
33599                   '<div class="x-grid-bottombar"></div>',
33600                  
33601                   '<div class="x-grid-resize-proxy">&#160;</div>',
33602                "</div>"
33603             );
33604             tpls.master.disableformats = true;
33605         }
33606
33607         if(!tpls.header){
33608             tpls.header = new Roo.Template(
33609                '<table border="0" cellspacing="0" cellpadding="0">',
33610                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33611                "</table>{splits}"
33612             );
33613             tpls.header.disableformats = true;
33614         }
33615         tpls.header.compile();
33616
33617         if(!tpls.hcell){
33618             tpls.hcell = new Roo.Template(
33619                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33620                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33621                 "</div></td>"
33622              );
33623              tpls.hcell.disableFormats = true;
33624         }
33625         tpls.hcell.compile();
33626
33627         if(!tpls.hsplit){
33628             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33629                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33630             tpls.hsplit.disableFormats = true;
33631         }
33632         tpls.hsplit.compile();
33633
33634         if(!tpls.body){
33635             tpls.body = new Roo.Template(
33636                '<table border="0" cellspacing="0" cellpadding="0">',
33637                "<tbody>{rows}</tbody>",
33638                "</table>"
33639             );
33640             tpls.body.disableFormats = true;
33641         }
33642         tpls.body.compile();
33643
33644         if(!tpls.row){
33645             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33646             tpls.row.disableFormats = true;
33647         }
33648         tpls.row.compile();
33649
33650         if(!tpls.cell){
33651             tpls.cell = new Roo.Template(
33652                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33653                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33654                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33655                 "</td>"
33656             );
33657             tpls.cell.disableFormats = true;
33658         }
33659         tpls.cell.compile();
33660
33661         this.templates = tpls;
33662     },
33663
33664     // remap these for backwards compat
33665     onColWidthChange : function(){
33666         this.updateColumns.apply(this, arguments);
33667     },
33668     onHeaderChange : function(){
33669         this.updateHeaders.apply(this, arguments);
33670     }, 
33671     onHiddenChange : function(){
33672         this.handleHiddenChange.apply(this, arguments);
33673     },
33674     onColumnMove : function(){
33675         this.handleColumnMove.apply(this, arguments);
33676     },
33677     onColumnLock : function(){
33678         this.handleLockChange.apply(this, arguments);
33679     },
33680
33681     onDataChange : function(){
33682         this.refresh();
33683         this.updateHeaderSortState();
33684     },
33685
33686     onClear : function(){
33687         this.refresh();
33688     },
33689
33690     onUpdate : function(ds, record){
33691         this.refreshRow(record);
33692     },
33693
33694     refreshRow : function(record){
33695         var ds = this.ds, index;
33696         if(typeof record == 'number'){
33697             index = record;
33698             record = ds.getAt(index);
33699         }else{
33700             index = ds.indexOf(record);
33701         }
33702         this.insertRows(ds, index, index, true);
33703         this.onRemove(ds, record, index+1, true);
33704         this.syncRowHeights(index, index);
33705         this.layout();
33706         this.fireEvent("rowupdated", this, index, record);
33707     },
33708
33709     onAdd : function(ds, records, index){
33710         this.insertRows(ds, index, index + (records.length-1));
33711     },
33712
33713     onRemove : function(ds, record, index, isUpdate){
33714         if(isUpdate !== true){
33715             this.fireEvent("beforerowremoved", this, index, record);
33716         }
33717         var bt = this.getBodyTable(), lt = this.getLockedTable();
33718         if(bt.rows[index]){
33719             bt.firstChild.removeChild(bt.rows[index]);
33720         }
33721         if(lt.rows[index]){
33722             lt.firstChild.removeChild(lt.rows[index]);
33723         }
33724         if(isUpdate !== true){
33725             this.stripeRows(index);
33726             this.syncRowHeights(index, index);
33727             this.layout();
33728             this.fireEvent("rowremoved", this, index, record);
33729         }
33730     },
33731
33732     onLoad : function(){
33733         this.scrollToTop();
33734     },
33735
33736     /**
33737      * Scrolls the grid to the top
33738      */
33739     scrollToTop : function(){
33740         if(this.scroller){
33741             this.scroller.dom.scrollTop = 0;
33742             this.syncScroll();
33743         }
33744     },
33745
33746     /**
33747      * Gets a panel in the header of the grid that can be used for toolbars etc.
33748      * After modifying the contents of this panel a call to grid.autoSize() may be
33749      * required to register any changes in size.
33750      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33751      * @return Roo.Element
33752      */
33753     getHeaderPanel : function(doShow){
33754         if(doShow){
33755             this.headerPanel.show();
33756         }
33757         return this.headerPanel;
33758     },
33759
33760     /**
33761      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33762      * After modifying the contents of this panel a call to grid.autoSize() may be
33763      * required to register any changes in size.
33764      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33765      * @return Roo.Element
33766      */
33767     getFooterPanel : function(doShow){
33768         if(doShow){
33769             this.footerPanel.show();
33770         }
33771         return this.footerPanel;
33772     },
33773
33774     initElements : function(){
33775         var E = Roo.Element;
33776         var el = this.grid.getGridEl().dom.firstChild;
33777         var cs = el.childNodes;
33778
33779         this.el = new E(el);
33780         
33781          this.focusEl = new E(el.firstChild);
33782         this.focusEl.swallowEvent("click", true);
33783         
33784         this.headerPanel = new E(cs[1]);
33785         this.headerPanel.enableDisplayMode("block");
33786
33787         this.scroller = new E(cs[2]);
33788         this.scrollSizer = new E(this.scroller.dom.firstChild);
33789
33790         this.lockedWrap = new E(cs[3]);
33791         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33792         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33793
33794         this.mainWrap = new E(cs[4]);
33795         this.mainHd = new E(this.mainWrap.dom.firstChild);
33796         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33797
33798         this.footerPanel = new E(cs[5]);
33799         this.footerPanel.enableDisplayMode("block");
33800
33801         this.resizeProxy = new E(cs[6]);
33802
33803         this.headerSelector = String.format(
33804            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33805            this.lockedHd.id, this.mainHd.id
33806         );
33807
33808         this.splitterSelector = String.format(
33809            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33810            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33811         );
33812     },
33813     idToCssName : function(s)
33814     {
33815         return s.replace(/[^a-z0-9]+/ig, '-');
33816     },
33817
33818     getHeaderCell : function(index){
33819         return Roo.DomQuery.select(this.headerSelector)[index];
33820     },
33821
33822     getHeaderCellMeasure : function(index){
33823         return this.getHeaderCell(index).firstChild;
33824     },
33825
33826     getHeaderCellText : function(index){
33827         return this.getHeaderCell(index).firstChild.firstChild;
33828     },
33829
33830     getLockedTable : function(){
33831         return this.lockedBody.dom.firstChild;
33832     },
33833
33834     getBodyTable : function(){
33835         return this.mainBody.dom.firstChild;
33836     },
33837
33838     getLockedRow : function(index){
33839         return this.getLockedTable().rows[index];
33840     },
33841
33842     getRow : function(index){
33843         return this.getBodyTable().rows[index];
33844     },
33845
33846     getRowComposite : function(index){
33847         if(!this.rowEl){
33848             this.rowEl = new Roo.CompositeElementLite();
33849         }
33850         var els = [], lrow, mrow;
33851         if(lrow = this.getLockedRow(index)){
33852             els.push(lrow);
33853         }
33854         if(mrow = this.getRow(index)){
33855             els.push(mrow);
33856         }
33857         this.rowEl.elements = els;
33858         return this.rowEl;
33859     },
33860     /**
33861      * Gets the 'td' of the cell
33862      * 
33863      * @param {Integer} rowIndex row to select
33864      * @param {Integer} colIndex column to select
33865      * 
33866      * @return {Object} 
33867      */
33868     getCell : function(rowIndex, colIndex){
33869         var locked = this.cm.getLockedCount();
33870         var source;
33871         if(colIndex < locked){
33872             source = this.lockedBody.dom.firstChild;
33873         }else{
33874             source = this.mainBody.dom.firstChild;
33875             colIndex -= locked;
33876         }
33877         return source.rows[rowIndex].childNodes[colIndex];
33878     },
33879
33880     getCellText : function(rowIndex, colIndex){
33881         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33882     },
33883
33884     getCellBox : function(cell){
33885         var b = this.fly(cell).getBox();
33886         if(Roo.isOpera){ // opera fails to report the Y
33887             b.y = cell.offsetTop + this.mainBody.getY();
33888         }
33889         return b;
33890     },
33891
33892     getCellIndex : function(cell){
33893         var id = String(cell.className).match(this.cellRE);
33894         if(id){
33895             return parseInt(id[1], 10);
33896         }
33897         return 0;
33898     },
33899
33900     findHeaderIndex : function(n){
33901         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33902         return r ? this.getCellIndex(r) : false;
33903     },
33904
33905     findHeaderCell : function(n){
33906         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33907         return r ? r : false;
33908     },
33909
33910     findRowIndex : function(n){
33911         if(!n){
33912             return false;
33913         }
33914         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33915         return r ? r.rowIndex : false;
33916     },
33917
33918     findCellIndex : function(node){
33919         var stop = this.el.dom;
33920         while(node && node != stop){
33921             if(this.findRE.test(node.className)){
33922                 return this.getCellIndex(node);
33923             }
33924             node = node.parentNode;
33925         }
33926         return false;
33927     },
33928
33929     getColumnId : function(index){
33930         return this.cm.getColumnId(index);
33931     },
33932
33933     getSplitters : function()
33934     {
33935         if(this.splitterSelector){
33936            return Roo.DomQuery.select(this.splitterSelector);
33937         }else{
33938             return null;
33939       }
33940     },
33941
33942     getSplitter : function(index){
33943         return this.getSplitters()[index];
33944     },
33945
33946     onRowOver : function(e, t){
33947         var row;
33948         if((row = this.findRowIndex(t)) !== false){
33949             this.getRowComposite(row).addClass("x-grid-row-over");
33950         }
33951     },
33952
33953     onRowOut : function(e, t){
33954         var row;
33955         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33956             this.getRowComposite(row).removeClass("x-grid-row-over");
33957         }
33958     },
33959
33960     renderHeaders : function(){
33961         var cm = this.cm;
33962         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33963         var cb = [], lb = [], sb = [], lsb = [], p = {};
33964         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33965             p.cellId = "x-grid-hd-0-" + i;
33966             p.splitId = "x-grid-csplit-0-" + i;
33967             p.id = cm.getColumnId(i);
33968             p.value = cm.getColumnHeader(i) || "";
33969             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33970             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33971             if(!cm.isLocked(i)){
33972                 cb[cb.length] = ct.apply(p);
33973                 sb[sb.length] = st.apply(p);
33974             }else{
33975                 lb[lb.length] = ct.apply(p);
33976                 lsb[lsb.length] = st.apply(p);
33977             }
33978         }
33979         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33980                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33981     },
33982
33983     updateHeaders : function(){
33984         var html = this.renderHeaders();
33985         this.lockedHd.update(html[0]);
33986         this.mainHd.update(html[1]);
33987     },
33988
33989     /**
33990      * Focuses the specified row.
33991      * @param {Number} row The row index
33992      */
33993     focusRow : function(row)
33994     {
33995         //Roo.log('GridView.focusRow');
33996         var x = this.scroller.dom.scrollLeft;
33997         this.focusCell(row, 0, false);
33998         this.scroller.dom.scrollLeft = x;
33999     },
34000
34001     /**
34002      * Focuses the specified cell.
34003      * @param {Number} row The row index
34004      * @param {Number} col The column index
34005      * @param {Boolean} hscroll false to disable horizontal scrolling
34006      */
34007     focusCell : function(row, col, hscroll)
34008     {
34009         //Roo.log('GridView.focusCell');
34010         var el = this.ensureVisible(row, col, hscroll);
34011         this.focusEl.alignTo(el, "tl-tl");
34012         if(Roo.isGecko){
34013             this.focusEl.focus();
34014         }else{
34015             this.focusEl.focus.defer(1, this.focusEl);
34016         }
34017     },
34018
34019     /**
34020      * Scrolls the specified cell into view
34021      * @param {Number} row The row index
34022      * @param {Number} col The column index
34023      * @param {Boolean} hscroll false to disable horizontal scrolling
34024      */
34025     ensureVisible : function(row, col, hscroll)
34026     {
34027         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34028         //return null; //disable for testing.
34029         if(typeof row != "number"){
34030             row = row.rowIndex;
34031         }
34032         if(row < 0 && row >= this.ds.getCount()){
34033             return  null;
34034         }
34035         col = (col !== undefined ? col : 0);
34036         var cm = this.grid.colModel;
34037         while(cm.isHidden(col)){
34038             col++;
34039         }
34040
34041         var el = this.getCell(row, col);
34042         if(!el){
34043             return null;
34044         }
34045         var c = this.scroller.dom;
34046
34047         var ctop = parseInt(el.offsetTop, 10);
34048         var cleft = parseInt(el.offsetLeft, 10);
34049         var cbot = ctop + el.offsetHeight;
34050         var cright = cleft + el.offsetWidth;
34051         
34052         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34053         var stop = parseInt(c.scrollTop, 10);
34054         var sleft = parseInt(c.scrollLeft, 10);
34055         var sbot = stop + ch;
34056         var sright = sleft + c.clientWidth;
34057         /*
34058         Roo.log('GridView.ensureVisible:' +
34059                 ' ctop:' + ctop +
34060                 ' c.clientHeight:' + c.clientHeight +
34061                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34062                 ' stop:' + stop +
34063                 ' cbot:' + cbot +
34064                 ' sbot:' + sbot +
34065                 ' ch:' + ch  
34066                 );
34067         */
34068         if(ctop < stop){
34069              c.scrollTop = ctop;
34070             //Roo.log("set scrolltop to ctop DISABLE?");
34071         }else if(cbot > sbot){
34072             //Roo.log("set scrolltop to cbot-ch");
34073             c.scrollTop = cbot-ch;
34074         }
34075         
34076         if(hscroll !== false){
34077             if(cleft < sleft){
34078                 c.scrollLeft = cleft;
34079             }else if(cright > sright){
34080                 c.scrollLeft = cright-c.clientWidth;
34081             }
34082         }
34083          
34084         return el;
34085     },
34086
34087     updateColumns : function(){
34088         this.grid.stopEditing();
34089         var cm = this.grid.colModel, colIds = this.getColumnIds();
34090         //var totalWidth = cm.getTotalWidth();
34091         var pos = 0;
34092         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34093             //if(cm.isHidden(i)) continue;
34094             var w = cm.getColumnWidth(i);
34095             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34096             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34097         }
34098         this.updateSplitters();
34099     },
34100
34101     generateRules : function(cm){
34102         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34103         Roo.util.CSS.removeStyleSheet(rulesId);
34104         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34105             var cid = cm.getColumnId(i);
34106             var align = '';
34107             if(cm.config[i].align){
34108                 align = 'text-align:'+cm.config[i].align+';';
34109             }
34110             var hidden = '';
34111             if(cm.isHidden(i)){
34112                 hidden = 'display:none;';
34113             }
34114             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34115             ruleBuf.push(
34116                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34117                     this.hdSelector, cid, " {\n", align, width, "}\n",
34118                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34119                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34120         }
34121         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34122     },
34123
34124     updateSplitters : function(){
34125         var cm = this.cm, s = this.getSplitters();
34126         if(s){ // splitters not created yet
34127             var pos = 0, locked = true;
34128             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34129                 if(cm.isHidden(i)) {
34130                     continue;
34131                 }
34132                 var w = cm.getColumnWidth(i); // make sure it's a number
34133                 if(!cm.isLocked(i) && locked){
34134                     pos = 0;
34135                     locked = false;
34136                 }
34137                 pos += w;
34138                 s[i].style.left = (pos-this.splitOffset) + "px";
34139             }
34140         }
34141     },
34142
34143     handleHiddenChange : function(colModel, colIndex, hidden){
34144         if(hidden){
34145             this.hideColumn(colIndex);
34146         }else{
34147             this.unhideColumn(colIndex);
34148         }
34149     },
34150
34151     hideColumn : function(colIndex){
34152         var cid = this.getColumnId(colIndex);
34153         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34154         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34155         if(Roo.isSafari){
34156             this.updateHeaders();
34157         }
34158         this.updateSplitters();
34159         this.layout();
34160     },
34161
34162     unhideColumn : function(colIndex){
34163         var cid = this.getColumnId(colIndex);
34164         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34165         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34166
34167         if(Roo.isSafari){
34168             this.updateHeaders();
34169         }
34170         this.updateSplitters();
34171         this.layout();
34172     },
34173
34174     insertRows : function(dm, firstRow, lastRow, isUpdate){
34175         if(firstRow == 0 && lastRow == dm.getCount()-1){
34176             this.refresh();
34177         }else{
34178             if(!isUpdate){
34179                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34180             }
34181             var s = this.getScrollState();
34182             var markup = this.renderRows(firstRow, lastRow);
34183             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34184             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34185             this.restoreScroll(s);
34186             if(!isUpdate){
34187                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34188                 this.syncRowHeights(firstRow, lastRow);
34189                 this.stripeRows(firstRow);
34190                 this.layout();
34191             }
34192         }
34193     },
34194
34195     bufferRows : function(markup, target, index){
34196         var before = null, trows = target.rows, tbody = target.tBodies[0];
34197         if(index < trows.length){
34198             before = trows[index];
34199         }
34200         var b = document.createElement("div");
34201         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34202         var rows = b.firstChild.rows;
34203         for(var i = 0, len = rows.length; i < len; i++){
34204             if(before){
34205                 tbody.insertBefore(rows[0], before);
34206             }else{
34207                 tbody.appendChild(rows[0]);
34208             }
34209         }
34210         b.innerHTML = "";
34211         b = null;
34212     },
34213
34214     deleteRows : function(dm, firstRow, lastRow){
34215         if(dm.getRowCount()<1){
34216             this.fireEvent("beforerefresh", this);
34217             this.mainBody.update("");
34218             this.lockedBody.update("");
34219             this.fireEvent("refresh", this);
34220         }else{
34221             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34222             var bt = this.getBodyTable();
34223             var tbody = bt.firstChild;
34224             var rows = bt.rows;
34225             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34226                 tbody.removeChild(rows[firstRow]);
34227             }
34228             this.stripeRows(firstRow);
34229             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34230         }
34231     },
34232
34233     updateRows : function(dataSource, firstRow, lastRow){
34234         var s = this.getScrollState();
34235         this.refresh();
34236         this.restoreScroll(s);
34237     },
34238
34239     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34240         if(!noRefresh){
34241            this.refresh();
34242         }
34243         this.updateHeaderSortState();
34244     },
34245
34246     getScrollState : function(){
34247         
34248         var sb = this.scroller.dom;
34249         return {left: sb.scrollLeft, top: sb.scrollTop};
34250     },
34251
34252     stripeRows : function(startRow){
34253         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34254             return;
34255         }
34256         startRow = startRow || 0;
34257         var rows = this.getBodyTable().rows;
34258         var lrows = this.getLockedTable().rows;
34259         var cls = ' x-grid-row-alt ';
34260         for(var i = startRow, len = rows.length; i < len; i++){
34261             var row = rows[i], lrow = lrows[i];
34262             var isAlt = ((i+1) % 2 == 0);
34263             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34264             if(isAlt == hasAlt){
34265                 continue;
34266             }
34267             if(isAlt){
34268                 row.className += " x-grid-row-alt";
34269             }else{
34270                 row.className = row.className.replace("x-grid-row-alt", "");
34271             }
34272             if(lrow){
34273                 lrow.className = row.className;
34274             }
34275         }
34276     },
34277
34278     restoreScroll : function(state){
34279         //Roo.log('GridView.restoreScroll');
34280         var sb = this.scroller.dom;
34281         sb.scrollLeft = state.left;
34282         sb.scrollTop = state.top;
34283         this.syncScroll();
34284     },
34285
34286     syncScroll : function(){
34287         //Roo.log('GridView.syncScroll');
34288         var sb = this.scroller.dom;
34289         var sh = this.mainHd.dom;
34290         var bs = this.mainBody.dom;
34291         var lv = this.lockedBody.dom;
34292         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34293         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34294     },
34295
34296     handleScroll : function(e){
34297         this.syncScroll();
34298         var sb = this.scroller.dom;
34299         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34300         e.stopEvent();
34301     },
34302
34303     handleWheel : function(e){
34304         var d = e.getWheelDelta();
34305         this.scroller.dom.scrollTop -= d*22;
34306         // set this here to prevent jumpy scrolling on large tables
34307         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34308         e.stopEvent();
34309     },
34310
34311     renderRows : function(startRow, endRow){
34312         // pull in all the crap needed to render rows
34313         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34314         var colCount = cm.getColumnCount();
34315
34316         if(ds.getCount() < 1){
34317             return ["", ""];
34318         }
34319
34320         // build a map for all the columns
34321         var cs = [];
34322         for(var i = 0; i < colCount; i++){
34323             var name = cm.getDataIndex(i);
34324             cs[i] = {
34325                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34326                 renderer : cm.getRenderer(i),
34327                 id : cm.getColumnId(i),
34328                 locked : cm.isLocked(i),
34329                 has_editor : cm.isCellEditable(i)
34330             };
34331         }
34332
34333         startRow = startRow || 0;
34334         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34335
34336         // records to render
34337         var rs = ds.getRange(startRow, endRow);
34338
34339         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34340     },
34341
34342     // As much as I hate to duplicate code, this was branched because FireFox really hates
34343     // [].join("") on strings. The performance difference was substantial enough to
34344     // branch this function
34345     doRender : Roo.isGecko ?
34346             function(cs, rs, ds, startRow, colCount, stripe){
34347                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34348                 // buffers
34349                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34350                 
34351                 var hasListener = this.grid.hasListener('rowclass');
34352                 var rowcfg = {};
34353                 for(var j = 0, len = rs.length; j < len; j++){
34354                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34355                     for(var i = 0; i < colCount; i++){
34356                         c = cs[i];
34357                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34358                         p.id = c.id;
34359                         p.css = p.attr = "";
34360                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34361                         if(p.value == undefined || p.value === "") {
34362                             p.value = "&#160;";
34363                         }
34364                         if(c.has_editor){
34365                             p.css += ' x-grid-editable-cell';
34366                         }
34367                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34368                             p.css +=  ' x-grid-dirty-cell';
34369                         }
34370                         var markup = ct.apply(p);
34371                         if(!c.locked){
34372                             cb+= markup;
34373                         }else{
34374                             lcb+= markup;
34375                         }
34376                     }
34377                     var alt = [];
34378                     if(stripe && ((rowIndex+1) % 2 == 0)){
34379                         alt.push("x-grid-row-alt")
34380                     }
34381                     if(r.dirty){
34382                         alt.push(  " x-grid-dirty-row");
34383                     }
34384                     rp.cells = lcb;
34385                     if(this.getRowClass){
34386                         alt.push(this.getRowClass(r, rowIndex));
34387                     }
34388                     if (hasListener) {
34389                         rowcfg = {
34390                              
34391                             record: r,
34392                             rowIndex : rowIndex,
34393                             rowClass : ''
34394                         };
34395                         this.grid.fireEvent('rowclass', this, rowcfg);
34396                         alt.push(rowcfg.rowClass);
34397                     }
34398                     rp.alt = alt.join(" ");
34399                     lbuf+= rt.apply(rp);
34400                     rp.cells = cb;
34401                     buf+=  rt.apply(rp);
34402                 }
34403                 return [lbuf, buf];
34404             } :
34405             function(cs, rs, ds, startRow, colCount, stripe){
34406                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34407                 // buffers
34408                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34409                 var hasListener = this.grid.hasListener('rowclass');
34410  
34411                 var rowcfg = {};
34412                 for(var j = 0, len = rs.length; j < len; j++){
34413                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34414                     for(var i = 0; i < colCount; i++){
34415                         c = cs[i];
34416                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34417                         p.id = c.id;
34418                         p.css = p.attr = "";
34419                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34420                         if(p.value == undefined || p.value === "") {
34421                             p.value = "&#160;";
34422                         }
34423                         //Roo.log(c);
34424                          if(c.has_editor){
34425                             p.css += ' x-grid-editable-cell';
34426                         }
34427                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34428                             p.css += ' x-grid-dirty-cell' 
34429                         }
34430                         
34431                         var markup = ct.apply(p);
34432                         if(!c.locked){
34433                             cb[cb.length] = markup;
34434                         }else{
34435                             lcb[lcb.length] = markup;
34436                         }
34437                     }
34438                     var alt = [];
34439                     if(stripe && ((rowIndex+1) % 2 == 0)){
34440                         alt.push( "x-grid-row-alt");
34441                     }
34442                     if(r.dirty){
34443                         alt.push(" x-grid-dirty-row");
34444                     }
34445                     rp.cells = lcb;
34446                     if(this.getRowClass){
34447                         alt.push( this.getRowClass(r, rowIndex));
34448                     }
34449                     if (hasListener) {
34450                         rowcfg = {
34451                              
34452                             record: r,
34453                             rowIndex : rowIndex,
34454                             rowClass : ''
34455                         };
34456                         this.grid.fireEvent('rowclass', this, rowcfg);
34457                         alt.push(rowcfg.rowClass);
34458                     }
34459                     
34460                     rp.alt = alt.join(" ");
34461                     rp.cells = lcb.join("");
34462                     lbuf[lbuf.length] = rt.apply(rp);
34463                     rp.cells = cb.join("");
34464                     buf[buf.length] =  rt.apply(rp);
34465                 }
34466                 return [lbuf.join(""), buf.join("")];
34467             },
34468
34469     renderBody : function(){
34470         var markup = this.renderRows();
34471         var bt = this.templates.body;
34472         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34473     },
34474
34475     /**
34476      * Refreshes the grid
34477      * @param {Boolean} headersToo
34478      */
34479     refresh : function(headersToo){
34480         this.fireEvent("beforerefresh", this);
34481         this.grid.stopEditing();
34482         var result = this.renderBody();
34483         this.lockedBody.update(result[0]);
34484         this.mainBody.update(result[1]);
34485         if(headersToo === true){
34486             this.updateHeaders();
34487             this.updateColumns();
34488             this.updateSplitters();
34489             this.updateHeaderSortState();
34490         }
34491         this.syncRowHeights();
34492         this.layout();
34493         this.fireEvent("refresh", this);
34494     },
34495
34496     handleColumnMove : function(cm, oldIndex, newIndex){
34497         this.indexMap = null;
34498         var s = this.getScrollState();
34499         this.refresh(true);
34500         this.restoreScroll(s);
34501         this.afterMove(newIndex);
34502     },
34503
34504     afterMove : function(colIndex){
34505         if(this.enableMoveAnim && Roo.enableFx){
34506             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34507         }
34508         // if multisort - fix sortOrder, and reload..
34509         if (this.grid.dataSource.multiSort) {
34510             // the we can call sort again..
34511             var dm = this.grid.dataSource;
34512             var cm = this.grid.colModel;
34513             var so = [];
34514             for(var i = 0; i < cm.config.length; i++ ) {
34515                 
34516                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34517                     continue; // dont' bother, it's not in sort list or being set.
34518                 }
34519                 
34520                 so.push(cm.config[i].dataIndex);
34521             };
34522             dm.sortOrder = so;
34523             dm.load(dm.lastOptions);
34524             
34525             
34526         }
34527         
34528     },
34529
34530     updateCell : function(dm, rowIndex, dataIndex){
34531         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34532         if(typeof colIndex == "undefined"){ // not present in grid
34533             return;
34534         }
34535         var cm = this.grid.colModel;
34536         var cell = this.getCell(rowIndex, colIndex);
34537         var cellText = this.getCellText(rowIndex, colIndex);
34538
34539         var p = {
34540             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34541             id : cm.getColumnId(colIndex),
34542             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34543         };
34544         var renderer = cm.getRenderer(colIndex);
34545         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34546         if(typeof val == "undefined" || val === "") {
34547             val = "&#160;";
34548         }
34549         cellText.innerHTML = val;
34550         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34551         this.syncRowHeights(rowIndex, rowIndex);
34552     },
34553
34554     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34555         var maxWidth = 0;
34556         if(this.grid.autoSizeHeaders){
34557             var h = this.getHeaderCellMeasure(colIndex);
34558             maxWidth = Math.max(maxWidth, h.scrollWidth);
34559         }
34560         var tb, index;
34561         if(this.cm.isLocked(colIndex)){
34562             tb = this.getLockedTable();
34563             index = colIndex;
34564         }else{
34565             tb = this.getBodyTable();
34566             index = colIndex - this.cm.getLockedCount();
34567         }
34568         if(tb && tb.rows){
34569             var rows = tb.rows;
34570             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34571             for(var i = 0; i < stopIndex; i++){
34572                 var cell = rows[i].childNodes[index].firstChild;
34573                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34574             }
34575         }
34576         return maxWidth + /*margin for error in IE*/ 5;
34577     },
34578     /**
34579      * Autofit a column to its content.
34580      * @param {Number} colIndex
34581      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34582      */
34583      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34584          if(this.cm.isHidden(colIndex)){
34585              return; // can't calc a hidden column
34586          }
34587         if(forceMinSize){
34588             var cid = this.cm.getColumnId(colIndex);
34589             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34590            if(this.grid.autoSizeHeaders){
34591                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34592            }
34593         }
34594         var newWidth = this.calcColumnWidth(colIndex);
34595         this.cm.setColumnWidth(colIndex,
34596             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34597         if(!suppressEvent){
34598             this.grid.fireEvent("columnresize", colIndex, newWidth);
34599         }
34600     },
34601
34602     /**
34603      * Autofits all columns to their content and then expands to fit any extra space in the grid
34604      */
34605      autoSizeColumns : function(){
34606         var cm = this.grid.colModel;
34607         var colCount = cm.getColumnCount();
34608         for(var i = 0; i < colCount; i++){
34609             this.autoSizeColumn(i, true, true);
34610         }
34611         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34612             this.fitColumns();
34613         }else{
34614             this.updateColumns();
34615             this.layout();
34616         }
34617     },
34618
34619     /**
34620      * Autofits all columns to the grid's width proportionate with their current size
34621      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34622      */
34623     fitColumns : function(reserveScrollSpace){
34624         var cm = this.grid.colModel;
34625         var colCount = cm.getColumnCount();
34626         var cols = [];
34627         var width = 0;
34628         var i, w;
34629         for (i = 0; i < colCount; i++){
34630             if(!cm.isHidden(i) && !cm.isFixed(i)){
34631                 w = cm.getColumnWidth(i);
34632                 cols.push(i);
34633                 cols.push(w);
34634                 width += w;
34635             }
34636         }
34637         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34638         if(reserveScrollSpace){
34639             avail -= 17;
34640         }
34641         var frac = (avail - cm.getTotalWidth())/width;
34642         while (cols.length){
34643             w = cols.pop();
34644             i = cols.pop();
34645             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34646         }
34647         this.updateColumns();
34648         this.layout();
34649     },
34650
34651     onRowSelect : function(rowIndex){
34652         var row = this.getRowComposite(rowIndex);
34653         row.addClass("x-grid-row-selected");
34654     },
34655
34656     onRowDeselect : function(rowIndex){
34657         var row = this.getRowComposite(rowIndex);
34658         row.removeClass("x-grid-row-selected");
34659     },
34660
34661     onCellSelect : function(row, col){
34662         var cell = this.getCell(row, col);
34663         if(cell){
34664             Roo.fly(cell).addClass("x-grid-cell-selected");
34665         }
34666     },
34667
34668     onCellDeselect : function(row, col){
34669         var cell = this.getCell(row, col);
34670         if(cell){
34671             Roo.fly(cell).removeClass("x-grid-cell-selected");
34672         }
34673     },
34674
34675     updateHeaderSortState : function(){
34676         
34677         // sort state can be single { field: xxx, direction : yyy}
34678         // or   { xxx=>ASC , yyy : DESC ..... }
34679         
34680         var mstate = {};
34681         if (!this.ds.multiSort) { 
34682             var state = this.ds.getSortState();
34683             if(!state){
34684                 return;
34685             }
34686             mstate[state.field] = state.direction;
34687             // FIXME... - this is not used here.. but might be elsewhere..
34688             this.sortState = state;
34689             
34690         } else {
34691             mstate = this.ds.sortToggle;
34692         }
34693         //remove existing sort classes..
34694         
34695         var sc = this.sortClasses;
34696         var hds = this.el.select(this.headerSelector).removeClass(sc);
34697         
34698         for(var f in mstate) {
34699         
34700             var sortColumn = this.cm.findColumnIndex(f);
34701             
34702             if(sortColumn != -1){
34703                 var sortDir = mstate[f];        
34704                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34705             }
34706         }
34707         
34708          
34709         
34710     },
34711
34712
34713     handleHeaderClick : function(g, index,e){
34714         
34715         Roo.log("header click");
34716         
34717         if (Roo.isTouch) {
34718             // touch events on header are handled by context
34719             this.handleHdCtx(g,index,e);
34720             return;
34721         }
34722         
34723         
34724         if(this.headersDisabled){
34725             return;
34726         }
34727         var dm = g.dataSource, cm = g.colModel;
34728         if(!cm.isSortable(index)){
34729             return;
34730         }
34731         g.stopEditing();
34732         
34733         if (dm.multiSort) {
34734             // update the sortOrder
34735             var so = [];
34736             for(var i = 0; i < cm.config.length; i++ ) {
34737                 
34738                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34739                     continue; // dont' bother, it's not in sort list or being set.
34740                 }
34741                 
34742                 so.push(cm.config[i].dataIndex);
34743             };
34744             dm.sortOrder = so;
34745         }
34746         
34747         
34748         dm.sort(cm.getDataIndex(index));
34749     },
34750
34751
34752     destroy : function(){
34753         if(this.colMenu){
34754             this.colMenu.removeAll();
34755             Roo.menu.MenuMgr.unregister(this.colMenu);
34756             this.colMenu.getEl().remove();
34757             delete this.colMenu;
34758         }
34759         if(this.hmenu){
34760             this.hmenu.removeAll();
34761             Roo.menu.MenuMgr.unregister(this.hmenu);
34762             this.hmenu.getEl().remove();
34763             delete this.hmenu;
34764         }
34765         if(this.grid.enableColumnMove){
34766             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34767             if(dds){
34768                 for(var dd in dds){
34769                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34770                         var elid = dds[dd].dragElId;
34771                         dds[dd].unreg();
34772                         Roo.get(elid).remove();
34773                     } else if(dds[dd].config.isTarget){
34774                         dds[dd].proxyTop.remove();
34775                         dds[dd].proxyBottom.remove();
34776                         dds[dd].unreg();
34777                     }
34778                     if(Roo.dd.DDM.locationCache[dd]){
34779                         delete Roo.dd.DDM.locationCache[dd];
34780                     }
34781                 }
34782                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34783             }
34784         }
34785         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34786         this.bind(null, null);
34787         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34788     },
34789
34790     handleLockChange : function(){
34791         this.refresh(true);
34792     },
34793
34794     onDenyColumnLock : function(){
34795
34796     },
34797
34798     onDenyColumnHide : function(){
34799
34800     },
34801
34802     handleHdMenuClick : function(item){
34803         var index = this.hdCtxIndex;
34804         var cm = this.cm, ds = this.ds;
34805         switch(item.id){
34806             case "asc":
34807                 ds.sort(cm.getDataIndex(index), "ASC");
34808                 break;
34809             case "desc":
34810                 ds.sort(cm.getDataIndex(index), "DESC");
34811                 break;
34812             case "lock":
34813                 var lc = cm.getLockedCount();
34814                 if(cm.getColumnCount(true) <= lc+1){
34815                     this.onDenyColumnLock();
34816                     return;
34817                 }
34818                 if(lc != index){
34819                     cm.setLocked(index, true, true);
34820                     cm.moveColumn(index, lc);
34821                     this.grid.fireEvent("columnmove", index, lc);
34822                 }else{
34823                     cm.setLocked(index, true);
34824                 }
34825             break;
34826             case "unlock":
34827                 var lc = cm.getLockedCount();
34828                 if((lc-1) != index){
34829                     cm.setLocked(index, false, true);
34830                     cm.moveColumn(index, lc-1);
34831                     this.grid.fireEvent("columnmove", index, lc-1);
34832                 }else{
34833                     cm.setLocked(index, false);
34834                 }
34835             break;
34836             case 'wider': // used to expand cols on touch..
34837             case 'narrow':
34838                 var cw = cm.getColumnWidth(index);
34839                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34840                 cw = Math.max(0, cw);
34841                 cw = Math.min(cw,4000);
34842                 cm.setColumnWidth(index, cw);
34843                 break;
34844                 
34845             default:
34846                 index = cm.getIndexById(item.id.substr(4));
34847                 if(index != -1){
34848                     if(item.checked && cm.getColumnCount(true) <= 1){
34849                         this.onDenyColumnHide();
34850                         return false;
34851                     }
34852                     cm.setHidden(index, item.checked);
34853                 }
34854         }
34855         return true;
34856     },
34857
34858     beforeColMenuShow : function(){
34859         var cm = this.cm,  colCount = cm.getColumnCount();
34860         this.colMenu.removeAll();
34861         for(var i = 0; i < colCount; i++){
34862             this.colMenu.add(new Roo.menu.CheckItem({
34863                 id: "col-"+cm.getColumnId(i),
34864                 text: cm.getColumnHeader(i),
34865                 checked: !cm.isHidden(i),
34866                 hideOnClick:false
34867             }));
34868         }
34869     },
34870
34871     handleHdCtx : function(g, index, e){
34872         e.stopEvent();
34873         var hd = this.getHeaderCell(index);
34874         this.hdCtxIndex = index;
34875         var ms = this.hmenu.items, cm = this.cm;
34876         ms.get("asc").setDisabled(!cm.isSortable(index));
34877         ms.get("desc").setDisabled(!cm.isSortable(index));
34878         if(this.grid.enableColLock !== false){
34879             ms.get("lock").setDisabled(cm.isLocked(index));
34880             ms.get("unlock").setDisabled(!cm.isLocked(index));
34881         }
34882         this.hmenu.show(hd, "tl-bl");
34883     },
34884
34885     handleHdOver : function(e){
34886         var hd = this.findHeaderCell(e.getTarget());
34887         if(hd && !this.headersDisabled){
34888             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34889                this.fly(hd).addClass("x-grid-hd-over");
34890             }
34891         }
34892     },
34893
34894     handleHdOut : function(e){
34895         var hd = this.findHeaderCell(e.getTarget());
34896         if(hd){
34897             this.fly(hd).removeClass("x-grid-hd-over");
34898         }
34899     },
34900
34901     handleSplitDblClick : function(e, t){
34902         var i = this.getCellIndex(t);
34903         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34904             this.autoSizeColumn(i, true);
34905             this.layout();
34906         }
34907     },
34908
34909     render : function(){
34910
34911         var cm = this.cm;
34912         var colCount = cm.getColumnCount();
34913
34914         if(this.grid.monitorWindowResize === true){
34915             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34916         }
34917         var header = this.renderHeaders();
34918         var body = this.templates.body.apply({rows:""});
34919         var html = this.templates.master.apply({
34920             lockedBody: body,
34921             body: body,
34922             lockedHeader: header[0],
34923             header: header[1]
34924         });
34925
34926         //this.updateColumns();
34927
34928         this.grid.getGridEl().dom.innerHTML = html;
34929
34930         this.initElements();
34931         
34932         // a kludge to fix the random scolling effect in webkit
34933         this.el.on("scroll", function() {
34934             this.el.dom.scrollTop=0; // hopefully not recursive..
34935         },this);
34936
34937         this.scroller.on("scroll", this.handleScroll, this);
34938         this.lockedBody.on("mousewheel", this.handleWheel, this);
34939         this.mainBody.on("mousewheel", this.handleWheel, this);
34940
34941         this.mainHd.on("mouseover", this.handleHdOver, this);
34942         this.mainHd.on("mouseout", this.handleHdOut, this);
34943         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34944                 {delegate: "."+this.splitClass});
34945
34946         this.lockedHd.on("mouseover", this.handleHdOver, this);
34947         this.lockedHd.on("mouseout", this.handleHdOut, this);
34948         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34949                 {delegate: "."+this.splitClass});
34950
34951         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34952             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34953         }
34954
34955         this.updateSplitters();
34956
34957         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34958             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34959             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34960         }
34961
34962         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34963             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34964             this.hmenu.add(
34965                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34966                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34967             );
34968             if(this.grid.enableColLock !== false){
34969                 this.hmenu.add('-',
34970                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34971                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34972                 );
34973             }
34974             if (Roo.isTouch) {
34975                  this.hmenu.add('-',
34976                     {id:"wider", text: this.columnsWiderText},
34977                     {id:"narrow", text: this.columnsNarrowText }
34978                 );
34979                 
34980                  
34981             }
34982             
34983             if(this.grid.enableColumnHide !== false){
34984
34985                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34986                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34987                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34988
34989                 this.hmenu.add('-',
34990                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34991                 );
34992             }
34993             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34994
34995             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34996         }
34997
34998         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34999             this.dd = new Roo.grid.GridDragZone(this.grid, {
35000                 ddGroup : this.grid.ddGroup || 'GridDD'
35001             });
35002             
35003         }
35004
35005         /*
35006         for(var i = 0; i < colCount; i++){
35007             if(cm.isHidden(i)){
35008                 this.hideColumn(i);
35009             }
35010             if(cm.config[i].align){
35011                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35012                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35013             }
35014         }*/
35015         
35016         this.updateHeaderSortState();
35017
35018         this.beforeInitialResize();
35019         this.layout(true);
35020
35021         // two part rendering gives faster view to the user
35022         this.renderPhase2.defer(1, this);
35023     },
35024
35025     renderPhase2 : function(){
35026         // render the rows now
35027         this.refresh();
35028         if(this.grid.autoSizeColumns){
35029             this.autoSizeColumns();
35030         }
35031     },
35032
35033     beforeInitialResize : function(){
35034
35035     },
35036
35037     onColumnSplitterMoved : function(i, w){
35038         this.userResized = true;
35039         var cm = this.grid.colModel;
35040         cm.setColumnWidth(i, w, true);
35041         var cid = cm.getColumnId(i);
35042         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35043         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35044         this.updateSplitters();
35045         this.layout();
35046         this.grid.fireEvent("columnresize", i, w);
35047     },
35048
35049     syncRowHeights : function(startIndex, endIndex){
35050         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35051             startIndex = startIndex || 0;
35052             var mrows = this.getBodyTable().rows;
35053             var lrows = this.getLockedTable().rows;
35054             var len = mrows.length-1;
35055             endIndex = Math.min(endIndex || len, len);
35056             for(var i = startIndex; i <= endIndex; i++){
35057                 var m = mrows[i], l = lrows[i];
35058                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35059                 m.style.height = l.style.height = h + "px";
35060             }
35061         }
35062     },
35063
35064     layout : function(initialRender, is2ndPass){
35065         var g = this.grid;
35066         var auto = g.autoHeight;
35067         var scrollOffset = 16;
35068         var c = g.getGridEl(), cm = this.cm,
35069                 expandCol = g.autoExpandColumn,
35070                 gv = this;
35071         //c.beginMeasure();
35072
35073         if(!c.dom.offsetWidth){ // display:none?
35074             if(initialRender){
35075                 this.lockedWrap.show();
35076                 this.mainWrap.show();
35077             }
35078             return;
35079         }
35080
35081         var hasLock = this.cm.isLocked(0);
35082
35083         var tbh = this.headerPanel.getHeight();
35084         var bbh = this.footerPanel.getHeight();
35085
35086         if(auto){
35087             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35088             var newHeight = ch + c.getBorderWidth("tb");
35089             if(g.maxHeight){
35090                 newHeight = Math.min(g.maxHeight, newHeight);
35091             }
35092             c.setHeight(newHeight);
35093         }
35094
35095         if(g.autoWidth){
35096             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35097         }
35098
35099         var s = this.scroller;
35100
35101         var csize = c.getSize(true);
35102
35103         this.el.setSize(csize.width, csize.height);
35104
35105         this.headerPanel.setWidth(csize.width);
35106         this.footerPanel.setWidth(csize.width);
35107
35108         var hdHeight = this.mainHd.getHeight();
35109         var vw = csize.width;
35110         var vh = csize.height - (tbh + bbh);
35111
35112         s.setSize(vw, vh);
35113
35114         var bt = this.getBodyTable();
35115         
35116         if(cm.getLockedCount() == cm.config.length){
35117             bt = this.getLockedTable();
35118         }
35119         
35120         var ltWidth = hasLock ?
35121                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35122
35123         var scrollHeight = bt.offsetHeight;
35124         var scrollWidth = ltWidth + bt.offsetWidth;
35125         var vscroll = false, hscroll = false;
35126
35127         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35128
35129         var lw = this.lockedWrap, mw = this.mainWrap;
35130         var lb = this.lockedBody, mb = this.mainBody;
35131
35132         setTimeout(function(){
35133             var t = s.dom.offsetTop;
35134             var w = s.dom.clientWidth,
35135                 h = s.dom.clientHeight;
35136
35137             lw.setTop(t);
35138             lw.setSize(ltWidth, h);
35139
35140             mw.setLeftTop(ltWidth, t);
35141             mw.setSize(w-ltWidth, h);
35142
35143             lb.setHeight(h-hdHeight);
35144             mb.setHeight(h-hdHeight);
35145
35146             if(is2ndPass !== true && !gv.userResized && expandCol){
35147                 // high speed resize without full column calculation
35148                 
35149                 var ci = cm.getIndexById(expandCol);
35150                 if (ci < 0) {
35151                     ci = cm.findColumnIndex(expandCol);
35152                 }
35153                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35154                 var expandId = cm.getColumnId(ci);
35155                 var  tw = cm.getTotalWidth(false);
35156                 var currentWidth = cm.getColumnWidth(ci);
35157                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35158                 if(currentWidth != cw){
35159                     cm.setColumnWidth(ci, cw, true);
35160                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35161                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35162                     gv.updateSplitters();
35163                     gv.layout(false, true);
35164                 }
35165             }
35166
35167             if(initialRender){
35168                 lw.show();
35169                 mw.show();
35170             }
35171             //c.endMeasure();
35172         }, 10);
35173     },
35174
35175     onWindowResize : function(){
35176         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35177             return;
35178         }
35179         this.layout();
35180     },
35181
35182     appendFooter : function(parentEl){
35183         return null;
35184     },
35185
35186     sortAscText : "Sort Ascending",
35187     sortDescText : "Sort Descending",
35188     lockText : "Lock Column",
35189     unlockText : "Unlock Column",
35190     columnsText : "Columns",
35191  
35192     columnsWiderText : "Wider",
35193     columnsNarrowText : "Thinner"
35194 });
35195
35196
35197 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35198     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35199     this.proxy.el.addClass('x-grid3-col-dd');
35200 };
35201
35202 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35203     handleMouseDown : function(e){
35204
35205     },
35206
35207     callHandleMouseDown : function(e){
35208         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35209     }
35210 });
35211 /*
35212  * Based on:
35213  * Ext JS Library 1.1.1
35214  * Copyright(c) 2006-2007, Ext JS, LLC.
35215  *
35216  * Originally Released Under LGPL - original licence link has changed is not relivant.
35217  *
35218  * Fork - LGPL
35219  * <script type="text/javascript">
35220  */
35221  
35222 // private
35223 // This is a support class used internally by the Grid components
35224 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35225     this.grid = grid;
35226     this.view = grid.getView();
35227     this.proxy = this.view.resizeProxy;
35228     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35229         "gridSplitters" + this.grid.getGridEl().id, {
35230         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35231     });
35232     this.setHandleElId(Roo.id(hd));
35233     this.setOuterHandleElId(Roo.id(hd2));
35234     this.scroll = false;
35235 };
35236 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35237     fly: Roo.Element.fly,
35238
35239     b4StartDrag : function(x, y){
35240         this.view.headersDisabled = true;
35241         this.proxy.setHeight(this.view.mainWrap.getHeight());
35242         var w = this.cm.getColumnWidth(this.cellIndex);
35243         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35244         this.resetConstraints();
35245         this.setXConstraint(minw, 1000);
35246         this.setYConstraint(0, 0);
35247         this.minX = x - minw;
35248         this.maxX = x + 1000;
35249         this.startPos = x;
35250         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35251     },
35252
35253
35254     handleMouseDown : function(e){
35255         ev = Roo.EventObject.setEvent(e);
35256         var t = this.fly(ev.getTarget());
35257         if(t.hasClass("x-grid-split")){
35258             this.cellIndex = this.view.getCellIndex(t.dom);
35259             this.split = t.dom;
35260             this.cm = this.grid.colModel;
35261             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35262                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35263             }
35264         }
35265     },
35266
35267     endDrag : function(e){
35268         this.view.headersDisabled = false;
35269         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35270         var diff = endX - this.startPos;
35271         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35272     },
35273
35274     autoOffset : function(){
35275         this.setDelta(0,0);
35276     }
35277 });/*
35278  * Based on:
35279  * Ext JS Library 1.1.1
35280  * Copyright(c) 2006-2007, Ext JS, LLC.
35281  *
35282  * Originally Released Under LGPL - original licence link has changed is not relivant.
35283  *
35284  * Fork - LGPL
35285  * <script type="text/javascript">
35286  */
35287  
35288 // private
35289 // This is a support class used internally by the Grid components
35290 Roo.grid.GridDragZone = function(grid, config){
35291     this.view = grid.getView();
35292     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35293     if(this.view.lockedBody){
35294         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35295         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35296     }
35297     this.scroll = false;
35298     this.grid = grid;
35299     this.ddel = document.createElement('div');
35300     this.ddel.className = 'x-grid-dd-wrap';
35301 };
35302
35303 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35304     ddGroup : "GridDD",
35305
35306     getDragData : function(e){
35307         var t = Roo.lib.Event.getTarget(e);
35308         var rowIndex = this.view.findRowIndex(t);
35309         var sm = this.grid.selModel;
35310             
35311         //Roo.log(rowIndex);
35312         
35313         if (sm.getSelectedCell) {
35314             // cell selection..
35315             if (!sm.getSelectedCell()) {
35316                 return false;
35317             }
35318             if (rowIndex != sm.getSelectedCell()[0]) {
35319                 return false;
35320             }
35321         
35322         }
35323         
35324         if(rowIndex !== false){
35325             
35326             // if editorgrid.. 
35327             
35328             
35329             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35330                
35331             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35332               //  
35333             //}
35334             if (e.hasModifier()){
35335                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35336             }
35337             
35338             Roo.log("getDragData");
35339             
35340             return {
35341                 grid: this.grid,
35342                 ddel: this.ddel,
35343                 rowIndex: rowIndex,
35344                 selections:sm.getSelections ? sm.getSelections() : (
35345                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35346                 )
35347             };
35348         }
35349         return false;
35350     },
35351
35352     onInitDrag : function(e){
35353         var data = this.dragData;
35354         this.ddel.innerHTML = this.grid.getDragDropText();
35355         this.proxy.update(this.ddel);
35356         // fire start drag?
35357     },
35358
35359     afterRepair : function(){
35360         this.dragging = false;
35361     },
35362
35363     getRepairXY : function(e, data){
35364         return false;
35365     },
35366
35367     onEndDrag : function(data, e){
35368         // fire end drag?
35369     },
35370
35371     onValidDrop : function(dd, e, id){
35372         // fire drag drop?
35373         this.hideProxy();
35374     },
35375
35376     beforeInvalidDrop : function(e, id){
35377
35378     }
35379 });/*
35380  * Based on:
35381  * Ext JS Library 1.1.1
35382  * Copyright(c) 2006-2007, Ext JS, LLC.
35383  *
35384  * Originally Released Under LGPL - original licence link has changed is not relivant.
35385  *
35386  * Fork - LGPL
35387  * <script type="text/javascript">
35388  */
35389  
35390
35391 /**
35392  * @class Roo.grid.ColumnModel
35393  * @extends Roo.util.Observable
35394  * This is the default implementation of a ColumnModel used by the Grid. It defines
35395  * the columns in the grid.
35396  * <br>Usage:<br>
35397  <pre><code>
35398  var colModel = new Roo.grid.ColumnModel([
35399         {header: "Ticker", width: 60, sortable: true, locked: true},
35400         {header: "Company Name", width: 150, sortable: true},
35401         {header: "Market Cap.", width: 100, sortable: true},
35402         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35403         {header: "Employees", width: 100, sortable: true, resizable: false}
35404  ]);
35405  </code></pre>
35406  * <p>
35407  
35408  * The config options listed for this class are options which may appear in each
35409  * individual column definition.
35410  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35411  * @constructor
35412  * @param {Object} config An Array of column config objects. See this class's
35413  * config objects for details.
35414 */
35415 Roo.grid.ColumnModel = function(config){
35416         /**
35417      * The config passed into the constructor
35418      */
35419     this.config = config;
35420     this.lookup = {};
35421
35422     // if no id, create one
35423     // if the column does not have a dataIndex mapping,
35424     // map it to the order it is in the config
35425     for(var i = 0, len = config.length; i < len; i++){
35426         var c = config[i];
35427         if(typeof c.dataIndex == "undefined"){
35428             c.dataIndex = i;
35429         }
35430         if(typeof c.renderer == "string"){
35431             c.renderer = Roo.util.Format[c.renderer];
35432         }
35433         if(typeof c.id == "undefined"){
35434             c.id = Roo.id();
35435         }
35436         if(c.editor && c.editor.xtype){
35437             c.editor  = Roo.factory(c.editor, Roo.grid);
35438         }
35439         if(c.editor && c.editor.isFormField){
35440             c.editor = new Roo.grid.GridEditor(c.editor);
35441         }
35442         this.lookup[c.id] = c;
35443     }
35444
35445     /**
35446      * The width of columns which have no width specified (defaults to 100)
35447      * @type Number
35448      */
35449     this.defaultWidth = 100;
35450
35451     /**
35452      * Default sortable of columns which have no sortable specified (defaults to false)
35453      * @type Boolean
35454      */
35455     this.defaultSortable = false;
35456
35457     this.addEvents({
35458         /**
35459              * @event widthchange
35460              * Fires when the width of a column changes.
35461              * @param {ColumnModel} this
35462              * @param {Number} columnIndex The column index
35463              * @param {Number} newWidth The new width
35464              */
35465             "widthchange": true,
35466         /**
35467              * @event headerchange
35468              * Fires when the text of a header changes.
35469              * @param {ColumnModel} this
35470              * @param {Number} columnIndex The column index
35471              * @param {Number} newText The new header text
35472              */
35473             "headerchange": true,
35474         /**
35475              * @event hiddenchange
35476              * Fires when a column is hidden or "unhidden".
35477              * @param {ColumnModel} this
35478              * @param {Number} columnIndex The column index
35479              * @param {Boolean} hidden true if hidden, false otherwise
35480              */
35481             "hiddenchange": true,
35482             /**
35483          * @event columnmoved
35484          * Fires when a column is moved.
35485          * @param {ColumnModel} this
35486          * @param {Number} oldIndex
35487          * @param {Number} newIndex
35488          */
35489         "columnmoved" : true,
35490         /**
35491          * @event columlockchange
35492          * Fires when a column's locked state is changed
35493          * @param {ColumnModel} this
35494          * @param {Number} colIndex
35495          * @param {Boolean} locked true if locked
35496          */
35497         "columnlockchange" : true
35498     });
35499     Roo.grid.ColumnModel.superclass.constructor.call(this);
35500 };
35501 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35502     /**
35503      * @cfg {String} header The header text to display in the Grid view.
35504      */
35505     /**
35506      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35507      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35508      * specified, the column's index is used as an index into the Record's data Array.
35509      */
35510     /**
35511      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35512      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35513      */
35514     /**
35515      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35516      * Defaults to the value of the {@link #defaultSortable} property.
35517      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35518      */
35519     /**
35520      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35521      */
35522     /**
35523      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35524      */
35525     /**
35526      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35527      */
35528     /**
35529      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35530      */
35531     /**
35532      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35533      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35534      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35535      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35536      */
35537        /**
35538      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35539      */
35540     /**
35541      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35542      */
35543     /**
35544      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35545      */
35546     /**
35547      * @cfg {String} cursor (Optional)
35548      */
35549     /**
35550      * @cfg {String} tooltip (Optional)
35551      */
35552     /**
35553      * @cfg {Number} xs (Optional)
35554      */
35555     /**
35556      * @cfg {Number} sm (Optional)
35557      */
35558     /**
35559      * @cfg {Number} md (Optional)
35560      */
35561     /**
35562      * @cfg {Number} lg (Optional)
35563      */
35564     /**
35565      * Returns the id of the column at the specified index.
35566      * @param {Number} index The column index
35567      * @return {String} the id
35568      */
35569     getColumnId : function(index){
35570         return this.config[index].id;
35571     },
35572
35573     /**
35574      * Returns the column for a specified id.
35575      * @param {String} id The column id
35576      * @return {Object} the column
35577      */
35578     getColumnById : function(id){
35579         return this.lookup[id];
35580     },
35581
35582     
35583     /**
35584      * Returns the column for a specified dataIndex.
35585      * @param {String} dataIndex The column dataIndex
35586      * @return {Object|Boolean} the column or false if not found
35587      */
35588     getColumnByDataIndex: function(dataIndex){
35589         var index = this.findColumnIndex(dataIndex);
35590         return index > -1 ? this.config[index] : false;
35591     },
35592     
35593     /**
35594      * Returns the index for a specified column id.
35595      * @param {String} id The column id
35596      * @return {Number} the index, or -1 if not found
35597      */
35598     getIndexById : function(id){
35599         for(var i = 0, len = this.config.length; i < len; i++){
35600             if(this.config[i].id == id){
35601                 return i;
35602             }
35603         }
35604         return -1;
35605     },
35606     
35607     /**
35608      * Returns the index for a specified column dataIndex.
35609      * @param {String} dataIndex The column dataIndex
35610      * @return {Number} the index, or -1 if not found
35611      */
35612     
35613     findColumnIndex : function(dataIndex){
35614         for(var i = 0, len = this.config.length; i < len; i++){
35615             if(this.config[i].dataIndex == dataIndex){
35616                 return i;
35617             }
35618         }
35619         return -1;
35620     },
35621     
35622     
35623     moveColumn : function(oldIndex, newIndex){
35624         var c = this.config[oldIndex];
35625         this.config.splice(oldIndex, 1);
35626         this.config.splice(newIndex, 0, c);
35627         this.dataMap = null;
35628         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35629     },
35630
35631     isLocked : function(colIndex){
35632         return this.config[colIndex].locked === true;
35633     },
35634
35635     setLocked : function(colIndex, value, suppressEvent){
35636         if(this.isLocked(colIndex) == value){
35637             return;
35638         }
35639         this.config[colIndex].locked = value;
35640         if(!suppressEvent){
35641             this.fireEvent("columnlockchange", this, colIndex, value);
35642         }
35643     },
35644
35645     getTotalLockedWidth : function(){
35646         var totalWidth = 0;
35647         for(var i = 0; i < this.config.length; i++){
35648             if(this.isLocked(i) && !this.isHidden(i)){
35649                 this.totalWidth += this.getColumnWidth(i);
35650             }
35651         }
35652         return totalWidth;
35653     },
35654
35655     getLockedCount : function(){
35656         for(var i = 0, len = this.config.length; i < len; i++){
35657             if(!this.isLocked(i)){
35658                 return i;
35659             }
35660         }
35661         
35662         return this.config.length;
35663     },
35664
35665     /**
35666      * Returns the number of columns.
35667      * @return {Number}
35668      */
35669     getColumnCount : function(visibleOnly){
35670         if(visibleOnly === true){
35671             var c = 0;
35672             for(var i = 0, len = this.config.length; i < len; i++){
35673                 if(!this.isHidden(i)){
35674                     c++;
35675                 }
35676             }
35677             return c;
35678         }
35679         return this.config.length;
35680     },
35681
35682     /**
35683      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35684      * @param {Function} fn
35685      * @param {Object} scope (optional)
35686      * @return {Array} result
35687      */
35688     getColumnsBy : function(fn, scope){
35689         var r = [];
35690         for(var i = 0, len = this.config.length; i < len; i++){
35691             var c = this.config[i];
35692             if(fn.call(scope||this, c, i) === true){
35693                 r[r.length] = c;
35694             }
35695         }
35696         return r;
35697     },
35698
35699     /**
35700      * Returns true if the specified column is sortable.
35701      * @param {Number} col The column index
35702      * @return {Boolean}
35703      */
35704     isSortable : function(col){
35705         if(typeof this.config[col].sortable == "undefined"){
35706             return this.defaultSortable;
35707         }
35708         return this.config[col].sortable;
35709     },
35710
35711     /**
35712      * Returns the rendering (formatting) function defined for the column.
35713      * @param {Number} col The column index.
35714      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35715      */
35716     getRenderer : function(col){
35717         if(!this.config[col].renderer){
35718             return Roo.grid.ColumnModel.defaultRenderer;
35719         }
35720         return this.config[col].renderer;
35721     },
35722
35723     /**
35724      * Sets the rendering (formatting) function for a column.
35725      * @param {Number} col The column index
35726      * @param {Function} fn The function to use to process the cell's raw data
35727      * to return HTML markup for the grid view. The render function is called with
35728      * the following parameters:<ul>
35729      * <li>Data value.</li>
35730      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35731      * <li>css A CSS style string to apply to the table cell.</li>
35732      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35733      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35734      * <li>Row index</li>
35735      * <li>Column index</li>
35736      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35737      */
35738     setRenderer : function(col, fn){
35739         this.config[col].renderer = fn;
35740     },
35741
35742     /**
35743      * Returns the width for the specified column.
35744      * @param {Number} col The column index
35745      * @return {Number}
35746      */
35747     getColumnWidth : function(col){
35748         return this.config[col].width * 1 || this.defaultWidth;
35749     },
35750
35751     /**
35752      * Sets the width for a column.
35753      * @param {Number} col The column index
35754      * @param {Number} width The new width
35755      */
35756     setColumnWidth : function(col, width, suppressEvent){
35757         this.config[col].width = width;
35758         this.totalWidth = null;
35759         if(!suppressEvent){
35760              this.fireEvent("widthchange", this, col, width);
35761         }
35762     },
35763
35764     /**
35765      * Returns the total width of all columns.
35766      * @param {Boolean} includeHidden True to include hidden column widths
35767      * @return {Number}
35768      */
35769     getTotalWidth : function(includeHidden){
35770         if(!this.totalWidth){
35771             this.totalWidth = 0;
35772             for(var i = 0, len = this.config.length; i < len; i++){
35773                 if(includeHidden || !this.isHidden(i)){
35774                     this.totalWidth += this.getColumnWidth(i);
35775                 }
35776             }
35777         }
35778         return this.totalWidth;
35779     },
35780
35781     /**
35782      * Returns the header for the specified column.
35783      * @param {Number} col The column index
35784      * @return {String}
35785      */
35786     getColumnHeader : function(col){
35787         return this.config[col].header;
35788     },
35789
35790     /**
35791      * Sets the header for a column.
35792      * @param {Number} col The column index
35793      * @param {String} header The new header
35794      */
35795     setColumnHeader : function(col, header){
35796         this.config[col].header = header;
35797         this.fireEvent("headerchange", this, col, header);
35798     },
35799
35800     /**
35801      * Returns the tooltip for the specified column.
35802      * @param {Number} col The column index
35803      * @return {String}
35804      */
35805     getColumnTooltip : function(col){
35806             return this.config[col].tooltip;
35807     },
35808     /**
35809      * Sets the tooltip for a column.
35810      * @param {Number} col The column index
35811      * @param {String} tooltip The new tooltip
35812      */
35813     setColumnTooltip : function(col, tooltip){
35814             this.config[col].tooltip = tooltip;
35815     },
35816
35817     /**
35818      * Returns the dataIndex for the specified column.
35819      * @param {Number} col The column index
35820      * @return {Number}
35821      */
35822     getDataIndex : function(col){
35823         return this.config[col].dataIndex;
35824     },
35825
35826     /**
35827      * Sets the dataIndex for a column.
35828      * @param {Number} col The column index
35829      * @param {Number} dataIndex The new dataIndex
35830      */
35831     setDataIndex : function(col, dataIndex){
35832         this.config[col].dataIndex = dataIndex;
35833     },
35834
35835     
35836     
35837     /**
35838      * Returns true if the cell is editable.
35839      * @param {Number} colIndex The column index
35840      * @param {Number} rowIndex The row index - this is nto actually used..?
35841      * @return {Boolean}
35842      */
35843     isCellEditable : function(colIndex, rowIndex){
35844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35845     },
35846
35847     /**
35848      * Returns the editor defined for the cell/column.
35849      * return false or null to disable editing.
35850      * @param {Number} colIndex The column index
35851      * @param {Number} rowIndex The row index
35852      * @return {Object}
35853      */
35854     getCellEditor : function(colIndex, rowIndex){
35855         return this.config[colIndex].editor;
35856     },
35857
35858     /**
35859      * Sets if a column is editable.
35860      * @param {Number} col The column index
35861      * @param {Boolean} editable True if the column is editable
35862      */
35863     setEditable : function(col, editable){
35864         this.config[col].editable = editable;
35865     },
35866
35867
35868     /**
35869      * Returns true if the column is hidden.
35870      * @param {Number} colIndex The column index
35871      * @return {Boolean}
35872      */
35873     isHidden : function(colIndex){
35874         return this.config[colIndex].hidden;
35875     },
35876
35877
35878     /**
35879      * Returns true if the column width cannot be changed
35880      */
35881     isFixed : function(colIndex){
35882         return this.config[colIndex].fixed;
35883     },
35884
35885     /**
35886      * Returns true if the column can be resized
35887      * @return {Boolean}
35888      */
35889     isResizable : function(colIndex){
35890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35891     },
35892     /**
35893      * Sets if a column is hidden.
35894      * @param {Number} colIndex The column index
35895      * @param {Boolean} hidden True if the column is hidden
35896      */
35897     setHidden : function(colIndex, hidden){
35898         this.config[colIndex].hidden = hidden;
35899         this.totalWidth = null;
35900         this.fireEvent("hiddenchange", this, colIndex, hidden);
35901     },
35902
35903     /**
35904      * Sets the editor for a column.
35905      * @param {Number} col The column index
35906      * @param {Object} editor The editor object
35907      */
35908     setEditor : function(col, editor){
35909         this.config[col].editor = editor;
35910     }
35911 });
35912
35913 Roo.grid.ColumnModel.defaultRenderer = function(value)
35914 {
35915     if(typeof value == "object") {
35916         return value;
35917     }
35918         if(typeof value == "string" && value.length < 1){
35919             return "&#160;";
35920         }
35921     
35922         return String.format("{0}", value);
35923 };
35924
35925 // Alias for backwards compatibility
35926 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35927 /*
35928  * Based on:
35929  * Ext JS Library 1.1.1
35930  * Copyright(c) 2006-2007, Ext JS, LLC.
35931  *
35932  * Originally Released Under LGPL - original licence link has changed is not relivant.
35933  *
35934  * Fork - LGPL
35935  * <script type="text/javascript">
35936  */
35937
35938 /**
35939  * @class Roo.grid.AbstractSelectionModel
35940  * @extends Roo.util.Observable
35941  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35942  * implemented by descendant classes.  This class should not be directly instantiated.
35943  * @constructor
35944  */
35945 Roo.grid.AbstractSelectionModel = function(){
35946     this.locked = false;
35947     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35948 };
35949
35950 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35951     /** @ignore Called by the grid automatically. Do not call directly. */
35952     init : function(grid){
35953         this.grid = grid;
35954         this.initEvents();
35955     },
35956
35957     /**
35958      * Locks the selections.
35959      */
35960     lock : function(){
35961         this.locked = true;
35962     },
35963
35964     /**
35965      * Unlocks the selections.
35966      */
35967     unlock : function(){
35968         this.locked = false;
35969     },
35970
35971     /**
35972      * Returns true if the selections are locked.
35973      * @return {Boolean}
35974      */
35975     isLocked : function(){
35976         return this.locked;
35977     }
35978 });/*
35979  * Based on:
35980  * Ext JS Library 1.1.1
35981  * Copyright(c) 2006-2007, Ext JS, LLC.
35982  *
35983  * Originally Released Under LGPL - original licence link has changed is not relivant.
35984  *
35985  * Fork - LGPL
35986  * <script type="text/javascript">
35987  */
35988 /**
35989  * @extends Roo.grid.AbstractSelectionModel
35990  * @class Roo.grid.RowSelectionModel
35991  * The default SelectionModel used by {@link Roo.grid.Grid}.
35992  * It supports multiple selections and keyboard selection/navigation. 
35993  * @constructor
35994  * @param {Object} config
35995  */
35996 Roo.grid.RowSelectionModel = function(config){
35997     Roo.apply(this, config);
35998     this.selections = new Roo.util.MixedCollection(false, function(o){
35999         return o.id;
36000     });
36001
36002     this.last = false;
36003     this.lastActive = false;
36004
36005     this.addEvents({
36006         /**
36007              * @event selectionchange
36008              * Fires when the selection changes
36009              * @param {SelectionModel} this
36010              */
36011             "selectionchange" : true,
36012         /**
36013              * @event afterselectionchange
36014              * Fires after the selection changes (eg. by key press or clicking)
36015              * @param {SelectionModel} this
36016              */
36017             "afterselectionchange" : true,
36018         /**
36019              * @event beforerowselect
36020              * Fires when a row is selected being selected, return false to cancel.
36021              * @param {SelectionModel} this
36022              * @param {Number} rowIndex The selected index
36023              * @param {Boolean} keepExisting False if other selections will be cleared
36024              */
36025             "beforerowselect" : true,
36026         /**
36027              * @event rowselect
36028              * Fires when a row is selected.
36029              * @param {SelectionModel} this
36030              * @param {Number} rowIndex The selected index
36031              * @param {Roo.data.Record} r The record
36032              */
36033             "rowselect" : true,
36034         /**
36035              * @event rowdeselect
36036              * Fires when a row is deselected.
36037              * @param {SelectionModel} this
36038              * @param {Number} rowIndex The selected index
36039              */
36040         "rowdeselect" : true
36041     });
36042     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36043     this.locked = false;
36044 };
36045
36046 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36047     /**
36048      * @cfg {Boolean} singleSelect
36049      * True to allow selection of only one row at a time (defaults to false)
36050      */
36051     singleSelect : false,
36052
36053     // private
36054     initEvents : function(){
36055
36056         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36057             this.grid.on("mousedown", this.handleMouseDown, this);
36058         }else{ // allow click to work like normal
36059             this.grid.on("rowclick", this.handleDragableRowClick, this);
36060         }
36061
36062         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36063             "up" : function(e){
36064                 if(!e.shiftKey){
36065                     this.selectPrevious(e.shiftKey);
36066                 }else if(this.last !== false && this.lastActive !== false){
36067                     var last = this.last;
36068                     this.selectRange(this.last,  this.lastActive-1);
36069                     this.grid.getView().focusRow(this.lastActive);
36070                     if(last !== false){
36071                         this.last = last;
36072                     }
36073                 }else{
36074                     this.selectFirstRow();
36075                 }
36076                 this.fireEvent("afterselectionchange", this);
36077             },
36078             "down" : function(e){
36079                 if(!e.shiftKey){
36080                     this.selectNext(e.shiftKey);
36081                 }else if(this.last !== false && this.lastActive !== false){
36082                     var last = this.last;
36083                     this.selectRange(this.last,  this.lastActive+1);
36084                     this.grid.getView().focusRow(this.lastActive);
36085                     if(last !== false){
36086                         this.last = last;
36087                     }
36088                 }else{
36089                     this.selectFirstRow();
36090                 }
36091                 this.fireEvent("afterselectionchange", this);
36092             },
36093             scope: this
36094         });
36095
36096         var view = this.grid.view;
36097         view.on("refresh", this.onRefresh, this);
36098         view.on("rowupdated", this.onRowUpdated, this);
36099         view.on("rowremoved", this.onRemove, this);
36100     },
36101
36102     // private
36103     onRefresh : function(){
36104         var ds = this.grid.dataSource, i, v = this.grid.view;
36105         var s = this.selections;
36106         s.each(function(r){
36107             if((i = ds.indexOfId(r.id)) != -1){
36108                 v.onRowSelect(i);
36109                 s.add(ds.getAt(i)); // updating the selection relate data
36110             }else{
36111                 s.remove(r);
36112             }
36113         });
36114     },
36115
36116     // private
36117     onRemove : function(v, index, r){
36118         this.selections.remove(r);
36119     },
36120
36121     // private
36122     onRowUpdated : function(v, index, r){
36123         if(this.isSelected(r)){
36124             v.onRowSelect(index);
36125         }
36126     },
36127
36128     /**
36129      * Select records.
36130      * @param {Array} records The records to select
36131      * @param {Boolean} keepExisting (optional) True to keep existing selections
36132      */
36133     selectRecords : function(records, keepExisting){
36134         if(!keepExisting){
36135             this.clearSelections();
36136         }
36137         var ds = this.grid.dataSource;
36138         for(var i = 0, len = records.length; i < len; i++){
36139             this.selectRow(ds.indexOf(records[i]), true);
36140         }
36141     },
36142
36143     /**
36144      * Gets the number of selected rows.
36145      * @return {Number}
36146      */
36147     getCount : function(){
36148         return this.selections.length;
36149     },
36150
36151     /**
36152      * Selects the first row in the grid.
36153      */
36154     selectFirstRow : function(){
36155         this.selectRow(0);
36156     },
36157
36158     /**
36159      * Select the last row.
36160      * @param {Boolean} keepExisting (optional) True to keep existing selections
36161      */
36162     selectLastRow : function(keepExisting){
36163         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36164     },
36165
36166     /**
36167      * Selects the row immediately following the last selected row.
36168      * @param {Boolean} keepExisting (optional) True to keep existing selections
36169      */
36170     selectNext : function(keepExisting){
36171         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36172             this.selectRow(this.last+1, keepExisting);
36173             this.grid.getView().focusRow(this.last);
36174         }
36175     },
36176
36177     /**
36178      * Selects the row that precedes the last selected row.
36179      * @param {Boolean} keepExisting (optional) True to keep existing selections
36180      */
36181     selectPrevious : function(keepExisting){
36182         if(this.last){
36183             this.selectRow(this.last-1, keepExisting);
36184             this.grid.getView().focusRow(this.last);
36185         }
36186     },
36187
36188     /**
36189      * Returns the selected records
36190      * @return {Array} Array of selected records
36191      */
36192     getSelections : function(){
36193         return [].concat(this.selections.items);
36194     },
36195
36196     /**
36197      * Returns the first selected record.
36198      * @return {Record}
36199      */
36200     getSelected : function(){
36201         return this.selections.itemAt(0);
36202     },
36203
36204
36205     /**
36206      * Clears all selections.
36207      */
36208     clearSelections : function(fast){
36209         if(this.locked) {
36210             return;
36211         }
36212         if(fast !== true){
36213             var ds = this.grid.dataSource;
36214             var s = this.selections;
36215             s.each(function(r){
36216                 this.deselectRow(ds.indexOfId(r.id));
36217             }, this);
36218             s.clear();
36219         }else{
36220             this.selections.clear();
36221         }
36222         this.last = false;
36223     },
36224
36225
36226     /**
36227      * Selects all rows.
36228      */
36229     selectAll : function(){
36230         if(this.locked) {
36231             return;
36232         }
36233         this.selections.clear();
36234         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36235             this.selectRow(i, true);
36236         }
36237     },
36238
36239     /**
36240      * Returns True if there is a selection.
36241      * @return {Boolean}
36242      */
36243     hasSelection : function(){
36244         return this.selections.length > 0;
36245     },
36246
36247     /**
36248      * Returns True if the specified row is selected.
36249      * @param {Number/Record} record The record or index of the record to check
36250      * @return {Boolean}
36251      */
36252     isSelected : function(index){
36253         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36254         return (r && this.selections.key(r.id) ? true : false);
36255     },
36256
36257     /**
36258      * Returns True if the specified record id is selected.
36259      * @param {String} id The id of record to check
36260      * @return {Boolean}
36261      */
36262     isIdSelected : function(id){
36263         return (this.selections.key(id) ? true : false);
36264     },
36265
36266     // private
36267     handleMouseDown : function(e, t){
36268         var view = this.grid.getView(), rowIndex;
36269         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36270             return;
36271         };
36272         if(e.shiftKey && this.last !== false){
36273             var last = this.last;
36274             this.selectRange(last, rowIndex, e.ctrlKey);
36275             this.last = last; // reset the last
36276             view.focusRow(rowIndex);
36277         }else{
36278             var isSelected = this.isSelected(rowIndex);
36279             if(e.button !== 0 && isSelected){
36280                 view.focusRow(rowIndex);
36281             }else if(e.ctrlKey && isSelected){
36282                 this.deselectRow(rowIndex);
36283             }else if(!isSelected){
36284                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36285                 view.focusRow(rowIndex);
36286             }
36287         }
36288         this.fireEvent("afterselectionchange", this);
36289     },
36290     // private
36291     handleDragableRowClick :  function(grid, rowIndex, e) 
36292     {
36293         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36294             this.selectRow(rowIndex, false);
36295             grid.view.focusRow(rowIndex);
36296              this.fireEvent("afterselectionchange", this);
36297         }
36298     },
36299     
36300     /**
36301      * Selects multiple rows.
36302      * @param {Array} rows Array of the indexes of the row to select
36303      * @param {Boolean} keepExisting (optional) True to keep existing selections
36304      */
36305     selectRows : function(rows, keepExisting){
36306         if(!keepExisting){
36307             this.clearSelections();
36308         }
36309         for(var i = 0, len = rows.length; i < len; i++){
36310             this.selectRow(rows[i], true);
36311         }
36312     },
36313
36314     /**
36315      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36316      * @param {Number} startRow The index of the first row in the range
36317      * @param {Number} endRow The index of the last row in the range
36318      * @param {Boolean} keepExisting (optional) True to retain existing selections
36319      */
36320     selectRange : function(startRow, endRow, keepExisting){
36321         if(this.locked) {
36322             return;
36323         }
36324         if(!keepExisting){
36325             this.clearSelections();
36326         }
36327         if(startRow <= endRow){
36328             for(var i = startRow; i <= endRow; i++){
36329                 this.selectRow(i, true);
36330             }
36331         }else{
36332             for(var i = startRow; i >= endRow; i--){
36333                 this.selectRow(i, true);
36334             }
36335         }
36336     },
36337
36338     /**
36339      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36340      * @param {Number} startRow The index of the first row in the range
36341      * @param {Number} endRow The index of the last row in the range
36342      */
36343     deselectRange : function(startRow, endRow, preventViewNotify){
36344         if(this.locked) {
36345             return;
36346         }
36347         for(var i = startRow; i <= endRow; i++){
36348             this.deselectRow(i, preventViewNotify);
36349         }
36350     },
36351
36352     /**
36353      * Selects a row.
36354      * @param {Number} row The index of the row to select
36355      * @param {Boolean} keepExisting (optional) True to keep existing selections
36356      */
36357     selectRow : function(index, keepExisting, preventViewNotify){
36358         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36359             return;
36360         }
36361         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36362             if(!keepExisting || this.singleSelect){
36363                 this.clearSelections();
36364             }
36365             var r = this.grid.dataSource.getAt(index);
36366             this.selections.add(r);
36367             this.last = this.lastActive = index;
36368             if(!preventViewNotify){
36369                 this.grid.getView().onRowSelect(index);
36370             }
36371             this.fireEvent("rowselect", this, index, r);
36372             this.fireEvent("selectionchange", this);
36373         }
36374     },
36375
36376     /**
36377      * Deselects a row.
36378      * @param {Number} row The index of the row to deselect
36379      */
36380     deselectRow : function(index, preventViewNotify){
36381         if(this.locked) {
36382             return;
36383         }
36384         if(this.last == index){
36385             this.last = false;
36386         }
36387         if(this.lastActive == index){
36388             this.lastActive = false;
36389         }
36390         var r = this.grid.dataSource.getAt(index);
36391         this.selections.remove(r);
36392         if(!preventViewNotify){
36393             this.grid.getView().onRowDeselect(index);
36394         }
36395         this.fireEvent("rowdeselect", this, index);
36396         this.fireEvent("selectionchange", this);
36397     },
36398
36399     // private
36400     restoreLast : function(){
36401         if(this._last){
36402             this.last = this._last;
36403         }
36404     },
36405
36406     // private
36407     acceptsNav : function(row, col, cm){
36408         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36409     },
36410
36411     // private
36412     onEditorKey : function(field, e){
36413         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36414         if(k == e.TAB){
36415             e.stopEvent();
36416             ed.completeEdit();
36417             if(e.shiftKey){
36418                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36419             }else{
36420                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36421             }
36422         }else if(k == e.ENTER && !e.ctrlKey){
36423             e.stopEvent();
36424             ed.completeEdit();
36425             if(e.shiftKey){
36426                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36427             }else{
36428                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36429             }
36430         }else if(k == e.ESC){
36431             ed.cancelEdit();
36432         }
36433         if(newCell){
36434             g.startEditing(newCell[0], newCell[1]);
36435         }
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447 /**
36448  * @class Roo.grid.CellSelectionModel
36449  * @extends Roo.grid.AbstractSelectionModel
36450  * This class provides the basic implementation for cell selection in a grid.
36451  * @constructor
36452  * @param {Object} config The object containing the configuration of this model.
36453  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36454  */
36455 Roo.grid.CellSelectionModel = function(config){
36456     Roo.apply(this, config);
36457
36458     this.selection = null;
36459
36460     this.addEvents({
36461         /**
36462              * @event beforerowselect
36463              * Fires before a cell is selected.
36464              * @param {SelectionModel} this
36465              * @param {Number} rowIndex The selected row index
36466              * @param {Number} colIndex The selected cell index
36467              */
36468             "beforecellselect" : true,
36469         /**
36470              * @event cellselect
36471              * Fires when a cell is selected.
36472              * @param {SelectionModel} this
36473              * @param {Number} rowIndex The selected row index
36474              * @param {Number} colIndex The selected cell index
36475              */
36476             "cellselect" : true,
36477         /**
36478              * @event selectionchange
36479              * Fires when the active selection changes.
36480              * @param {SelectionModel} this
36481              * @param {Object} selection null for no selection or an object (o) with two properties
36482                 <ul>
36483                 <li>o.record: the record object for the row the selection is in</li>
36484                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36485                 </ul>
36486              */
36487             "selectionchange" : true,
36488         /**
36489              * @event tabend
36490              * Fires when the tab (or enter) was pressed on the last editable cell
36491              * You can use this to trigger add new row.
36492              * @param {SelectionModel} this
36493              */
36494             "tabend" : true,
36495          /**
36496              * @event beforeeditnext
36497              * Fires before the next editable sell is made active
36498              * You can use this to skip to another cell or fire the tabend
36499              *    if you set cell to false
36500              * @param {Object} eventdata object : { cell : [ row, col ] } 
36501              */
36502             "beforeeditnext" : true
36503     });
36504     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36505 };
36506
36507 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36508     
36509     enter_is_tab: false,
36510
36511     /** @ignore */
36512     initEvents : function(){
36513         this.grid.on("mousedown", this.handleMouseDown, this);
36514         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36515         var view = this.grid.view;
36516         view.on("refresh", this.onViewChange, this);
36517         view.on("rowupdated", this.onRowUpdated, this);
36518         view.on("beforerowremoved", this.clearSelections, this);
36519         view.on("beforerowsinserted", this.clearSelections, this);
36520         if(this.grid.isEditor){
36521             this.grid.on("beforeedit", this.beforeEdit,  this);
36522         }
36523     },
36524
36525         //private
36526     beforeEdit : function(e){
36527         this.select(e.row, e.column, false, true, e.record);
36528     },
36529
36530         //private
36531     onRowUpdated : function(v, index, r){
36532         if(this.selection && this.selection.record == r){
36533             v.onCellSelect(index, this.selection.cell[1]);
36534         }
36535     },
36536
36537         //private
36538     onViewChange : function(){
36539         this.clearSelections(true);
36540     },
36541
36542         /**
36543          * Returns the currently selected cell,.
36544          * @return {Array} The selected cell (row, column) or null if none selected.
36545          */
36546     getSelectedCell : function(){
36547         return this.selection ? this.selection.cell : null;
36548     },
36549
36550     /**
36551      * Clears all selections.
36552      * @param {Boolean} true to prevent the gridview from being notified about the change.
36553      */
36554     clearSelections : function(preventNotify){
36555         var s = this.selection;
36556         if(s){
36557             if(preventNotify !== true){
36558                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36559             }
36560             this.selection = null;
36561             this.fireEvent("selectionchange", this, null);
36562         }
36563     },
36564
36565     /**
36566      * Returns true if there is a selection.
36567      * @return {Boolean}
36568      */
36569     hasSelection : function(){
36570         return this.selection ? true : false;
36571     },
36572
36573     /** @ignore */
36574     handleMouseDown : function(e, t){
36575         var v = this.grid.getView();
36576         if(this.isLocked()){
36577             return;
36578         };
36579         var row = v.findRowIndex(t);
36580         var cell = v.findCellIndex(t);
36581         if(row !== false && cell !== false){
36582             this.select(row, cell);
36583         }
36584     },
36585
36586     /**
36587      * Selects a cell.
36588      * @param {Number} rowIndex
36589      * @param {Number} collIndex
36590      */
36591     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36592         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36593             this.clearSelections();
36594             r = r || this.grid.dataSource.getAt(rowIndex);
36595             this.selection = {
36596                 record : r,
36597                 cell : [rowIndex, colIndex]
36598             };
36599             if(!preventViewNotify){
36600                 var v = this.grid.getView();
36601                 v.onCellSelect(rowIndex, colIndex);
36602                 if(preventFocus !== true){
36603                     v.focusCell(rowIndex, colIndex);
36604                 }
36605             }
36606             this.fireEvent("cellselect", this, rowIndex, colIndex);
36607             this.fireEvent("selectionchange", this, this.selection);
36608         }
36609     },
36610
36611         //private
36612     isSelectable : function(rowIndex, colIndex, cm){
36613         return !cm.isHidden(colIndex);
36614     },
36615
36616     /** @ignore */
36617     handleKeyDown : function(e){
36618         //Roo.log('Cell Sel Model handleKeyDown');
36619         if(!e.isNavKeyPress()){
36620             return;
36621         }
36622         var g = this.grid, s = this.selection;
36623         if(!s){
36624             e.stopEvent();
36625             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36626             if(cell){
36627                 this.select(cell[0], cell[1]);
36628             }
36629             return;
36630         }
36631         var sm = this;
36632         var walk = function(row, col, step){
36633             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36634         };
36635         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36636         var newCell;
36637
36638       
36639
36640         switch(k){
36641             case e.TAB:
36642                 // handled by onEditorKey
36643                 if (g.isEditor && g.editing) {
36644                     return;
36645                 }
36646                 if(e.shiftKey) {
36647                     newCell = walk(r, c-1, -1);
36648                 } else {
36649                     newCell = walk(r, c+1, 1);
36650                 }
36651                 break;
36652             
36653             case e.DOWN:
36654                newCell = walk(r+1, c, 1);
36655                 break;
36656             
36657             case e.UP:
36658                 newCell = walk(r-1, c, -1);
36659                 break;
36660             
36661             case e.RIGHT:
36662                 newCell = walk(r, c+1, 1);
36663                 break;
36664             
36665             case e.LEFT:
36666                 newCell = walk(r, c-1, -1);
36667                 break;
36668             
36669             case e.ENTER:
36670                 
36671                 if(g.isEditor && !g.editing){
36672                    g.startEditing(r, c);
36673                    e.stopEvent();
36674                    return;
36675                 }
36676                 
36677                 
36678              break;
36679         };
36680         if(newCell){
36681             this.select(newCell[0], newCell[1]);
36682             e.stopEvent();
36683             
36684         }
36685     },
36686
36687     acceptsNav : function(row, col, cm){
36688         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36689     },
36690     /**
36691      * Selects a cell.
36692      * @param {Number} field (not used) - as it's normally used as a listener
36693      * @param {Number} e - event - fake it by using
36694      *
36695      * var e = Roo.EventObjectImpl.prototype;
36696      * e.keyCode = e.TAB
36697      *
36698      * 
36699      */
36700     onEditorKey : function(field, e){
36701         
36702         var k = e.getKey(),
36703             newCell,
36704             g = this.grid,
36705             ed = g.activeEditor,
36706             forward = false;
36707         ///Roo.log('onEditorKey' + k);
36708         
36709         
36710         if (this.enter_is_tab && k == e.ENTER) {
36711             k = e.TAB;
36712         }
36713         
36714         if(k == e.TAB){
36715             if(e.shiftKey){
36716                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36717             }else{
36718                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36719                 forward = true;
36720             }
36721             
36722             e.stopEvent();
36723             
36724         } else if(k == e.ENTER &&  !e.ctrlKey){
36725             ed.completeEdit();
36726             e.stopEvent();
36727             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36728         
36729                 } else if(k == e.ESC){
36730             ed.cancelEdit();
36731         }
36732                 
36733         if (newCell) {
36734             var ecall = { cell : newCell, forward : forward };
36735             this.fireEvent('beforeeditnext', ecall );
36736             newCell = ecall.cell;
36737                         forward = ecall.forward;
36738         }
36739                 
36740         if(newCell){
36741             //Roo.log('next cell after edit');
36742             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36743         } else if (forward) {
36744             // tabbed past last
36745             this.fireEvent.defer(100, this, ['tabend',this]);
36746         }
36747     }
36748 });/*
36749  * Based on:
36750  * Ext JS Library 1.1.1
36751  * Copyright(c) 2006-2007, Ext JS, LLC.
36752  *
36753  * Originally Released Under LGPL - original licence link has changed is not relivant.
36754  *
36755  * Fork - LGPL
36756  * <script type="text/javascript">
36757  */
36758  
36759 /**
36760  * @class Roo.grid.EditorGrid
36761  * @extends Roo.grid.Grid
36762  * Class for creating and editable grid.
36763  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36764  * The container MUST have some type of size defined for the grid to fill. The container will be 
36765  * automatically set to position relative if it isn't already.
36766  * @param {Object} dataSource The data model to bind to
36767  * @param {Object} colModel The column model with info about this grid's columns
36768  */
36769 Roo.grid.EditorGrid = function(container, config){
36770     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36771     this.getGridEl().addClass("xedit-grid");
36772
36773     if(!this.selModel){
36774         this.selModel = new Roo.grid.CellSelectionModel();
36775     }
36776
36777     this.activeEditor = null;
36778
36779         this.addEvents({
36780             /**
36781              * @event beforeedit
36782              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36783              * <ul style="padding:5px;padding-left:16px;">
36784              * <li>grid - This grid</li>
36785              * <li>record - The record being edited</li>
36786              * <li>field - The field name being edited</li>
36787              * <li>value - The value for the field being edited.</li>
36788              * <li>row - The grid row index</li>
36789              * <li>column - The grid column index</li>
36790              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36791              * </ul>
36792              * @param {Object} e An edit event (see above for description)
36793              */
36794             "beforeedit" : true,
36795             /**
36796              * @event afteredit
36797              * Fires after a cell is edited. <br />
36798              * <ul style="padding:5px;padding-left:16px;">
36799              * <li>grid - This grid</li>
36800              * <li>record - The record being edited</li>
36801              * <li>field - The field name being edited</li>
36802              * <li>value - The value being set</li>
36803              * <li>originalValue - The original value for the field, before the edit.</li>
36804              * <li>row - The grid row index</li>
36805              * <li>column - The grid column index</li>
36806              * </ul>
36807              * @param {Object} e An edit event (see above for description)
36808              */
36809             "afteredit" : true,
36810             /**
36811              * @event validateedit
36812              * Fires after a cell is edited, but before the value is set in the record. 
36813          * You can use this to modify the value being set in the field, Return false
36814              * to cancel the change. The edit event object has the following properties <br />
36815              * <ul style="padding:5px;padding-left:16px;">
36816          * <li>editor - This editor</li>
36817              * <li>grid - This grid</li>
36818              * <li>record - The record being edited</li>
36819              * <li>field - The field name being edited</li>
36820              * <li>value - The value being set</li>
36821              * <li>originalValue - The original value for the field, before the edit.</li>
36822              * <li>row - The grid row index</li>
36823              * <li>column - The grid column index</li>
36824              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36825              * </ul>
36826              * @param {Object} e An edit event (see above for description)
36827              */
36828             "validateedit" : true
36829         });
36830     this.on("bodyscroll", this.stopEditing,  this);
36831     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36832 };
36833
36834 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36835     /**
36836      * @cfg {Number} clicksToEdit
36837      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36838      */
36839     clicksToEdit: 2,
36840
36841     // private
36842     isEditor : true,
36843     // private
36844     trackMouseOver: false, // causes very odd FF errors
36845
36846     onCellDblClick : function(g, row, col){
36847         this.startEditing(row, col);
36848     },
36849
36850     onEditComplete : function(ed, value, startValue){
36851         this.editing = false;
36852         this.activeEditor = null;
36853         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36854         var r = ed.record;
36855         var field = this.colModel.getDataIndex(ed.col);
36856         var e = {
36857             grid: this,
36858             record: r,
36859             field: field,
36860             originalValue: startValue,
36861             value: value,
36862             row: ed.row,
36863             column: ed.col,
36864             cancel:false,
36865             editor: ed
36866         };
36867         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36868         cell.show();
36869           
36870         if(String(value) !== String(startValue)){
36871             
36872             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36873                 r.set(field, e.value);
36874                 // if we are dealing with a combo box..
36875                 // then we also set the 'name' colum to be the displayField
36876                 if (ed.field.displayField && ed.field.name) {
36877                     r.set(ed.field.name, ed.field.el.dom.value);
36878                 }
36879                 
36880                 delete e.cancel; //?? why!!!
36881                 this.fireEvent("afteredit", e);
36882             }
36883         } else {
36884             this.fireEvent("afteredit", e); // always fire it!
36885         }
36886         this.view.focusCell(ed.row, ed.col);
36887     },
36888
36889     /**
36890      * Starts editing the specified for the specified row/column
36891      * @param {Number} rowIndex
36892      * @param {Number} colIndex
36893      */
36894     startEditing : function(row, col){
36895         this.stopEditing();
36896         if(this.colModel.isCellEditable(col, row)){
36897             this.view.ensureVisible(row, col, true);
36898           
36899             var r = this.dataSource.getAt(row);
36900             var field = this.colModel.getDataIndex(col);
36901             var cell = Roo.get(this.view.getCell(row,col));
36902             var e = {
36903                 grid: this,
36904                 record: r,
36905                 field: field,
36906                 value: r.data[field],
36907                 row: row,
36908                 column: col,
36909                 cancel:false 
36910             };
36911             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36912                 this.editing = true;
36913                 var ed = this.colModel.getCellEditor(col, row);
36914                 
36915                 if (!ed) {
36916                     return;
36917                 }
36918                 if(!ed.rendered){
36919                     ed.render(ed.parentEl || document.body);
36920                 }
36921                 ed.field.reset();
36922                
36923                 cell.hide();
36924                 
36925                 (function(){ // complex but required for focus issues in safari, ie and opera
36926                     ed.row = row;
36927                     ed.col = col;
36928                     ed.record = r;
36929                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36930                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36931                     this.activeEditor = ed;
36932                     var v = r.data[field];
36933                     ed.startEdit(this.view.getCell(row, col), v);
36934                     // combo's with 'displayField and name set
36935                     if (ed.field.displayField && ed.field.name) {
36936                         ed.field.el.dom.value = r.data[ed.field.name];
36937                     }
36938                     
36939                     
36940                 }).defer(50, this);
36941             }
36942         }
36943     },
36944         
36945     /**
36946      * Stops any active editing
36947      */
36948     stopEditing : function(){
36949         if(this.activeEditor){
36950             this.activeEditor.completeEdit();
36951         }
36952         this.activeEditor = null;
36953     },
36954         
36955          /**
36956      * Called to get grid's drag proxy text, by default returns this.ddText.
36957      * @return {String}
36958      */
36959     getDragDropText : function(){
36960         var count = this.selModel.getSelectedCell() ? 1 : 0;
36961         return String.format(this.ddText, count, count == 1 ? '' : 's');
36962     }
36963         
36964 });/*
36965  * Based on:
36966  * Ext JS Library 1.1.1
36967  * Copyright(c) 2006-2007, Ext JS, LLC.
36968  *
36969  * Originally Released Under LGPL - original licence link has changed is not relivant.
36970  *
36971  * Fork - LGPL
36972  * <script type="text/javascript">
36973  */
36974
36975 // private - not really -- you end up using it !
36976 // This is a support class used internally by the Grid components
36977
36978 /**
36979  * @class Roo.grid.GridEditor
36980  * @extends Roo.Editor
36981  * Class for creating and editable grid elements.
36982  * @param {Object} config any settings (must include field)
36983  */
36984 Roo.grid.GridEditor = function(field, config){
36985     if (!config && field.field) {
36986         config = field;
36987         field = Roo.factory(config.field, Roo.form);
36988     }
36989     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36990     field.monitorTab = false;
36991 };
36992
36993 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36994     
36995     /**
36996      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36997      */
36998     
36999     alignment: "tl-tl",
37000     autoSize: "width",
37001     hideEl : false,
37002     cls: "x-small-editor x-grid-editor",
37003     shim:false,
37004     shadow:"frame"
37005 });/*
37006  * Based on:
37007  * Ext JS Library 1.1.1
37008  * Copyright(c) 2006-2007, Ext JS, LLC.
37009  *
37010  * Originally Released Under LGPL - original licence link has changed is not relivant.
37011  *
37012  * Fork - LGPL
37013  * <script type="text/javascript">
37014  */
37015   
37016
37017   
37018 Roo.grid.PropertyRecord = Roo.data.Record.create([
37019     {name:'name',type:'string'},  'value'
37020 ]);
37021
37022
37023 Roo.grid.PropertyStore = function(grid, source){
37024     this.grid = grid;
37025     this.store = new Roo.data.Store({
37026         recordType : Roo.grid.PropertyRecord
37027     });
37028     this.store.on('update', this.onUpdate,  this);
37029     if(source){
37030         this.setSource(source);
37031     }
37032     Roo.grid.PropertyStore.superclass.constructor.call(this);
37033 };
37034
37035
37036
37037 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37038     setSource : function(o){
37039         this.source = o;
37040         this.store.removeAll();
37041         var data = [];
37042         for(var k in o){
37043             if(this.isEditableValue(o[k])){
37044                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37045             }
37046         }
37047         this.store.loadRecords({records: data}, {}, true);
37048     },
37049
37050     onUpdate : function(ds, record, type){
37051         if(type == Roo.data.Record.EDIT){
37052             var v = record.data['value'];
37053             var oldValue = record.modified['value'];
37054             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37055                 this.source[record.id] = v;
37056                 record.commit();
37057                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37058             }else{
37059                 record.reject();
37060             }
37061         }
37062     },
37063
37064     getProperty : function(row){
37065        return this.store.getAt(row);
37066     },
37067
37068     isEditableValue: function(val){
37069         if(val && val instanceof Date){
37070             return true;
37071         }else if(typeof val == 'object' || typeof val == 'function'){
37072             return false;
37073         }
37074         return true;
37075     },
37076
37077     setValue : function(prop, value){
37078         this.source[prop] = value;
37079         this.store.getById(prop).set('value', value);
37080     },
37081
37082     getSource : function(){
37083         return this.source;
37084     }
37085 });
37086
37087 Roo.grid.PropertyColumnModel = function(grid, store){
37088     this.grid = grid;
37089     var g = Roo.grid;
37090     g.PropertyColumnModel.superclass.constructor.call(this, [
37091         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37092         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37093     ]);
37094     this.store = store;
37095     this.bselect = Roo.DomHelper.append(document.body, {
37096         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37097             {tag: 'option', value: 'true', html: 'true'},
37098             {tag: 'option', value: 'false', html: 'false'}
37099         ]
37100     });
37101     Roo.id(this.bselect);
37102     var f = Roo.form;
37103     this.editors = {
37104         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37105         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37106         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37107         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37108         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37109     };
37110     this.renderCellDelegate = this.renderCell.createDelegate(this);
37111     this.renderPropDelegate = this.renderProp.createDelegate(this);
37112 };
37113
37114 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37115     
37116     
37117     nameText : 'Name',
37118     valueText : 'Value',
37119     
37120     dateFormat : 'm/j/Y',
37121     
37122     
37123     renderDate : function(dateVal){
37124         return dateVal.dateFormat(this.dateFormat);
37125     },
37126
37127     renderBool : function(bVal){
37128         return bVal ? 'true' : 'false';
37129     },
37130
37131     isCellEditable : function(colIndex, rowIndex){
37132         return colIndex == 1;
37133     },
37134
37135     getRenderer : function(col){
37136         return col == 1 ?
37137             this.renderCellDelegate : this.renderPropDelegate;
37138     },
37139
37140     renderProp : function(v){
37141         return this.getPropertyName(v);
37142     },
37143
37144     renderCell : function(val){
37145         var rv = val;
37146         if(val instanceof Date){
37147             rv = this.renderDate(val);
37148         }else if(typeof val == 'boolean'){
37149             rv = this.renderBool(val);
37150         }
37151         return Roo.util.Format.htmlEncode(rv);
37152     },
37153
37154     getPropertyName : function(name){
37155         var pn = this.grid.propertyNames;
37156         return pn && pn[name] ? pn[name] : name;
37157     },
37158
37159     getCellEditor : function(colIndex, rowIndex){
37160         var p = this.store.getProperty(rowIndex);
37161         var n = p.data['name'], val = p.data['value'];
37162         
37163         if(typeof(this.grid.customEditors[n]) == 'string'){
37164             return this.editors[this.grid.customEditors[n]];
37165         }
37166         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37167             return this.grid.customEditors[n];
37168         }
37169         if(val instanceof Date){
37170             return this.editors['date'];
37171         }else if(typeof val == 'number'){
37172             return this.editors['number'];
37173         }else if(typeof val == 'boolean'){
37174             return this.editors['boolean'];
37175         }else{
37176             return this.editors['string'];
37177         }
37178     }
37179 });
37180
37181 /**
37182  * @class Roo.grid.PropertyGrid
37183  * @extends Roo.grid.EditorGrid
37184  * This class represents the  interface of a component based property grid control.
37185  * <br><br>Usage:<pre><code>
37186  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37187       
37188  });
37189  // set any options
37190  grid.render();
37191  * </code></pre>
37192   
37193  * @constructor
37194  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37195  * The container MUST have some type of size defined for the grid to fill. The container will be
37196  * automatically set to position relative if it isn't already.
37197  * @param {Object} config A config object that sets properties on this grid.
37198  */
37199 Roo.grid.PropertyGrid = function(container, config){
37200     config = config || {};
37201     var store = new Roo.grid.PropertyStore(this);
37202     this.store = store;
37203     var cm = new Roo.grid.PropertyColumnModel(this, store);
37204     store.store.sort('name', 'ASC');
37205     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37206         ds: store.store,
37207         cm: cm,
37208         enableColLock:false,
37209         enableColumnMove:false,
37210         stripeRows:false,
37211         trackMouseOver: false,
37212         clicksToEdit:1
37213     }, config));
37214     this.getGridEl().addClass('x-props-grid');
37215     this.lastEditRow = null;
37216     this.on('columnresize', this.onColumnResize, this);
37217     this.addEvents({
37218          /**
37219              * @event beforepropertychange
37220              * Fires before a property changes (return false to stop?)
37221              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37222              * @param {String} id Record Id
37223              * @param {String} newval New Value
37224          * @param {String} oldval Old Value
37225              */
37226         "beforepropertychange": true,
37227         /**
37228              * @event propertychange
37229              * Fires after a property changes
37230              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37231              * @param {String} id Record Id
37232              * @param {String} newval New Value
37233          * @param {String} oldval Old Value
37234              */
37235         "propertychange": true
37236     });
37237     this.customEditors = this.customEditors || {};
37238 };
37239 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37240     
37241      /**
37242      * @cfg {Object} customEditors map of colnames=> custom editors.
37243      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37244      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37245      * false disables editing of the field.
37246          */
37247     
37248       /**
37249      * @cfg {Object} propertyNames map of property Names to their displayed value
37250          */
37251     
37252     render : function(){
37253         Roo.grid.PropertyGrid.superclass.render.call(this);
37254         this.autoSize.defer(100, this);
37255     },
37256
37257     autoSize : function(){
37258         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37259         if(this.view){
37260             this.view.fitColumns();
37261         }
37262     },
37263
37264     onColumnResize : function(){
37265         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37266         this.autoSize();
37267     },
37268     /**
37269      * Sets the data for the Grid
37270      * accepts a Key => Value object of all the elements avaiable.
37271      * @param {Object} data  to appear in grid.
37272      */
37273     setSource : function(source){
37274         this.store.setSource(source);
37275         //this.autoSize();
37276     },
37277     /**
37278      * Gets all the data from the grid.
37279      * @return {Object} data  data stored in grid
37280      */
37281     getSource : function(){
37282         return this.store.getSource();
37283     }
37284 });/*
37285   
37286  * Licence LGPL
37287  
37288  */
37289  
37290 /**
37291  * @class Roo.grid.Calendar
37292  * @extends Roo.util.Grid
37293  * This class extends the Grid to provide a calendar widget
37294  * <br><br>Usage:<pre><code>
37295  var grid = new Roo.grid.Calendar("my-container-id", {
37296      ds: myDataStore,
37297      cm: myColModel,
37298      selModel: mySelectionModel,
37299      autoSizeColumns: true,
37300      monitorWindowResize: false,
37301      trackMouseOver: true
37302      eventstore : real data store..
37303  });
37304  // set any options
37305  grid.render();
37306   
37307   * @constructor
37308  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37309  * The container MUST have some type of size defined for the grid to fill. The container will be
37310  * automatically set to position relative if it isn't already.
37311  * @param {Object} config A config object that sets properties on this grid.
37312  */
37313 Roo.grid.Calendar = function(container, config){
37314         // initialize the container
37315         this.container = Roo.get(container);
37316         this.container.update("");
37317         this.container.setStyle("overflow", "hidden");
37318     this.container.addClass('x-grid-container');
37319
37320     this.id = this.container.id;
37321
37322     Roo.apply(this, config);
37323     // check and correct shorthanded configs
37324     
37325     var rows = [];
37326     var d =1;
37327     for (var r = 0;r < 6;r++) {
37328         
37329         rows[r]=[];
37330         for (var c =0;c < 7;c++) {
37331             rows[r][c]= '';
37332         }
37333     }
37334     if (this.eventStore) {
37335         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37336         this.eventStore.on('load',this.onLoad, this);
37337         this.eventStore.on('beforeload',this.clearEvents, this);
37338          
37339     }
37340     
37341     this.dataSource = new Roo.data.Store({
37342             proxy: new Roo.data.MemoryProxy(rows),
37343             reader: new Roo.data.ArrayReader({}, [
37344                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37345     });
37346
37347     this.dataSource.load();
37348     this.ds = this.dataSource;
37349     this.ds.xmodule = this.xmodule || false;
37350     
37351     
37352     var cellRender = function(v,x,r)
37353     {
37354         return String.format(
37355             '<div class="fc-day  fc-widget-content"><div>' +
37356                 '<div class="fc-event-container"></div>' +
37357                 '<div class="fc-day-number">{0}</div>'+
37358                 
37359                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37360             '</div></div>', v);
37361     
37362     }
37363     
37364     
37365     this.colModel = new Roo.grid.ColumnModel( [
37366         {
37367             xtype: 'ColumnModel',
37368             xns: Roo.grid,
37369             dataIndex : 'weekday0',
37370             header : 'Sunday',
37371             renderer : cellRender
37372         },
37373         {
37374             xtype: 'ColumnModel',
37375             xns: Roo.grid,
37376             dataIndex : 'weekday1',
37377             header : 'Monday',
37378             renderer : cellRender
37379         },
37380         {
37381             xtype: 'ColumnModel',
37382             xns: Roo.grid,
37383             dataIndex : 'weekday2',
37384             header : 'Tuesday',
37385             renderer : cellRender
37386         },
37387         {
37388             xtype: 'ColumnModel',
37389             xns: Roo.grid,
37390             dataIndex : 'weekday3',
37391             header : 'Wednesday',
37392             renderer : cellRender
37393         },
37394         {
37395             xtype: 'ColumnModel',
37396             xns: Roo.grid,
37397             dataIndex : 'weekday4',
37398             header : 'Thursday',
37399             renderer : cellRender
37400         },
37401         {
37402             xtype: 'ColumnModel',
37403             xns: Roo.grid,
37404             dataIndex : 'weekday5',
37405             header : 'Friday',
37406             renderer : cellRender
37407         },
37408         {
37409             xtype: 'ColumnModel',
37410             xns: Roo.grid,
37411             dataIndex : 'weekday6',
37412             header : 'Saturday',
37413             renderer : cellRender
37414         }
37415     ]);
37416     this.cm = this.colModel;
37417     this.cm.xmodule = this.xmodule || false;
37418  
37419         
37420           
37421     //this.selModel = new Roo.grid.CellSelectionModel();
37422     //this.sm = this.selModel;
37423     //this.selModel.init(this);
37424     
37425     
37426     if(this.width){
37427         this.container.setWidth(this.width);
37428     }
37429
37430     if(this.height){
37431         this.container.setHeight(this.height);
37432     }
37433     /** @private */
37434         this.addEvents({
37435         // raw events
37436         /**
37437          * @event click
37438          * The raw click event for the entire grid.
37439          * @param {Roo.EventObject} e
37440          */
37441         "click" : true,
37442         /**
37443          * @event dblclick
37444          * The raw dblclick event for the entire grid.
37445          * @param {Roo.EventObject} e
37446          */
37447         "dblclick" : true,
37448         /**
37449          * @event contextmenu
37450          * The raw contextmenu event for the entire grid.
37451          * @param {Roo.EventObject} e
37452          */
37453         "contextmenu" : true,
37454         /**
37455          * @event mousedown
37456          * The raw mousedown event for the entire grid.
37457          * @param {Roo.EventObject} e
37458          */
37459         "mousedown" : true,
37460         /**
37461          * @event mouseup
37462          * The raw mouseup event for the entire grid.
37463          * @param {Roo.EventObject} e
37464          */
37465         "mouseup" : true,
37466         /**
37467          * @event mouseover
37468          * The raw mouseover event for the entire grid.
37469          * @param {Roo.EventObject} e
37470          */
37471         "mouseover" : true,
37472         /**
37473          * @event mouseout
37474          * The raw mouseout event for the entire grid.
37475          * @param {Roo.EventObject} e
37476          */
37477         "mouseout" : true,
37478         /**
37479          * @event keypress
37480          * The raw keypress event for the entire grid.
37481          * @param {Roo.EventObject} e
37482          */
37483         "keypress" : true,
37484         /**
37485          * @event keydown
37486          * The raw keydown event for the entire grid.
37487          * @param {Roo.EventObject} e
37488          */
37489         "keydown" : true,
37490
37491         // custom events
37492
37493         /**
37494          * @event cellclick
37495          * Fires when a cell is clicked
37496          * @param {Grid} this
37497          * @param {Number} rowIndex
37498          * @param {Number} columnIndex
37499          * @param {Roo.EventObject} e
37500          */
37501         "cellclick" : true,
37502         /**
37503          * @event celldblclick
37504          * Fires when a cell is double clicked
37505          * @param {Grid} this
37506          * @param {Number} rowIndex
37507          * @param {Number} columnIndex
37508          * @param {Roo.EventObject} e
37509          */
37510         "celldblclick" : true,
37511         /**
37512          * @event rowclick
37513          * Fires when a row is clicked
37514          * @param {Grid} this
37515          * @param {Number} rowIndex
37516          * @param {Roo.EventObject} e
37517          */
37518         "rowclick" : true,
37519         /**
37520          * @event rowdblclick
37521          * Fires when a row is double clicked
37522          * @param {Grid} this
37523          * @param {Number} rowIndex
37524          * @param {Roo.EventObject} e
37525          */
37526         "rowdblclick" : true,
37527         /**
37528          * @event headerclick
37529          * Fires when a header is clicked
37530          * @param {Grid} this
37531          * @param {Number} columnIndex
37532          * @param {Roo.EventObject} e
37533          */
37534         "headerclick" : true,
37535         /**
37536          * @event headerdblclick
37537          * Fires when a header cell is double clicked
37538          * @param {Grid} this
37539          * @param {Number} columnIndex
37540          * @param {Roo.EventObject} e
37541          */
37542         "headerdblclick" : true,
37543         /**
37544          * @event rowcontextmenu
37545          * Fires when a row is right clicked
37546          * @param {Grid} this
37547          * @param {Number} rowIndex
37548          * @param {Roo.EventObject} e
37549          */
37550         "rowcontextmenu" : true,
37551         /**
37552          * @event cellcontextmenu
37553          * Fires when a cell is right clicked
37554          * @param {Grid} this
37555          * @param {Number} rowIndex
37556          * @param {Number} cellIndex
37557          * @param {Roo.EventObject} e
37558          */
37559          "cellcontextmenu" : true,
37560         /**
37561          * @event headercontextmenu
37562          * Fires when a header is right clicked
37563          * @param {Grid} this
37564          * @param {Number} columnIndex
37565          * @param {Roo.EventObject} e
37566          */
37567         "headercontextmenu" : true,
37568         /**
37569          * @event bodyscroll
37570          * Fires when the body element is scrolled
37571          * @param {Number} scrollLeft
37572          * @param {Number} scrollTop
37573          */
37574         "bodyscroll" : true,
37575         /**
37576          * @event columnresize
37577          * Fires when the user resizes a column
37578          * @param {Number} columnIndex
37579          * @param {Number} newSize
37580          */
37581         "columnresize" : true,
37582         /**
37583          * @event columnmove
37584          * Fires when the user moves a column
37585          * @param {Number} oldIndex
37586          * @param {Number} newIndex
37587          */
37588         "columnmove" : true,
37589         /**
37590          * @event startdrag
37591          * Fires when row(s) start being dragged
37592          * @param {Grid} this
37593          * @param {Roo.GridDD} dd The drag drop object
37594          * @param {event} e The raw browser event
37595          */
37596         "startdrag" : true,
37597         /**
37598          * @event enddrag
37599          * Fires when a drag operation is complete
37600          * @param {Grid} this
37601          * @param {Roo.GridDD} dd The drag drop object
37602          * @param {event} e The raw browser event
37603          */
37604         "enddrag" : true,
37605         /**
37606          * @event dragdrop
37607          * Fires when dragged row(s) are dropped on a valid DD target
37608          * @param {Grid} this
37609          * @param {Roo.GridDD} dd The drag drop object
37610          * @param {String} targetId The target drag drop object
37611          * @param {event} e The raw browser event
37612          */
37613         "dragdrop" : true,
37614         /**
37615          * @event dragover
37616          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37617          * @param {Grid} this
37618          * @param {Roo.GridDD} dd The drag drop object
37619          * @param {String} targetId The target drag drop object
37620          * @param {event} e The raw browser event
37621          */
37622         "dragover" : true,
37623         /**
37624          * @event dragenter
37625          *  Fires when the dragged row(s) first cross another DD target while being dragged
37626          * @param {Grid} this
37627          * @param {Roo.GridDD} dd The drag drop object
37628          * @param {String} targetId The target drag drop object
37629          * @param {event} e The raw browser event
37630          */
37631         "dragenter" : true,
37632         /**
37633          * @event dragout
37634          * Fires when the dragged row(s) leave another DD target while being dragged
37635          * @param {Grid} this
37636          * @param {Roo.GridDD} dd The drag drop object
37637          * @param {String} targetId The target drag drop object
37638          * @param {event} e The raw browser event
37639          */
37640         "dragout" : true,
37641         /**
37642          * @event rowclass
37643          * Fires when a row is rendered, so you can change add a style to it.
37644          * @param {GridView} gridview   The grid view
37645          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37646          */
37647         'rowclass' : true,
37648
37649         /**
37650          * @event render
37651          * Fires when the grid is rendered
37652          * @param {Grid} grid
37653          */
37654         'render' : true,
37655             /**
37656              * @event select
37657              * Fires when a date is selected
37658              * @param {DatePicker} this
37659              * @param {Date} date The selected date
37660              */
37661         'select': true,
37662         /**
37663              * @event monthchange
37664              * Fires when the displayed month changes 
37665              * @param {DatePicker} this
37666              * @param {Date} date The selected month
37667              */
37668         'monthchange': true,
37669         /**
37670              * @event evententer
37671              * Fires when mouse over an event
37672              * @param {Calendar} this
37673              * @param {event} Event
37674              */
37675         'evententer': true,
37676         /**
37677              * @event eventleave
37678              * Fires when the mouse leaves an
37679              * @param {Calendar} this
37680              * @param {event}
37681              */
37682         'eventleave': true,
37683         /**
37684              * @event eventclick
37685              * Fires when the mouse click an
37686              * @param {Calendar} this
37687              * @param {event}
37688              */
37689         'eventclick': true,
37690         /**
37691              * @event eventrender
37692              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37693              * @param {Calendar} this
37694              * @param {data} data to be modified
37695              */
37696         'eventrender': true
37697         
37698     });
37699
37700     Roo.grid.Grid.superclass.constructor.call(this);
37701     this.on('render', function() {
37702         this.view.el.addClass('x-grid-cal'); 
37703         
37704         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37705
37706     },this);
37707     
37708     if (!Roo.grid.Calendar.style) {
37709         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37710             
37711             
37712             '.x-grid-cal .x-grid-col' :  {
37713                 height: 'auto !important',
37714                 'vertical-align': 'top'
37715             },
37716             '.x-grid-cal  .fc-event-hori' : {
37717                 height: '14px'
37718             }
37719              
37720             
37721         }, Roo.id());
37722     }
37723
37724     
37725     
37726 };
37727 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37728     /**
37729      * @cfg {Store} eventStore The store that loads events.
37730      */
37731     eventStore : 25,
37732
37733      
37734     activeDate : false,
37735     startDay : 0,
37736     autoWidth : true,
37737     monitorWindowResize : false,
37738
37739     
37740     resizeColumns : function() {
37741         var col = (this.view.el.getWidth() / 7) - 3;
37742         // loop through cols, and setWidth
37743         for(var i =0 ; i < 7 ; i++){
37744             this.cm.setColumnWidth(i, col);
37745         }
37746     },
37747      setDate :function(date) {
37748         
37749         Roo.log('setDate?');
37750         
37751         this.resizeColumns();
37752         var vd = this.activeDate;
37753         this.activeDate = date;
37754 //        if(vd && this.el){
37755 //            var t = date.getTime();
37756 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37757 //                Roo.log('using add remove');
37758 //                
37759 //                this.fireEvent('monthchange', this, date);
37760 //                
37761 //                this.cells.removeClass("fc-state-highlight");
37762 //                this.cells.each(function(c){
37763 //                   if(c.dateValue == t){
37764 //                       c.addClass("fc-state-highlight");
37765 //                       setTimeout(function(){
37766 //                            try{c.dom.firstChild.focus();}catch(e){}
37767 //                       }, 50);
37768 //                       return false;
37769 //                   }
37770 //                   return true;
37771 //                });
37772 //                return;
37773 //            }
37774 //        }
37775         
37776         var days = date.getDaysInMonth();
37777         
37778         var firstOfMonth = date.getFirstDateOfMonth();
37779         var startingPos = firstOfMonth.getDay()-this.startDay;
37780         
37781         if(startingPos < this.startDay){
37782             startingPos += 7;
37783         }
37784         
37785         var pm = date.add(Date.MONTH, -1);
37786         var prevStart = pm.getDaysInMonth()-startingPos;
37787 //        
37788         
37789         
37790         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37791         
37792         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37793         //this.cells.addClassOnOver('fc-state-hover');
37794         
37795         var cells = this.cells.elements;
37796         var textEls = this.textNodes;
37797         
37798         //Roo.each(cells, function(cell){
37799         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37800         //});
37801         
37802         days += startingPos;
37803
37804         // convert everything to numbers so it's fast
37805         var day = 86400000;
37806         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37807         //Roo.log(d);
37808         //Roo.log(pm);
37809         //Roo.log(prevStart);
37810         
37811         var today = new Date().clearTime().getTime();
37812         var sel = date.clearTime().getTime();
37813         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37814         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37815         var ddMatch = this.disabledDatesRE;
37816         var ddText = this.disabledDatesText;
37817         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37818         var ddaysText = this.disabledDaysText;
37819         var format = this.format;
37820         
37821         var setCellClass = function(cal, cell){
37822             
37823             //Roo.log('set Cell Class');
37824             cell.title = "";
37825             var t = d.getTime();
37826             
37827             //Roo.log(d);
37828             
37829             
37830             cell.dateValue = t;
37831             if(t == today){
37832                 cell.className += " fc-today";
37833                 cell.className += " fc-state-highlight";
37834                 cell.title = cal.todayText;
37835             }
37836             if(t == sel){
37837                 // disable highlight in other month..
37838                 cell.className += " fc-state-highlight";
37839                 
37840             }
37841             // disabling
37842             if(t < min) {
37843                 //cell.className = " fc-state-disabled";
37844                 cell.title = cal.minText;
37845                 return;
37846             }
37847             if(t > max) {
37848                 //cell.className = " fc-state-disabled";
37849                 cell.title = cal.maxText;
37850                 return;
37851             }
37852             if(ddays){
37853                 if(ddays.indexOf(d.getDay()) != -1){
37854                     // cell.title = ddaysText;
37855                    // cell.className = " fc-state-disabled";
37856                 }
37857             }
37858             if(ddMatch && format){
37859                 var fvalue = d.dateFormat(format);
37860                 if(ddMatch.test(fvalue)){
37861                     cell.title = ddText.replace("%0", fvalue);
37862                    cell.className = " fc-state-disabled";
37863                 }
37864             }
37865             
37866             if (!cell.initialClassName) {
37867                 cell.initialClassName = cell.dom.className;
37868             }
37869             
37870             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37871         };
37872
37873         var i = 0;
37874         
37875         for(; i < startingPos; i++) {
37876             cells[i].dayName =  (++prevStart);
37877             Roo.log(textEls[i]);
37878             d.setDate(d.getDate()+1);
37879             
37880             //cells[i].className = "fc-past fc-other-month";
37881             setCellClass(this, cells[i]);
37882         }
37883         
37884         var intDay = 0;
37885         
37886         for(; i < days; i++){
37887             intDay = i - startingPos + 1;
37888             cells[i].dayName =  (intDay);
37889             d.setDate(d.getDate()+1);
37890             
37891             cells[i].className = ''; // "x-date-active";
37892             setCellClass(this, cells[i]);
37893         }
37894         var extraDays = 0;
37895         
37896         for(; i < 42; i++) {
37897             //textEls[i].innerHTML = (++extraDays);
37898             
37899             d.setDate(d.getDate()+1);
37900             cells[i].dayName = (++extraDays);
37901             cells[i].className = "fc-future fc-other-month";
37902             setCellClass(this, cells[i]);
37903         }
37904         
37905         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37906         
37907         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37908         
37909         // this will cause all the cells to mis
37910         var rows= [];
37911         var i =0;
37912         for (var r = 0;r < 6;r++) {
37913             for (var c =0;c < 7;c++) {
37914                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37915             }    
37916         }
37917         
37918         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37919         for(i=0;i<cells.length;i++) {
37920             
37921             this.cells.elements[i].dayName = cells[i].dayName ;
37922             this.cells.elements[i].className = cells[i].className;
37923             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37924             this.cells.elements[i].title = cells[i].title ;
37925             this.cells.elements[i].dateValue = cells[i].dateValue ;
37926         }
37927         
37928         
37929         
37930         
37931         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37932         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37933         
37934         ////if(totalRows != 6){
37935             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37936            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37937        // }
37938         
37939         this.fireEvent('monthchange', this, date);
37940         
37941         
37942     },
37943  /**
37944      * Returns the grid's SelectionModel.
37945      * @return {SelectionModel}
37946      */
37947     getSelectionModel : function(){
37948         if(!this.selModel){
37949             this.selModel = new Roo.grid.CellSelectionModel();
37950         }
37951         return this.selModel;
37952     },
37953
37954     load: function() {
37955         this.eventStore.load()
37956         
37957         
37958         
37959     },
37960     
37961     findCell : function(dt) {
37962         dt = dt.clearTime().getTime();
37963         var ret = false;
37964         this.cells.each(function(c){
37965             //Roo.log("check " +c.dateValue + '?=' + dt);
37966             if(c.dateValue == dt){
37967                 ret = c;
37968                 return false;
37969             }
37970             return true;
37971         });
37972         
37973         return ret;
37974     },
37975     
37976     findCells : function(rec) {
37977         var s = rec.data.start_dt.clone().clearTime().getTime();
37978        // Roo.log(s);
37979         var e= rec.data.end_dt.clone().clearTime().getTime();
37980        // Roo.log(e);
37981         var ret = [];
37982         this.cells.each(function(c){
37983              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37984             
37985             if(c.dateValue > e){
37986                 return ;
37987             }
37988             if(c.dateValue < s){
37989                 return ;
37990             }
37991             ret.push(c);
37992         });
37993         
37994         return ret;    
37995     },
37996     
37997     findBestRow: function(cells)
37998     {
37999         var ret = 0;
38000         
38001         for (var i =0 ; i < cells.length;i++) {
38002             ret  = Math.max(cells[i].rows || 0,ret);
38003         }
38004         return ret;
38005         
38006     },
38007     
38008     
38009     addItem : function(rec)
38010     {
38011         // look for vertical location slot in
38012         var cells = this.findCells(rec);
38013         
38014         rec.row = this.findBestRow(cells);
38015         
38016         // work out the location.
38017         
38018         var crow = false;
38019         var rows = [];
38020         for(var i =0; i < cells.length; i++) {
38021             if (!crow) {
38022                 crow = {
38023                     start : cells[i],
38024                     end :  cells[i]
38025                 };
38026                 continue;
38027             }
38028             if (crow.start.getY() == cells[i].getY()) {
38029                 // on same row.
38030                 crow.end = cells[i];
38031                 continue;
38032             }
38033             // different row.
38034             rows.push(crow);
38035             crow = {
38036                 start: cells[i],
38037                 end : cells[i]
38038             };
38039             
38040         }
38041         
38042         rows.push(crow);
38043         rec.els = [];
38044         rec.rows = rows;
38045         rec.cells = cells;
38046         for (var i = 0; i < cells.length;i++) {
38047             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38048             
38049         }
38050         
38051         
38052     },
38053     
38054     clearEvents: function() {
38055         
38056         if (!this.eventStore.getCount()) {
38057             return;
38058         }
38059         // reset number of rows in cells.
38060         Roo.each(this.cells.elements, function(c){
38061             c.rows = 0;
38062         });
38063         
38064         this.eventStore.each(function(e) {
38065             this.clearEvent(e);
38066         },this);
38067         
38068     },
38069     
38070     clearEvent : function(ev)
38071     {
38072         if (ev.els) {
38073             Roo.each(ev.els, function(el) {
38074                 el.un('mouseenter' ,this.onEventEnter, this);
38075                 el.un('mouseleave' ,this.onEventLeave, this);
38076                 el.remove();
38077             },this);
38078             ev.els = [];
38079         }
38080     },
38081     
38082     
38083     renderEvent : function(ev,ctr) {
38084         if (!ctr) {
38085              ctr = this.view.el.select('.fc-event-container',true).first();
38086         }
38087         
38088          
38089         this.clearEvent(ev);
38090             //code
38091        
38092         
38093         
38094         ev.els = [];
38095         var cells = ev.cells;
38096         var rows = ev.rows;
38097         this.fireEvent('eventrender', this, ev);
38098         
38099         for(var i =0; i < rows.length; i++) {
38100             
38101             cls = '';
38102             if (i == 0) {
38103                 cls += ' fc-event-start';
38104             }
38105             if ((i+1) == rows.length) {
38106                 cls += ' fc-event-end';
38107             }
38108             
38109             //Roo.log(ev.data);
38110             // how many rows should it span..
38111             var cg = this.eventTmpl.append(ctr,Roo.apply({
38112                 fccls : cls
38113                 
38114             }, ev.data) , true);
38115             
38116             
38117             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38118             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38119             cg.on('click', this.onEventClick, this, ev);
38120             
38121             ev.els.push(cg);
38122             
38123             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38124             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38125             //Roo.log(cg);
38126              
38127             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38128             cg.setWidth(ebox.right - sbox.x -2);
38129         }
38130     },
38131     
38132     renderEvents: function()
38133     {   
38134         // first make sure there is enough space..
38135         
38136         if (!this.eventTmpl) {
38137             this.eventTmpl = new Roo.Template(
38138                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38139                     '<div class="fc-event-inner">' +
38140                         '<span class="fc-event-time">{time}</span>' +
38141                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38142                     '</div>' +
38143                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38144                 '</div>'
38145             );
38146                 
38147         }
38148                
38149         
38150         
38151         this.cells.each(function(c) {
38152             //Roo.log(c.select('.fc-day-content div',true).first());
38153             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38154         });
38155         
38156         var ctr = this.view.el.select('.fc-event-container',true).first();
38157         
38158         var cls;
38159         this.eventStore.each(function(ev){
38160             
38161             this.renderEvent(ev);
38162              
38163              
38164         }, this);
38165         this.view.layout();
38166         
38167     },
38168     
38169     onEventEnter: function (e, el,event,d) {
38170         this.fireEvent('evententer', this, el, event);
38171     },
38172     
38173     onEventLeave: function (e, el,event,d) {
38174         this.fireEvent('eventleave', this, el, event);
38175     },
38176     
38177     onEventClick: function (e, el,event,d) {
38178         this.fireEvent('eventclick', this, el, event);
38179     },
38180     
38181     onMonthChange: function () {
38182         this.store.load();
38183     },
38184     
38185     onLoad: function () {
38186         
38187         //Roo.log('calendar onload');
38188 //         
38189         if(this.eventStore.getCount() > 0){
38190             
38191            
38192             
38193             this.eventStore.each(function(d){
38194                 
38195                 
38196                 // FIXME..
38197                 var add =   d.data;
38198                 if (typeof(add.end_dt) == 'undefined')  {
38199                     Roo.log("Missing End time in calendar data: ");
38200                     Roo.log(d);
38201                     return;
38202                 }
38203                 if (typeof(add.start_dt) == 'undefined')  {
38204                     Roo.log("Missing Start time in calendar data: ");
38205                     Roo.log(d);
38206                     return;
38207                 }
38208                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38209                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38210                 add.id = add.id || d.id;
38211                 add.title = add.title || '??';
38212                 
38213                 this.addItem(d);
38214                 
38215              
38216             },this);
38217         }
38218         
38219         this.renderEvents();
38220     }
38221     
38222
38223 });
38224 /*
38225  grid : {
38226                 xtype: 'Grid',
38227                 xns: Roo.grid,
38228                 listeners : {
38229                     render : function ()
38230                     {
38231                         _this.grid = this;
38232                         
38233                         if (!this.view.el.hasClass('course-timesheet')) {
38234                             this.view.el.addClass('course-timesheet');
38235                         }
38236                         if (this.tsStyle) {
38237                             this.ds.load({});
38238                             return; 
38239                         }
38240                         Roo.log('width');
38241                         Roo.log(_this.grid.view.el.getWidth());
38242                         
38243                         
38244                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38245                             '.course-timesheet .x-grid-row' : {
38246                                 height: '80px'
38247                             },
38248                             '.x-grid-row td' : {
38249                                 'vertical-align' : 0
38250                             },
38251                             '.course-edit-link' : {
38252                                 'color' : 'blue',
38253                                 'text-overflow' : 'ellipsis',
38254                                 'overflow' : 'hidden',
38255                                 'white-space' : 'nowrap',
38256                                 'cursor' : 'pointer'
38257                             },
38258                             '.sub-link' : {
38259                                 'color' : 'green'
38260                             },
38261                             '.de-act-sup-link' : {
38262                                 'color' : 'purple',
38263                                 'text-decoration' : 'line-through'
38264                             },
38265                             '.de-act-link' : {
38266                                 'color' : 'red',
38267                                 'text-decoration' : 'line-through'
38268                             },
38269                             '.course-timesheet .course-highlight' : {
38270                                 'border-top-style': 'dashed !important',
38271                                 'border-bottom-bottom': 'dashed !important'
38272                             },
38273                             '.course-timesheet .course-item' : {
38274                                 'font-family'   : 'tahoma, arial, helvetica',
38275                                 'font-size'     : '11px',
38276                                 'overflow'      : 'hidden',
38277                                 'padding-left'  : '10px',
38278                                 'padding-right' : '10px',
38279                                 'padding-top' : '10px' 
38280                             }
38281                             
38282                         }, Roo.id());
38283                                 this.ds.load({});
38284                     }
38285                 },
38286                 autoWidth : true,
38287                 monitorWindowResize : false,
38288                 cellrenderer : function(v,x,r)
38289                 {
38290                     return v;
38291                 },
38292                 sm : {
38293                     xtype: 'CellSelectionModel',
38294                     xns: Roo.grid
38295                 },
38296                 dataSource : {
38297                     xtype: 'Store',
38298                     xns: Roo.data,
38299                     listeners : {
38300                         beforeload : function (_self, options)
38301                         {
38302                             options.params = options.params || {};
38303                             options.params._month = _this.monthField.getValue();
38304                             options.params.limit = 9999;
38305                             options.params['sort'] = 'when_dt';    
38306                             options.params['dir'] = 'ASC';    
38307                             this.proxy.loadResponse = this.loadResponse;
38308                             Roo.log("load?");
38309                             //this.addColumns();
38310                         },
38311                         load : function (_self, records, options)
38312                         {
38313                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38314                                 // if you click on the translation.. you can edit it...
38315                                 var el = Roo.get(this);
38316                                 var id = el.dom.getAttribute('data-id');
38317                                 var d = el.dom.getAttribute('data-date');
38318                                 var t = el.dom.getAttribute('data-time');
38319                                 //var id = this.child('span').dom.textContent;
38320                                 
38321                                 //Roo.log(this);
38322                                 Pman.Dialog.CourseCalendar.show({
38323                                     id : id,
38324                                     when_d : d,
38325                                     when_t : t,
38326                                     productitem_active : id ? 1 : 0
38327                                 }, function() {
38328                                     _this.grid.ds.load({});
38329                                 });
38330                            
38331                            });
38332                            
38333                            _this.panel.fireEvent('resize', [ '', '' ]);
38334                         }
38335                     },
38336                     loadResponse : function(o, success, response){
38337                             // this is overridden on before load..
38338                             
38339                             Roo.log("our code?");       
38340                             //Roo.log(success);
38341                             //Roo.log(response)
38342                             delete this.activeRequest;
38343                             if(!success){
38344                                 this.fireEvent("loadexception", this, o, response);
38345                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38346                                 return;
38347                             }
38348                             var result;
38349                             try {
38350                                 result = o.reader.read(response);
38351                             }catch(e){
38352                                 Roo.log("load exception?");
38353                                 this.fireEvent("loadexception", this, o, response, e);
38354                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38355                                 return;
38356                             }
38357                             Roo.log("ready...");        
38358                             // loop through result.records;
38359                             // and set this.tdate[date] = [] << array of records..
38360                             _this.tdata  = {};
38361                             Roo.each(result.records, function(r){
38362                                 //Roo.log(r.data);
38363                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38364                                     _this.tdata[r.data.when_dt.format('j')] = [];
38365                                 }
38366                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38367                             });
38368                             
38369                             //Roo.log(_this.tdata);
38370                             
38371                             result.records = [];
38372                             result.totalRecords = 6;
38373                     
38374                             // let's generate some duumy records for the rows.
38375                             //var st = _this.dateField.getValue();
38376                             
38377                             // work out monday..
38378                             //st = st.add(Date.DAY, -1 * st.format('w'));
38379                             
38380                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38381                             
38382                             var firstOfMonth = date.getFirstDayOfMonth();
38383                             var days = date.getDaysInMonth();
38384                             var d = 1;
38385                             var firstAdded = false;
38386                             for (var i = 0; i < result.totalRecords ; i++) {
38387                                 //var d= st.add(Date.DAY, i);
38388                                 var row = {};
38389                                 var added = 0;
38390                                 for(var w = 0 ; w < 7 ; w++){
38391                                     if(!firstAdded && firstOfMonth != w){
38392                                         continue;
38393                                     }
38394                                     if(d > days){
38395                                         continue;
38396                                     }
38397                                     firstAdded = true;
38398                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38399                                     row['weekday'+w] = String.format(
38400                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38401                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38402                                                     d,
38403                                                     date.format('Y-m-')+dd
38404                                                 );
38405                                     added++;
38406                                     if(typeof(_this.tdata[d]) != 'undefined'){
38407                                         Roo.each(_this.tdata[d], function(r){
38408                                             var is_sub = '';
38409                                             var deactive = '';
38410                                             var id = r.id;
38411                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38412                                             if(r.parent_id*1>0){
38413                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38414                                                 id = r.parent_id;
38415                                             }
38416                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38417                                                 deactive = 'de-act-link';
38418                                             }
38419                                             
38420                                             row['weekday'+w] += String.format(
38421                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38422                                                     id, //0
38423                                                     r.product_id_name, //1
38424                                                     r.when_dt.format('h:ia'), //2
38425                                                     is_sub, //3
38426                                                     deactive, //4
38427                                                     desc // 5
38428                                             );
38429                                         });
38430                                     }
38431                                     d++;
38432                                 }
38433                                 
38434                                 // only do this if something added..
38435                                 if(added > 0){ 
38436                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38437                                 }
38438                                 
38439                                 
38440                                 // push it twice. (second one with an hour..
38441                                 
38442                             }
38443                             //Roo.log(result);
38444                             this.fireEvent("load", this, o, o.request.arg);
38445                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38446                         },
38447                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38448                     proxy : {
38449                         xtype: 'HttpProxy',
38450                         xns: Roo.data,
38451                         method : 'GET',
38452                         url : baseURL + '/Roo/Shop_course.php'
38453                     },
38454                     reader : {
38455                         xtype: 'JsonReader',
38456                         xns: Roo.data,
38457                         id : 'id',
38458                         fields : [
38459                             {
38460                                 'name': 'id',
38461                                 'type': 'int'
38462                             },
38463                             {
38464                                 'name': 'when_dt',
38465                                 'type': 'string'
38466                             },
38467                             {
38468                                 'name': 'end_dt',
38469                                 'type': 'string'
38470                             },
38471                             {
38472                                 'name': 'parent_id',
38473                                 'type': 'int'
38474                             },
38475                             {
38476                                 'name': 'product_id',
38477                                 'type': 'int'
38478                             },
38479                             {
38480                                 'name': 'productitem_id',
38481                                 'type': 'int'
38482                             },
38483                             {
38484                                 'name': 'guid',
38485                                 'type': 'int'
38486                             }
38487                         ]
38488                     }
38489                 },
38490                 toolbar : {
38491                     xtype: 'Toolbar',
38492                     xns: Roo,
38493                     items : [
38494                         {
38495                             xtype: 'Button',
38496                             xns: Roo.Toolbar,
38497                             listeners : {
38498                                 click : function (_self, e)
38499                                 {
38500                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38501                                     sd.setMonth(sd.getMonth()-1);
38502                                     _this.monthField.setValue(sd.format('Y-m-d'));
38503                                     _this.grid.ds.load({});
38504                                 }
38505                             },
38506                             text : "Back"
38507                         },
38508                         {
38509                             xtype: 'Separator',
38510                             xns: Roo.Toolbar
38511                         },
38512                         {
38513                             xtype: 'MonthField',
38514                             xns: Roo.form,
38515                             listeners : {
38516                                 render : function (_self)
38517                                 {
38518                                     _this.monthField = _self;
38519                                    // _this.monthField.set  today
38520                                 },
38521                                 select : function (combo, date)
38522                                 {
38523                                     _this.grid.ds.load({});
38524                                 }
38525                             },
38526                             value : (function() { return new Date(); })()
38527                         },
38528                         {
38529                             xtype: 'Separator',
38530                             xns: Roo.Toolbar
38531                         },
38532                         {
38533                             xtype: 'TextItem',
38534                             xns: Roo.Toolbar,
38535                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38536                         },
38537                         {
38538                             xtype: 'Fill',
38539                             xns: Roo.Toolbar
38540                         },
38541                         {
38542                             xtype: 'Button',
38543                             xns: Roo.Toolbar,
38544                             listeners : {
38545                                 click : function (_self, e)
38546                                 {
38547                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38548                                     sd.setMonth(sd.getMonth()+1);
38549                                     _this.monthField.setValue(sd.format('Y-m-d'));
38550                                     _this.grid.ds.load({});
38551                                 }
38552                             },
38553                             text : "Next"
38554                         }
38555                     ]
38556                 },
38557                  
38558             }
38559         };
38560         
38561         *//*
38562  * Based on:
38563  * Ext JS Library 1.1.1
38564  * Copyright(c) 2006-2007, Ext JS, LLC.
38565  *
38566  * Originally Released Under LGPL - original licence link has changed is not relivant.
38567  *
38568  * Fork - LGPL
38569  * <script type="text/javascript">
38570  */
38571  
38572 /**
38573  * @class Roo.LoadMask
38574  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38575  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38576  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38577  * element's UpdateManager load indicator and will be destroyed after the initial load.
38578  * @constructor
38579  * Create a new LoadMask
38580  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38581  * @param {Object} config The config object
38582  */
38583 Roo.LoadMask = function(el, config){
38584     this.el = Roo.get(el);
38585     Roo.apply(this, config);
38586     if(this.store){
38587         this.store.on('beforeload', this.onBeforeLoad, this);
38588         this.store.on('load', this.onLoad, this);
38589         this.store.on('loadexception', this.onLoadException, this);
38590         this.removeMask = false;
38591     }else{
38592         var um = this.el.getUpdateManager();
38593         um.showLoadIndicator = false; // disable the default indicator
38594         um.on('beforeupdate', this.onBeforeLoad, this);
38595         um.on('update', this.onLoad, this);
38596         um.on('failure', this.onLoad, this);
38597         this.removeMask = true;
38598     }
38599 };
38600
38601 Roo.LoadMask.prototype = {
38602     /**
38603      * @cfg {Boolean} removeMask
38604      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38605      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38606      */
38607     /**
38608      * @cfg {String} msg
38609      * The text to display in a centered loading message box (defaults to 'Loading...')
38610      */
38611     msg : 'Loading...',
38612     /**
38613      * @cfg {String} msgCls
38614      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38615      */
38616     msgCls : 'x-mask-loading',
38617
38618     /**
38619      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38620      * @type Boolean
38621      */
38622     disabled: false,
38623
38624     /**
38625      * Disables the mask to prevent it from being displayed
38626      */
38627     disable : function(){
38628        this.disabled = true;
38629     },
38630
38631     /**
38632      * Enables the mask so that it can be displayed
38633      */
38634     enable : function(){
38635         this.disabled = false;
38636     },
38637     
38638     onLoadException : function()
38639     {
38640         Roo.log(arguments);
38641         
38642         if (typeof(arguments[3]) != 'undefined') {
38643             Roo.MessageBox.alert("Error loading",arguments[3]);
38644         } 
38645         /*
38646         try {
38647             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38648                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38649             }   
38650         } catch(e) {
38651             
38652         }
38653         */
38654     
38655         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38656     },
38657     // private
38658     onLoad : function()
38659     {
38660         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38661     },
38662
38663     // private
38664     onBeforeLoad : function(){
38665         if(!this.disabled){
38666             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38667         }
38668     },
38669
38670     // private
38671     destroy : function(){
38672         if(this.store){
38673             this.store.un('beforeload', this.onBeforeLoad, this);
38674             this.store.un('load', this.onLoad, this);
38675             this.store.un('loadexception', this.onLoadException, this);
38676         }else{
38677             var um = this.el.getUpdateManager();
38678             um.un('beforeupdate', this.onBeforeLoad, this);
38679             um.un('update', this.onLoad, this);
38680             um.un('failure', this.onLoad, this);
38681         }
38682     }
38683 };/*
38684  * Based on:
38685  * Ext JS Library 1.1.1
38686  * Copyright(c) 2006-2007, Ext JS, LLC.
38687  *
38688  * Originally Released Under LGPL - original licence link has changed is not relivant.
38689  *
38690  * Fork - LGPL
38691  * <script type="text/javascript">
38692  */
38693
38694
38695 /**
38696  * @class Roo.XTemplate
38697  * @extends Roo.Template
38698  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38699 <pre><code>
38700 var t = new Roo.XTemplate(
38701         '&lt;select name="{name}"&gt;',
38702                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38703         '&lt;/select&gt;'
38704 );
38705  
38706 // then append, applying the master template values
38707  </code></pre>
38708  *
38709  * Supported features:
38710  *
38711  *  Tags:
38712
38713 <pre><code>
38714       {a_variable} - output encoded.
38715       {a_variable.format:("Y-m-d")} - call a method on the variable
38716       {a_variable:raw} - unencoded output
38717       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38718       {a_variable:this.method_on_template(...)} - call a method on the template object.
38719  
38720 </code></pre>
38721  *  The tpl tag:
38722 <pre><code>
38723         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38724         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38725         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38726         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38727   
38728         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38729         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38730 </code></pre>
38731  *      
38732  */
38733 Roo.XTemplate = function()
38734 {
38735     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38736     if (this.html) {
38737         this.compile();
38738     }
38739 };
38740
38741
38742 Roo.extend(Roo.XTemplate, Roo.Template, {
38743
38744     /**
38745      * The various sub templates
38746      */
38747     tpls : false,
38748     /**
38749      *
38750      * basic tag replacing syntax
38751      * WORD:WORD()
38752      *
38753      * // you can fake an object call by doing this
38754      *  x.t:(test,tesT) 
38755      * 
38756      */
38757     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38758
38759     /**
38760      * compile the template
38761      *
38762      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38763      *
38764      */
38765     compile: function()
38766     {
38767         var s = this.html;
38768      
38769         s = ['<tpl>', s, '</tpl>'].join('');
38770     
38771         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38772             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38773             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38774             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38775             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38776             m,
38777             id     = 0,
38778             tpls   = [];
38779     
38780         while(true == !!(m = s.match(re))){
38781             var forMatch   = m[0].match(nameRe),
38782                 ifMatch   = m[0].match(ifRe),
38783                 execMatch   = m[0].match(execRe),
38784                 namedMatch   = m[0].match(namedRe),
38785                 
38786                 exp  = null, 
38787                 fn   = null,
38788                 exec = null,
38789                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38790                 
38791             if (ifMatch) {
38792                 // if - puts fn into test..
38793                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38794                 if(exp){
38795                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38796                 }
38797             }
38798             
38799             if (execMatch) {
38800                 // exec - calls a function... returns empty if true is  returned.
38801                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38802                 if(exp){
38803                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38804                 }
38805             }
38806             
38807             
38808             if (name) {
38809                 // for = 
38810                 switch(name){
38811                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38812                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38813                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38814                 }
38815             }
38816             var uid = namedMatch ? namedMatch[1] : id;
38817             
38818             
38819             tpls.push({
38820                 id:     namedMatch ? namedMatch[1] : id,
38821                 target: name,
38822                 exec:   exec,
38823                 test:   fn,
38824                 body:   m[1] || ''
38825             });
38826             if (namedMatch) {
38827                 s = s.replace(m[0], '');
38828             } else { 
38829                 s = s.replace(m[0], '{xtpl'+ id + '}');
38830             }
38831             ++id;
38832         }
38833         this.tpls = [];
38834         for(var i = tpls.length-1; i >= 0; --i){
38835             this.compileTpl(tpls[i]);
38836             this.tpls[tpls[i].id] = tpls[i];
38837         }
38838         this.master = tpls[tpls.length-1];
38839         return this;
38840     },
38841     /**
38842      * same as applyTemplate, except it's done to one of the subTemplates
38843      * when using named templates, you can do:
38844      *
38845      * var str = pl.applySubTemplate('your-name', values);
38846      *
38847      * 
38848      * @param {Number} id of the template
38849      * @param {Object} values to apply to template
38850      * @param {Object} parent (normaly the instance of this object)
38851      */
38852     applySubTemplate : function(id, values, parent)
38853     {
38854         
38855         
38856         var t = this.tpls[id];
38857         
38858         
38859         try { 
38860             if(t.test && !t.test.call(this, values, parent)){
38861                 return '';
38862             }
38863         } catch(e) {
38864             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38865             Roo.log(e.toString());
38866             Roo.log(t.test);
38867             return ''
38868         }
38869         try { 
38870             
38871             if(t.exec && t.exec.call(this, values, parent)){
38872                 return '';
38873             }
38874         } catch(e) {
38875             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38876             Roo.log(e.toString());
38877             Roo.log(t.exec);
38878             return ''
38879         }
38880         try {
38881             var vs = t.target ? t.target.call(this, values, parent) : values;
38882             parent = t.target ? values : parent;
38883             if(t.target && vs instanceof Array){
38884                 var buf = [];
38885                 for(var i = 0, len = vs.length; i < len; i++){
38886                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38887                 }
38888                 return buf.join('');
38889             }
38890             return t.compiled.call(this, vs, parent);
38891         } catch (e) {
38892             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38893             Roo.log(e.toString());
38894             Roo.log(t.compiled);
38895             return '';
38896         }
38897     },
38898
38899     compileTpl : function(tpl)
38900     {
38901         var fm = Roo.util.Format;
38902         var useF = this.disableFormats !== true;
38903         var sep = Roo.isGecko ? "+" : ",";
38904         var undef = function(str) {
38905             Roo.log("Property not found :"  + str);
38906             return '';
38907         };
38908         
38909         var fn = function(m, name, format, args)
38910         {
38911             //Roo.log(arguments);
38912             args = args ? args.replace(/\\'/g,"'") : args;
38913             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38914             if (typeof(format) == 'undefined') {
38915                 format= 'htmlEncode';
38916             }
38917             if (format == 'raw' ) {
38918                 format = false;
38919             }
38920             
38921             if(name.substr(0, 4) == 'xtpl'){
38922                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38923             }
38924             
38925             // build an array of options to determine if value is undefined..
38926             
38927             // basically get 'xxxx.yyyy' then do
38928             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38929             //    (function () { Roo.log("Property not found"); return ''; })() :
38930             //    ......
38931             
38932             var udef_ar = [];
38933             var lookfor = '';
38934             Roo.each(name.split('.'), function(st) {
38935                 lookfor += (lookfor.length ? '.': '') + st;
38936                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38937             });
38938             
38939             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38940             
38941             
38942             if(format && useF){
38943                 
38944                 args = args ? ',' + args : "";
38945                  
38946                 if(format.substr(0, 5) != "this."){
38947                     format = "fm." + format + '(';
38948                 }else{
38949                     format = 'this.call("'+ format.substr(5) + '", ';
38950                     args = ", values";
38951                 }
38952                 
38953                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38954             }
38955              
38956             if (args.length) {
38957                 // called with xxyx.yuu:(test,test)
38958                 // change to ()
38959                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38960             }
38961             // raw.. - :raw modifier..
38962             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38963             
38964         };
38965         var body;
38966         // branched to use + in gecko and [].join() in others
38967         if(Roo.isGecko){
38968             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38969                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38970                     "';};};";
38971         }else{
38972             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38973             body.push(tpl.body.replace(/(\r\n|\n)/g,
38974                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38975             body.push("'].join('');};};");
38976             body = body.join('');
38977         }
38978         
38979         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38980        
38981         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38982         eval(body);
38983         
38984         return this;
38985     },
38986
38987     applyTemplate : function(values){
38988         return this.master.compiled.call(this, values, {});
38989         //var s = this.subs;
38990     },
38991
38992     apply : function(){
38993         return this.applyTemplate.apply(this, arguments);
38994     }
38995
38996  });
38997
38998 Roo.XTemplate.from = function(el){
38999     el = Roo.getDom(el);
39000     return new Roo.XTemplate(el.value || el.innerHTML);
39001 };