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      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Object} an existing reader (eg. copied from another store)
1092  * @cfg {Array} data The multi-dimensional array of data
1093  * @constructor
1094  * @param {Object} config
1095  */
1096 Roo.data.SimpleStore = function(config)
1097 {
1098     Roo.data.SimpleStore.superclass.constructor.call(this, {
1099         isLocal : true,
1100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1101                 id: config.id
1102             },
1103             Roo.data.Record.create(config.fields)
1104         ),
1105         proxy : new Roo.data.MemoryProxy(config.data)
1106     });
1107     this.load();
1108 };
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1110  * Based on:
1111  * Ext JS Library 1.1.1
1112  * Copyright(c) 2006-2007, Ext JS, LLC.
1113  *
1114  * Originally Released Under LGPL - original licence link has changed is not relivant.
1115  *
1116  * Fork - LGPL
1117  * <script type="text/javascript">
1118  */
1119
1120 /**
1121 /**
1122  * @extends Roo.data.Store
1123  * @class Roo.data.JsonStore
1124  * Small helper class to make creating Stores for JSON data easier. <br/>
1125 <pre><code>
1126 var store = new Roo.data.JsonStore({
1127     url: 'get-images.php',
1128     root: 'images',
1129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 });
1131 </code></pre>
1132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133  * JsonReader and HttpProxy (unless inline data is provided).</b>
1134  * @cfg {Array} fields An array of field definition objects, or field name strings.
1135  * @constructor
1136  * @param {Object} config
1137  */
1138 Roo.data.JsonStore = function(c){
1139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141         reader: new Roo.data.JsonReader(c, c.fields)
1142     }));
1143 };
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1145  * Based on:
1146  * Ext JS Library 1.1.1
1147  * Copyright(c) 2006-2007, Ext JS, LLC.
1148  *
1149  * Originally Released Under LGPL - original licence link has changed is not relivant.
1150  *
1151  * Fork - LGPL
1152  * <script type="text/javascript">
1153  */
1154
1155  
1156 Roo.data.Field = function(config){
1157     if(typeof config == "string"){
1158         config = {name: config};
1159     }
1160     Roo.apply(this, config);
1161     
1162     if(!this.type){
1163         this.type = "auto";
1164     }
1165     
1166     var st = Roo.data.SortTypes;
1167     // named sortTypes are supported, here we look them up
1168     if(typeof this.sortType == "string"){
1169         this.sortType = st[this.sortType];
1170     }
1171     
1172     // set default sortType for strings and dates
1173     if(!this.sortType){
1174         switch(this.type){
1175             case "string":
1176                 this.sortType = st.asUCString;
1177                 break;
1178             case "date":
1179                 this.sortType = st.asDate;
1180                 break;
1181             default:
1182                 this.sortType = st.none;
1183         }
1184     }
1185
1186     // define once
1187     var stripRe = /[\$,%]/g;
1188
1189     // prebuilt conversion function for this field, instead of
1190     // switching every time we're reading a value
1191     if(!this.convert){
1192         var cv, dateFormat = this.dateFormat;
1193         switch(this.type){
1194             case "":
1195             case "auto":
1196             case undefined:
1197                 cv = function(v){ return v; };
1198                 break;
1199             case "string":
1200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1201                 break;
1202             case "int":
1203                 cv = function(v){
1204                     return v !== undefined && v !== null && v !== '' ?
1205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1206                     };
1207                 break;
1208             case "float":
1209                 cv = function(v){
1210                     return v !== undefined && v !== null && v !== '' ?
1211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1212                     };
1213                 break;
1214             case "bool":
1215             case "boolean":
1216                 cv = function(v){ return v === true || v === "true" || v == 1; };
1217                 break;
1218             case "date":
1219                 cv = function(v){
1220                     if(!v){
1221                         return '';
1222                     }
1223                     if(v instanceof Date){
1224                         return v;
1225                     }
1226                     if(dateFormat){
1227                         if(dateFormat == "timestamp"){
1228                             return new Date(v*1000);
1229                         }
1230                         return Date.parseDate(v, dateFormat);
1231                     }
1232                     var parsed = Date.parse(v);
1233                     return parsed ? new Date(parsed) : null;
1234                 };
1235              break;
1236             
1237         }
1238         this.convert = cv;
1239     }
1240 };
1241
1242 Roo.data.Field.prototype = {
1243     dateFormat: null,
1244     defaultValue: "",
1245     mapping: null,
1246     sortType : null,
1247     sortDir : "ASC"
1248 };/*
1249  * Based on:
1250  * Ext JS Library 1.1.1
1251  * Copyright(c) 2006-2007, Ext JS, LLC.
1252  *
1253  * Originally Released Under LGPL - original licence link has changed is not relivant.
1254  *
1255  * Fork - LGPL
1256  * <script type="text/javascript">
1257  */
1258  
1259 // Base class for reading structured data from a data source.  This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261
1262 /**
1263  * @class Roo.data.DataReader
1264  * Base class for reading structured data from a data source.  This class is intended to be
1265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266  */
1267
1268 Roo.data.DataReader = function(meta, recordType){
1269     
1270     this.meta = meta;
1271     
1272     this.recordType = recordType instanceof Array ? 
1273         Roo.data.Record.create(recordType) : recordType;
1274 };
1275
1276 Roo.data.DataReader.prototype = {
1277     
1278     
1279     readerType : 'Data',
1280      /**
1281      * Create an empty record
1282      * @param {Object} data (optional) - overlay some values
1283      * @return {Roo.data.Record} record created.
1284      */
1285     newRow :  function(d) {
1286         var da =  {};
1287         this.recordType.prototype.fields.each(function(c) {
1288             switch( c.type) {
1289                 case 'int' : da[c.name] = 0; break;
1290                 case 'date' : da[c.name] = new Date(); break;
1291                 case 'float' : da[c.name] = 0.0; break;
1292                 case 'boolean' : da[c.name] = false; break;
1293                 default : da[c.name] = ""; break;
1294             }
1295             
1296         });
1297         return new this.recordType(Roo.apply(da, d));
1298     }
1299     
1300     
1301 };/*
1302  * Based on:
1303  * Ext JS Library 1.1.1
1304  * Copyright(c) 2006-2007, Ext JS, LLC.
1305  *
1306  * Originally Released Under LGPL - original licence link has changed is not relivant.
1307  *
1308  * Fork - LGPL
1309  * <script type="text/javascript">
1310  */
1311
1312 /**
1313  * @class Roo.data.DataProxy
1314  * @extends Roo.data.Observable
1315  * This class is an abstract base class for implementations which provide retrieval of
1316  * unformatted data objects.<br>
1317  * <p>
1318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319  * (of the appropriate type which knows how to parse the data object) to provide a block of
1320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1321  * <p>
1322  * Custom implementations must implement the load method as described in
1323  * {@link Roo.data.HttpProxy#load}.
1324  */
1325 Roo.data.DataProxy = function(){
1326     this.addEvents({
1327         /**
1328          * @event beforeload
1329          * Fires before a network request is made to retrieve a data object.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} params The params parameter to the load function.
1332          */
1333         beforeload : true,
1334         /**
1335          * @event load
1336          * Fires before the load method's callback is called.
1337          * @param {Object} This DataProxy object.
1338          * @param {Object} o The data object.
1339          * @param {Object} arg The callback argument object passed to the load function.
1340          */
1341         load : true,
1342         /**
1343          * @event loadexception
1344          * Fires if an Exception occurs during data retrieval.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} o The data object.
1347          * @param {Object} arg The callback argument object passed to the load function.
1348          * @param {Object} e The Exception.
1349          */
1350         loadexception : true
1351     });
1352     Roo.data.DataProxy.superclass.constructor.call(this);
1353 };
1354
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1356
1357     /**
1358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1359      */
1360 /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370 /**
1371  * @class Roo.data.MemoryProxy
1372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373  * to the Reader when its load method is called.
1374  * @constructor
1375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1376  */
1377 Roo.data.MemoryProxy = function(data){
1378     if (data.data) {
1379         data = data.data;
1380     }
1381     Roo.data.MemoryProxy.superclass.constructor.call(this);
1382     this.data = data;
1383 };
1384
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1386     
1387     /**
1388      * Load data from the requested source (in this case an in-memory
1389      * data object passed to the constructor), read the data object into
1390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391      * process that block using the passed callback.
1392      * @param {Object} params This parameter is not used by the MemoryProxy class.
1393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394      * object into a block of Roo.data.Records.
1395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396      * The function must be passed <ul>
1397      * <li>The Record block object</li>
1398      * <li>The "arg" argument from the load function</li>
1399      * <li>A boolean success indicator</li>
1400      * </ul>
1401      * @param {Object} scope The scope in which to call the callback
1402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1403      */
1404     load : function(params, reader, callback, scope, arg){
1405         params = params || {};
1406         var result;
1407         try {
1408             result = reader.readRecords(params.data ? params.data :this.data);
1409         }catch(e){
1410             this.fireEvent("loadexception", this, arg, null, e);
1411             callback.call(scope, null, arg, false);
1412             return;
1413         }
1414         callback.call(scope, result, arg, true);
1415     },
1416     
1417     // private
1418     update : function(params, records){
1419         
1420     }
1421 });/*
1422  * Based on:
1423  * Ext JS Library 1.1.1
1424  * Copyright(c) 2006-2007, Ext JS, LLC.
1425  *
1426  * Originally Released Under LGPL - original licence link has changed is not relivant.
1427  *
1428  * Fork - LGPL
1429  * <script type="text/javascript">
1430  */
1431 /**
1432  * @class Roo.data.HttpProxy
1433  * @extends Roo.data.DataProxy
1434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435  * configured to reference a certain URL.<br><br>
1436  * <p>
1437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438  * from which the running page was served.<br><br>
1439  * <p>
1440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1441  * <p>
1442  * Be aware that to enable the browser to parse an XML document, the server must set
1443  * the Content-Type header in the HTTP response to "text/xml".
1444  * @constructor
1445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447  * will be used to make the request.
1448  */
1449 Roo.data.HttpProxy = function(conn){
1450     Roo.data.HttpProxy.superclass.constructor.call(this);
1451     // is conn a conn config or a real conn?
1452     this.conn = conn;
1453     this.useAjax = !conn || !conn.events;
1454   
1455 };
1456
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458     // thse are take from connection...
1459     
1460     /**
1461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1462      */
1463     /**
1464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465      * extra parameters to each request made by this object. (defaults to undefined)
1466      */
1467     /**
1468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469      *  to each request made by this object. (defaults to undefined)
1470      */
1471     /**
1472      * @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)
1473      */
1474     /**
1475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1476      */
1477      /**
1478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1479      * @type Boolean
1480      */
1481   
1482
1483     /**
1484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1485      * @type Boolean
1486      */
1487     /**
1488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490      * a finer-grained basis than the DataProxy events.
1491      */
1492     getConnection : function(){
1493         return this.useAjax ? Roo.Ajax : this.conn;
1494     },
1495
1496     /**
1497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499      * process that block using the passed callback.
1500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501      * for the request to the remote server.
1502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503      * object into a block of Roo.data.Records.
1504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505      * The function must be passed <ul>
1506      * <li>The Record block object</li>
1507      * <li>The "arg" argument from the load function</li>
1508      * <li>A boolean success indicator</li>
1509      * </ul>
1510      * @param {Object} scope The scope in which to call the callback
1511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1512      */
1513     load : function(params, reader, callback, scope, arg){
1514         if(this.fireEvent("beforeload", this, params) !== false){
1515             var  o = {
1516                 params : params || {},
1517                 request: {
1518                     callback : callback,
1519                     scope : scope,
1520                     arg : arg
1521                 },
1522                 reader: reader,
1523                 callback : this.loadResponse,
1524                 scope: this
1525             };
1526             if(this.useAjax){
1527                 Roo.applyIf(o, this.conn);
1528                 if(this.activeRequest){
1529                     Roo.Ajax.abort(this.activeRequest);
1530                 }
1531                 this.activeRequest = Roo.Ajax.request(o);
1532             }else{
1533                 this.conn.request(o);
1534             }
1535         }else{
1536             callback.call(scope||this, null, arg, false);
1537         }
1538     },
1539
1540     // private
1541     loadResponse : function(o, success, response){
1542         delete this.activeRequest;
1543         if(!success){
1544             this.fireEvent("loadexception", this, o, response);
1545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1546             return;
1547         }
1548         var result;
1549         try {
1550             result = o.reader.read(response);
1551         }catch(e){
1552             this.fireEvent("loadexception", this, o, response, e);
1553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1554             return;
1555         }
1556         
1557         this.fireEvent("load", this, o, o.request.arg);
1558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1559     },
1560
1561     // private
1562     update : function(dataSet){
1563
1564     },
1565
1566     // private
1567     updateResponse : function(dataSet){
1568
1569     }
1570 });/*
1571  * Based on:
1572  * Ext JS Library 1.1.1
1573  * Copyright(c) 2006-2007, Ext JS, LLC.
1574  *
1575  * Originally Released Under LGPL - original licence link has changed is not relivant.
1576  *
1577  * Fork - LGPL
1578  * <script type="text/javascript">
1579  */
1580
1581 /**
1582  * @class Roo.data.ScriptTagProxy
1583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584  * other than the originating domain of the running page.<br><br>
1585  * <p>
1586  * <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
1587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1588  * <p>
1589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590  * source code that is used as the source inside a &lt;script> tag.<br><br>
1591  * <p>
1592  * In order for the browser to process the returned data, the server must wrap the data object
1593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595  * depending on whether the callback name was passed:
1596  * <p>
1597  * <pre><code>
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1600 if (cb != null) {
1601     scriptTag = true;
1602     response.setContentType("text/javascript");
1603 } else {
1604     response.setContentType("application/x-json");
1605 }
1606 Writer out = response.getWriter();
1607 if (scriptTag) {
1608     out.write(cb + "(");
1609 }
1610 out.print(dataBlock.toJsonString());
1611 if (scriptTag) {
1612     out.write(");");
1613 }
1614 </pre></code>
1615  *
1616  * @constructor
1617  * @param {Object} config A configuration object.
1618  */
1619 Roo.data.ScriptTagProxy = function(config){
1620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621     Roo.apply(this, config);
1622     this.head = document.getElementsByTagName("head")[0];
1623 };
1624
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1626
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1628     /**
1629      * @cfg {String} url The URL from which to request the data object.
1630      */
1631     /**
1632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1633      */
1634     timeout : 30000,
1635     /**
1636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637      * the server the name of the callback function set up by the load call to process the returned data object.
1638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639      * javascript output which calls this named function passing the data object as its only parameter.
1640      */
1641     callbackParam : "callback",
1642     /**
1643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644      * name to the request.
1645      */
1646     nocache : true,
1647
1648     /**
1649      * Load data from the configured URL, read the data object into
1650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651      * process that block using the passed callback.
1652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653      * for the request to the remote server.
1654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655      * object into a block of Roo.data.Records.
1656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657      * The function must be passed <ul>
1658      * <li>The Record block object</li>
1659      * <li>The "arg" argument from the load function</li>
1660      * <li>A boolean success indicator</li>
1661      * </ul>
1662      * @param {Object} scope The scope in which to call the callback
1663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1664      */
1665     load : function(params, reader, callback, scope, arg){
1666         if(this.fireEvent("beforeload", this, params) !== false){
1667
1668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1669
1670             var url = this.url;
1671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1672             if(this.nocache){
1673                 url += "&_dc=" + (new Date().getTime());
1674             }
1675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1676             var trans = {
1677                 id : transId,
1678                 cb : "stcCallback"+transId,
1679                 scriptId : "stcScript"+transId,
1680                 params : params,
1681                 arg : arg,
1682                 url : url,
1683                 callback : callback,
1684                 scope : scope,
1685                 reader : reader
1686             };
1687             var conn = this;
1688
1689             window[trans.cb] = function(o){
1690                 conn.handleResponse(o, trans);
1691             };
1692
1693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1694
1695             if(this.autoAbort !== false){
1696                 this.abort();
1697             }
1698
1699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1700
1701             var script = document.createElement("script");
1702             script.setAttribute("src", url);
1703             script.setAttribute("type", "text/javascript");
1704             script.setAttribute("id", trans.scriptId);
1705             this.head.appendChild(script);
1706
1707             this.trans = trans;
1708         }else{
1709             callback.call(scope||this, null, arg, false);
1710         }
1711     },
1712
1713     // private
1714     isLoading : function(){
1715         return this.trans ? true : false;
1716     },
1717
1718     /**
1719      * Abort the current server request.
1720      */
1721     abort : function(){
1722         if(this.isLoading()){
1723             this.destroyTrans(this.trans);
1724         }
1725     },
1726
1727     // private
1728     destroyTrans : function(trans, isLoaded){
1729         this.head.removeChild(document.getElementById(trans.scriptId));
1730         clearTimeout(trans.timeoutId);
1731         if(isLoaded){
1732             window[trans.cb] = undefined;
1733             try{
1734                 delete window[trans.cb];
1735             }catch(e){}
1736         }else{
1737             // if hasn't been loaded, wait for load to remove it to prevent script error
1738             window[trans.cb] = function(){
1739                 window[trans.cb] = undefined;
1740                 try{
1741                     delete window[trans.cb];
1742                 }catch(e){}
1743             };
1744         }
1745     },
1746
1747     // private
1748     handleResponse : function(o, trans){
1749         this.trans = false;
1750         this.destroyTrans(trans, true);
1751         var result;
1752         try {
1753             result = trans.reader.readRecords(o);
1754         }catch(e){
1755             this.fireEvent("loadexception", this, o, trans.arg, e);
1756             trans.callback.call(trans.scope||window, null, trans.arg, false);
1757             return;
1758         }
1759         this.fireEvent("load", this, o, trans.arg);
1760         trans.callback.call(trans.scope||window, result, trans.arg, true);
1761     },
1762
1763     // private
1764     handleFailure : function(trans){
1765         this.trans = false;
1766         this.destroyTrans(trans, false);
1767         this.fireEvent("loadexception", this, null, trans.arg);
1768         trans.callback.call(trans.scope||window, null, trans.arg, false);
1769     }
1770 });/*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.data.JsonReader
1783  * @extends Roo.data.DataReader
1784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785  * based on mappings in a provided Roo.data.Record constructor.
1786  * 
1787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788  * in the reply previously. 
1789  * 
1790  * <p>
1791  * Example code:
1792  * <pre><code>
1793 var RecordDef = Roo.data.Record.create([
1794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1796 ]);
1797 var myReader = new Roo.data.JsonReader({
1798     totalProperty: "results",    // The property which contains the total dataset size (optional)
1799     root: "rows",                // The property which contains an Array of row objects
1800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1801 }, RecordDef);
1802 </code></pre>
1803  * <p>
1804  * This would consume a JSON file like this:
1805  * <pre><code>
1806 { 'results': 2, 'rows': [
1807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1809 }
1810 </code></pre>
1811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813  * paged from the remote server.
1814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815  * @cfg {String} root name of the property which contains the Array of row objects.
1816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817  * @cfg {Array} fields Array of field definition objects
1818  * @constructor
1819  * Create a new JsonReader
1820  * @param {Object} meta Metadata configuration options
1821  * @param {Object} recordType Either an Array of field definition objects,
1822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1823  */
1824 Roo.data.JsonReader = function(meta, recordType){
1825     
1826     meta = meta || {};
1827     // set some defaults:
1828     Roo.applyIf(meta, {
1829         totalProperty: 'total',
1830         successProperty : 'success',
1831         root : 'data',
1832         id : 'id'
1833     });
1834     
1835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1836 };
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1838     
1839     readerType : 'Json',
1840     
1841     /**
1842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1843      * Used by Store query builder to append _requestMeta to params.
1844      * 
1845      */
1846     metaFromRemote : false,
1847     /**
1848      * This method is only used by a DataProxy which has retrieved data from a remote server.
1849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850      * @return {Object} data A data block which is used by an Roo.data.Store object as
1851      * a cache of Roo.data.Records.
1852      */
1853     read : function(response){
1854         var json = response.responseText;
1855        
1856         var o = /* eval:var:o */ eval("("+json+")");
1857         if(!o) {
1858             throw {message: "JsonReader.read: Json object not found"};
1859         }
1860         
1861         if(o.metaData){
1862             
1863             delete this.ef;
1864             this.metaFromRemote = true;
1865             this.meta = o.metaData;
1866             this.recordType = Roo.data.Record.create(o.metaData.fields);
1867             this.onMetaChange(this.meta, this.recordType, o);
1868         }
1869         return this.readRecords(o);
1870     },
1871
1872     // private function a store will implement
1873     onMetaChange : function(meta, recordType, o){
1874
1875     },
1876
1877     /**
1878          * @ignore
1879          */
1880     simpleAccess: function(obj, subsc) {
1881         return obj[subsc];
1882     },
1883
1884         /**
1885          * @ignore
1886          */
1887     getJsonAccessor: function(){
1888         var re = /[\[\.]/;
1889         return function(expr) {
1890             try {
1891                 return(re.test(expr))
1892                     ? new Function("obj", "return obj." + expr)
1893                     : function(obj){
1894                         return obj[expr];
1895                     };
1896             } catch(e){}
1897             return Roo.emptyFn;
1898         };
1899     }(),
1900
1901     /**
1902      * Create a data block containing Roo.data.Records from an XML document.
1903      * @param {Object} o An object which contains an Array of row objects in the property specified
1904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905      * which contains the total size of the dataset.
1906      * @return {Object} data A data block which is used by an Roo.data.Store object as
1907      * a cache of Roo.data.Records.
1908      */
1909     readRecords : function(o){
1910         /**
1911          * After any data loads, the raw JSON data is available for further custom processing.
1912          * @type Object
1913          */
1914         this.o = o;
1915         var s = this.meta, Record = this.recordType,
1916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1917
1918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1919         if (!this.ef) {
1920             if(s.totalProperty) {
1921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1922                 }
1923                 if(s.successProperty) {
1924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1925                 }
1926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1927                 if (s.id) {
1928                         var g = this.getJsonAccessor(s.id);
1929                         this.getId = function(rec) {
1930                                 var r = g(rec);  
1931                                 return (r === undefined || r === "") ? null : r;
1932                         };
1933                 } else {
1934                         this.getId = function(){return null;};
1935                 }
1936             this.ef = [];
1937             for(var jj = 0; jj < fl; jj++){
1938                 f = fi[jj];
1939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940                 this.ef[jj] = this.getJsonAccessor(map);
1941             }
1942         }
1943
1944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945         if(s.totalProperty){
1946             var vt = parseInt(this.getTotal(o), 10);
1947             if(!isNaN(vt)){
1948                 totalRecords = vt;
1949             }
1950         }
1951         if(s.successProperty){
1952             var vs = this.getSuccess(o);
1953             if(vs === false || vs === 'false'){
1954                 success = false;
1955             }
1956         }
1957         var records = [];
1958         for(var i = 0; i < c; i++){
1959                 var n = root[i];
1960             var values = {};
1961             var id = this.getId(n);
1962             for(var j = 0; j < fl; j++){
1963                 f = fi[j];
1964             var v = this.ef[j](n);
1965             if (!f.convert) {
1966                 Roo.log('missing convert for ' + f.name);
1967                 Roo.log(f);
1968                 continue;
1969             }
1970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1971             }
1972             var record = new Record(values, id);
1973             record.json = n;
1974             records[i] = record;
1975         }
1976         return {
1977             raw : o,
1978             success : success,
1979             records : records,
1980             totalRecords : totalRecords
1981         };
1982     }
1983 });/*
1984  * Based on:
1985  * Ext JS Library 1.1.1
1986  * Copyright(c) 2006-2007, Ext JS, LLC.
1987  *
1988  * Originally Released Under LGPL - original licence link has changed is not relivant.
1989  *
1990  * Fork - LGPL
1991  * <script type="text/javascript">
1992  */
1993
1994 /**
1995  * @class Roo.data.XmlReader
1996  * @extends Roo.data.DataReader
1997  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1998  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1999  * <p>
2000  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2001  * header in the HTTP response must be set to "text/xml".</em>
2002  * <p>
2003  * Example code:
2004  * <pre><code>
2005 var RecordDef = Roo.data.Record.create([
2006    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2007    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2008 ]);
2009 var myReader = new Roo.data.XmlReader({
2010    totalRecords: "results", // The element which contains the total dataset size (optional)
2011    record: "row",           // The repeated element which contains row information
2012    id: "id"                 // The element within the row that provides an ID for the record (optional)
2013 }, RecordDef);
2014 </code></pre>
2015  * <p>
2016  * This would consume an XML file like this:
2017  * <pre><code>
2018 &lt;?xml?>
2019 &lt;dataset>
2020  &lt;results>2&lt;/results>
2021  &lt;row>
2022    &lt;id>1&lt;/id>
2023    &lt;name>Bill&lt;/name>
2024    &lt;occupation>Gardener&lt;/occupation>
2025  &lt;/row>
2026  &lt;row>
2027    &lt;id>2&lt;/id>
2028    &lt;name>Ben&lt;/name>
2029    &lt;occupation>Horticulturalist&lt;/occupation>
2030  &lt;/row>
2031 &lt;/dataset>
2032 </code></pre>
2033  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2034  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2035  * paged from the remote server.
2036  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2037  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2038  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2039  * a record identifier value.
2040  * @constructor
2041  * Create a new XmlReader
2042  * @param {Object} meta Metadata configuration options
2043  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2044  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2045  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2046  */
2047 Roo.data.XmlReader = function(meta, recordType){
2048     meta = meta || {};
2049     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2050 };
2051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2052     /**
2053      * This method is only used by a DataProxy which has retrieved data from a remote server.
2054          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2055          * to contain a method called 'responseXML' that returns an XML document object.
2056      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2057      * a cache of Roo.data.Records.
2058      */
2059     read : function(response){
2060         var doc = response.responseXML;
2061         if(!doc) {
2062             throw {message: "XmlReader.read: XML Document not available"};
2063         }
2064         return this.readRecords(doc);
2065     },
2066
2067     /**
2068      * Create a data block containing Roo.data.Records from an XML document.
2069          * @param {Object} doc A parsed XML document.
2070      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2071      * a cache of Roo.data.Records.
2072      */
2073     readRecords : function(doc){
2074         /**
2075          * After any data loads/reads, the raw XML Document is available for further custom processing.
2076          * @type XMLDocument
2077          */
2078         this.xmlData = doc;
2079         var root = doc.documentElement || doc;
2080         var q = Roo.DomQuery;
2081         var recordType = this.recordType, fields = recordType.prototype.fields;
2082         var sid = this.meta.id;
2083         var totalRecords = 0, success = true;
2084         if(this.meta.totalRecords){
2085             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2086         }
2087         
2088         if(this.meta.success){
2089             var sv = q.selectValue(this.meta.success, root, true);
2090             success = sv !== false && sv !== 'false';
2091         }
2092         var records = [];
2093         var ns = q.select(this.meta.record, root);
2094         for(var i = 0, len = ns.length; i < len; i++) {
2095                 var n = ns[i];
2096                 var values = {};
2097                 var id = sid ? q.selectValue(sid, n) : undefined;
2098                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2099                     var f = fields.items[j];
2100                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2101                     v = f.convert(v);
2102                     values[f.name] = v;
2103                 }
2104                 var record = new recordType(values, id);
2105                 record.node = n;
2106                 records[records.length] = record;
2107             }
2108
2109             return {
2110                 success : success,
2111                 records : records,
2112                 totalRecords : totalRecords || records.length
2113             };
2114     }
2115 });/*
2116  * Based on:
2117  * Ext JS Library 1.1.1
2118  * Copyright(c) 2006-2007, Ext JS, LLC.
2119  *
2120  * Originally Released Under LGPL - original licence link has changed is not relivant.
2121  *
2122  * Fork - LGPL
2123  * <script type="text/javascript">
2124  */
2125
2126 /**
2127  * @class Roo.data.ArrayReader
2128  * @extends Roo.data.DataReader
2129  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2130  * Each element of that Array represents a row of data fields. The
2131  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2132  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2133  * <p>
2134  * Example code:.
2135  * <pre><code>
2136 var RecordDef = Roo.data.Record.create([
2137     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2138     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2139 ]);
2140 var myReader = new Roo.data.ArrayReader({
2141     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2142 }, RecordDef);
2143 </code></pre>
2144  * <p>
2145  * This would consume an Array like this:
2146  * <pre><code>
2147 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2148   </code></pre>
2149  
2150  * @constructor
2151  * Create a new JsonReader
2152  * @param {Object} meta Metadata configuration options.
2153  * @param {Object|Array} recordType Either an Array of field definition objects
2154  * 
2155  * @cfg {Array} fields Array of field definition objects
2156  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2157  * as specified to {@link Roo.data.Record#create},
2158  * or an {@link Roo.data.Record} object
2159  *
2160  * 
2161  * created using {@link Roo.data.Record#create}.
2162  */
2163 Roo.data.ArrayReader = function(meta, recordType)
2164 {    
2165     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2166 };
2167
2168 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2169     /**
2170      * Create a data block containing Roo.data.Records from an XML document.
2171      * @param {Object} o An Array of row objects which represents the dataset.
2172      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2173      * a cache of Roo.data.Records.
2174      */
2175     readRecords : function(o)
2176     {
2177         var sid = this.meta ? this.meta.id : null;
2178         var recordType = this.recordType, fields = recordType.prototype.fields;
2179         var records = [];
2180         var root = o;
2181         for(var i = 0; i < root.length; i++){
2182                 var n = root[i];
2183             var values = {};
2184             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2185             for(var j = 0, jlen = fields.length; j < jlen; j++){
2186                 var f = fields.items[j];
2187                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2188                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2189                 v = f.convert(v);
2190                 values[f.name] = v;
2191             }
2192             var record = new recordType(values, id);
2193             record.json = n;
2194             records[records.length] = record;
2195         }
2196         return {
2197             records : records,
2198             totalRecords : records.length
2199         };
2200     }
2201 });/*
2202  * Based on:
2203  * Ext JS Library 1.1.1
2204  * Copyright(c) 2006-2007, Ext JS, LLC.
2205  *
2206  * Originally Released Under LGPL - original licence link has changed is not relivant.
2207  *
2208  * Fork - LGPL
2209  * <script type="text/javascript">
2210  */
2211
2212
2213 /**
2214  * @class Roo.data.Tree
2215  * @extends Roo.util.Observable
2216  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2217  * in the tree have most standard DOM functionality.
2218  * @constructor
2219  * @param {Node} root (optional) The root node
2220  */
2221 Roo.data.Tree = function(root){
2222    this.nodeHash = {};
2223    /**
2224     * The root node for this tree
2225     * @type Node
2226     */
2227    this.root = null;
2228    if(root){
2229        this.setRootNode(root);
2230    }
2231    this.addEvents({
2232        /**
2233         * @event append
2234         * Fires when a new child node is appended to a node in this tree.
2235         * @param {Tree} tree The owner tree
2236         * @param {Node} parent The parent node
2237         * @param {Node} node The newly appended node
2238         * @param {Number} index The index of the newly appended node
2239         */
2240        "append" : true,
2241        /**
2242         * @event remove
2243         * Fires when a child node is removed from a node in this tree.
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} parent The parent node
2246         * @param {Node} node The child node removed
2247         */
2248        "remove" : true,
2249        /**
2250         * @event move
2251         * Fires when a node is moved to a new location in the tree
2252         * @param {Tree} tree The owner tree
2253         * @param {Node} node The node moved
2254         * @param {Node} oldParent The old parent of this node
2255         * @param {Node} newParent The new parent of this node
2256         * @param {Number} index The index it was moved to
2257         */
2258        "move" : true,
2259        /**
2260         * @event insert
2261         * Fires when a new child node is inserted in a node in this tree.
2262         * @param {Tree} tree The owner tree
2263         * @param {Node} parent The parent node
2264         * @param {Node} node The child node inserted
2265         * @param {Node} refNode The child node the node was inserted before
2266         */
2267        "insert" : true,
2268        /**
2269         * @event beforeappend
2270         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be appended
2274         */
2275        "beforeappend" : true,
2276        /**
2277         * @event beforeremove
2278         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} parent The parent node
2281         * @param {Node} node The child node to be removed
2282         */
2283        "beforeremove" : true,
2284        /**
2285         * @event beforemove
2286         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2287         * @param {Tree} tree The owner tree
2288         * @param {Node} node The node being moved
2289         * @param {Node} oldParent The parent of the node
2290         * @param {Node} newParent The new parent the node is moving to
2291         * @param {Number} index The index it is being moved to
2292         */
2293        "beforemove" : true,
2294        /**
2295         * @event beforeinsert
2296         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2297         * @param {Tree} tree The owner tree
2298         * @param {Node} parent The parent node
2299         * @param {Node} node The child node to be inserted
2300         * @param {Node} refNode The child node the node is being inserted before
2301         */
2302        "beforeinsert" : true
2303    });
2304
2305     Roo.data.Tree.superclass.constructor.call(this);
2306 };
2307
2308 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2309     pathSeparator: "/",
2310
2311     proxyNodeEvent : function(){
2312         return this.fireEvent.apply(this, arguments);
2313     },
2314
2315     /**
2316      * Returns the root node for this tree.
2317      * @return {Node}
2318      */
2319     getRootNode : function(){
2320         return this.root;
2321     },
2322
2323     /**
2324      * Sets the root node for this tree.
2325      * @param {Node} node
2326      * @return {Node}
2327      */
2328     setRootNode : function(node){
2329         this.root = node;
2330         node.ownerTree = this;
2331         node.isRoot = true;
2332         this.registerNode(node);
2333         return node;
2334     },
2335
2336     /**
2337      * Gets a node in this tree by its id.
2338      * @param {String} id
2339      * @return {Node}
2340      */
2341     getNodeById : function(id){
2342         return this.nodeHash[id];
2343     },
2344
2345     registerNode : function(node){
2346         this.nodeHash[node.id] = node;
2347     },
2348
2349     unregisterNode : function(node){
2350         delete this.nodeHash[node.id];
2351     },
2352
2353     toString : function(){
2354         return "[Tree"+(this.id?" "+this.id:"")+"]";
2355     }
2356 });
2357
2358 /**
2359  * @class Roo.data.Node
2360  * @extends Roo.util.Observable
2361  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2362  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2363  * @constructor
2364  * @param {Object} attributes The attributes/config for the node
2365  */
2366 Roo.data.Node = function(attributes){
2367     /**
2368      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2369      * @type {Object}
2370      */
2371     this.attributes = attributes || {};
2372     this.leaf = this.attributes.leaf;
2373     /**
2374      * The node id. @type String
2375      */
2376     this.id = this.attributes.id;
2377     if(!this.id){
2378         this.id = Roo.id(null, "ynode-");
2379         this.attributes.id = this.id;
2380     }
2381      
2382     
2383     /**
2384      * All child nodes of this node. @type Array
2385      */
2386     this.childNodes = [];
2387     if(!this.childNodes.indexOf){ // indexOf is a must
2388         this.childNodes.indexOf = function(o){
2389             for(var i = 0, len = this.length; i < len; i++){
2390                 if(this[i] == o) {
2391                     return i;
2392                 }
2393             }
2394             return -1;
2395         };
2396     }
2397     /**
2398      * The parent node for this node. @type Node
2399      */
2400     this.parentNode = null;
2401     /**
2402      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2403      */
2404     this.firstChild = null;
2405     /**
2406      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2407      */
2408     this.lastChild = null;
2409     /**
2410      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2411      */
2412     this.previousSibling = null;
2413     /**
2414      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2415      */
2416     this.nextSibling = null;
2417
2418     this.addEvents({
2419        /**
2420         * @event append
2421         * Fires when a new child node is appended
2422         * @param {Tree} tree The owner tree
2423         * @param {Node} this This node
2424         * @param {Node} node The newly appended node
2425         * @param {Number} index The index of the newly appended node
2426         */
2427        "append" : true,
2428        /**
2429         * @event remove
2430         * Fires when a child node is removed
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} node The removed node
2434         */
2435        "remove" : true,
2436        /**
2437         * @event move
2438         * Fires when this node is moved to a new location in the tree
2439         * @param {Tree} tree The owner tree
2440         * @param {Node} this This node
2441         * @param {Node} oldParent The old parent of this node
2442         * @param {Node} newParent The new parent of this node
2443         * @param {Number} index The index it was moved to
2444         */
2445        "move" : true,
2446        /**
2447         * @event insert
2448         * Fires when a new child node is inserted.
2449         * @param {Tree} tree The owner tree
2450         * @param {Node} this This node
2451         * @param {Node} node The child node inserted
2452         * @param {Node} refNode The child node the node was inserted before
2453         */
2454        "insert" : true,
2455        /**
2456         * @event beforeappend
2457         * Fires before a new child is appended, return false to cancel the append.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be appended
2461         */
2462        "beforeappend" : true,
2463        /**
2464         * @event beforeremove
2465         * Fires before a child is removed, return false to cancel the remove.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} node The child node to be removed
2469         */
2470        "beforeremove" : true,
2471        /**
2472         * @event beforemove
2473         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2474         * @param {Tree} tree The owner tree
2475         * @param {Node} this This node
2476         * @param {Node} oldParent The parent of this node
2477         * @param {Node} newParent The new parent this node is moving to
2478         * @param {Number} index The index it is being moved to
2479         */
2480        "beforemove" : true,
2481        /**
2482         * @event beforeinsert
2483         * Fires before a new child is inserted, return false to cancel the insert.
2484         * @param {Tree} tree The owner tree
2485         * @param {Node} this This node
2486         * @param {Node} node The child node to be inserted
2487         * @param {Node} refNode The child node the node is being inserted before
2488         */
2489        "beforeinsert" : true
2490    });
2491     this.listeners = this.attributes.listeners;
2492     Roo.data.Node.superclass.constructor.call(this);
2493 };
2494
2495 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2496     fireEvent : function(evtName){
2497         // first do standard event for this node
2498         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2499             return false;
2500         }
2501         // then bubble it up to the tree if the event wasn't cancelled
2502         var ot = this.getOwnerTree();
2503         if(ot){
2504             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2505                 return false;
2506             }
2507         }
2508         return true;
2509     },
2510
2511     /**
2512      * Returns true if this node is a leaf
2513      * @return {Boolean}
2514      */
2515     isLeaf : function(){
2516         return this.leaf === true;
2517     },
2518
2519     // private
2520     setFirstChild : function(node){
2521         this.firstChild = node;
2522     },
2523
2524     //private
2525     setLastChild : function(node){
2526         this.lastChild = node;
2527     },
2528
2529
2530     /**
2531      * Returns true if this node is the last child of its parent
2532      * @return {Boolean}
2533      */
2534     isLast : function(){
2535        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2536     },
2537
2538     /**
2539      * Returns true if this node is the first child of its parent
2540      * @return {Boolean}
2541      */
2542     isFirst : function(){
2543        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2544     },
2545
2546     hasChildNodes : function(){
2547         return !this.isLeaf() && this.childNodes.length > 0;
2548     },
2549
2550     /**
2551      * Insert node(s) as the last child node of this node.
2552      * @param {Node/Array} node The node or Array of nodes to append
2553      * @return {Node} The appended node if single append, or null if an array was passed
2554      */
2555     appendChild : function(node){
2556         var multi = false;
2557         if(node instanceof Array){
2558             multi = node;
2559         }else if(arguments.length > 1){
2560             multi = arguments;
2561         }
2562         
2563         // if passed an array or multiple args do them one by one
2564         if(multi){
2565             for(var i = 0, len = multi.length; i < len; i++) {
2566                 this.appendChild(multi[i]);
2567             }
2568         }else{
2569             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2570                 return false;
2571             }
2572             var index = this.childNodes.length;
2573             var oldParent = node.parentNode;
2574             // it's a move, make sure we move it cleanly
2575             if(oldParent){
2576                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2577                     return false;
2578                 }
2579                 oldParent.removeChild(node);
2580             }
2581             
2582             index = this.childNodes.length;
2583             if(index == 0){
2584                 this.setFirstChild(node);
2585             }
2586             this.childNodes.push(node);
2587             node.parentNode = this;
2588             var ps = this.childNodes[index-1];
2589             if(ps){
2590                 node.previousSibling = ps;
2591                 ps.nextSibling = node;
2592             }else{
2593                 node.previousSibling = null;
2594             }
2595             node.nextSibling = null;
2596             this.setLastChild(node);
2597             node.setOwnerTree(this.getOwnerTree());
2598             this.fireEvent("append", this.ownerTree, this, node, index);
2599             if(this.ownerTree) {
2600                 this.ownerTree.fireEvent("appendnode", this, node, index);
2601             }
2602             if(oldParent){
2603                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2604             }
2605             return node;
2606         }
2607     },
2608
2609     /**
2610      * Removes a child node from this node.
2611      * @param {Node} node The node to remove
2612      * @return {Node} The removed node
2613      */
2614     removeChild : function(node){
2615         var index = this.childNodes.indexOf(node);
2616         if(index == -1){
2617             return false;
2618         }
2619         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2620             return false;
2621         }
2622
2623         // remove it from childNodes collection
2624         this.childNodes.splice(index, 1);
2625
2626         // update siblings
2627         if(node.previousSibling){
2628             node.previousSibling.nextSibling = node.nextSibling;
2629         }
2630         if(node.nextSibling){
2631             node.nextSibling.previousSibling = node.previousSibling;
2632         }
2633
2634         // update child refs
2635         if(this.firstChild == node){
2636             this.setFirstChild(node.nextSibling);
2637         }
2638         if(this.lastChild == node){
2639             this.setLastChild(node.previousSibling);
2640         }
2641
2642         node.setOwnerTree(null);
2643         // clear any references from the node
2644         node.parentNode = null;
2645         node.previousSibling = null;
2646         node.nextSibling = null;
2647         this.fireEvent("remove", this.ownerTree, this, node);
2648         return node;
2649     },
2650
2651     /**
2652      * Inserts the first node before the second node in this nodes childNodes collection.
2653      * @param {Node} node The node to insert
2654      * @param {Node} refNode The node to insert before (if null the node is appended)
2655      * @return {Node} The inserted node
2656      */
2657     insertBefore : function(node, refNode){
2658         if(!refNode){ // like standard Dom, refNode can be null for append
2659             return this.appendChild(node);
2660         }
2661         // nothing to do
2662         if(node == refNode){
2663             return false;
2664         }
2665
2666         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2667             return false;
2668         }
2669         var index = this.childNodes.indexOf(refNode);
2670         var oldParent = node.parentNode;
2671         var refIndex = index;
2672
2673         // when moving internally, indexes will change after remove
2674         if(oldParent == this && this.childNodes.indexOf(node) < index){
2675             refIndex--;
2676         }
2677
2678         // it's a move, make sure we move it cleanly
2679         if(oldParent){
2680             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2681                 return false;
2682             }
2683             oldParent.removeChild(node);
2684         }
2685         if(refIndex == 0){
2686             this.setFirstChild(node);
2687         }
2688         this.childNodes.splice(refIndex, 0, node);
2689         node.parentNode = this;
2690         var ps = this.childNodes[refIndex-1];
2691         if(ps){
2692             node.previousSibling = ps;
2693             ps.nextSibling = node;
2694         }else{
2695             node.previousSibling = null;
2696         }
2697         node.nextSibling = refNode;
2698         refNode.previousSibling = node;
2699         node.setOwnerTree(this.getOwnerTree());
2700         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2701         if(oldParent){
2702             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2703         }
2704         return node;
2705     },
2706
2707     /**
2708      * Returns the child node at the specified index.
2709      * @param {Number} index
2710      * @return {Node}
2711      */
2712     item : function(index){
2713         return this.childNodes[index];
2714     },
2715
2716     /**
2717      * Replaces one child node in this node with another.
2718      * @param {Node} newChild The replacement node
2719      * @param {Node} oldChild The node to replace
2720      * @return {Node} The replaced node
2721      */
2722     replaceChild : function(newChild, oldChild){
2723         this.insertBefore(newChild, oldChild);
2724         this.removeChild(oldChild);
2725         return oldChild;
2726     },
2727
2728     /**
2729      * Returns the index of a child node
2730      * @param {Node} node
2731      * @return {Number} The index of the node or -1 if it was not found
2732      */
2733     indexOf : function(child){
2734         return this.childNodes.indexOf(child);
2735     },
2736
2737     /**
2738      * Returns the tree this node is in.
2739      * @return {Tree}
2740      */
2741     getOwnerTree : function(){
2742         // if it doesn't have one, look for one
2743         if(!this.ownerTree){
2744             var p = this;
2745             while(p){
2746                 if(p.ownerTree){
2747                     this.ownerTree = p.ownerTree;
2748                     break;
2749                 }
2750                 p = p.parentNode;
2751             }
2752         }
2753         return this.ownerTree;
2754     },
2755
2756     /**
2757      * Returns depth of this node (the root node has a depth of 0)
2758      * @return {Number}
2759      */
2760     getDepth : function(){
2761         var depth = 0;
2762         var p = this;
2763         while(p.parentNode){
2764             ++depth;
2765             p = p.parentNode;
2766         }
2767         return depth;
2768     },
2769
2770     // private
2771     setOwnerTree : function(tree){
2772         // if it's move, we need to update everyone
2773         if(tree != this.ownerTree){
2774             if(this.ownerTree){
2775                 this.ownerTree.unregisterNode(this);
2776             }
2777             this.ownerTree = tree;
2778             var cs = this.childNodes;
2779             for(var i = 0, len = cs.length; i < len; i++) {
2780                 cs[i].setOwnerTree(tree);
2781             }
2782             if(tree){
2783                 tree.registerNode(this);
2784             }
2785         }
2786     },
2787
2788     /**
2789      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2790      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2791      * @return {String} The path
2792      */
2793     getPath : function(attr){
2794         attr = attr || "id";
2795         var p = this.parentNode;
2796         var b = [this.attributes[attr]];
2797         while(p){
2798             b.unshift(p.attributes[attr]);
2799             p = p.parentNode;
2800         }
2801         var sep = this.getOwnerTree().pathSeparator;
2802         return sep + b.join(sep);
2803     },
2804
2805     /**
2806      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2807      * function call will be the scope provided or the current node. The arguments to the function
2808      * will be the args provided or the current node. If the function returns false at any point,
2809      * the bubble is stopped.
2810      * @param {Function} fn The function to call
2811      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2812      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2813      */
2814     bubble : function(fn, scope, args){
2815         var p = this;
2816         while(p){
2817             if(fn.call(scope || p, args || p) === false){
2818                 break;
2819             }
2820             p = p.parentNode;
2821         }
2822     },
2823
2824     /**
2825      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2826      * function call will be the scope provided or the current node. The arguments to the function
2827      * will be the args provided or the current node. If the function returns false at any point,
2828      * the cascade is stopped on that branch.
2829      * @param {Function} fn The function to call
2830      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2831      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2832      */
2833     cascade : function(fn, scope, args){
2834         if(fn.call(scope || this, args || this) !== false){
2835             var cs = this.childNodes;
2836             for(var i = 0, len = cs.length; i < len; i++) {
2837                 cs[i].cascade(fn, scope, args);
2838             }
2839         }
2840     },
2841
2842     /**
2843      * Interates the child nodes of 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 iteration stops.
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     eachChild : function(fn, scope, args){
2852         var cs = this.childNodes;
2853         for(var i = 0, len = cs.length; i < len; i++) {
2854                 if(fn.call(scope || this, args || cs[i]) === false){
2855                     break;
2856                 }
2857         }
2858     },
2859
2860     /**
2861      * Finds the first child that has the attribute with the specified value.
2862      * @param {String} attribute The attribute name
2863      * @param {Mixed} value The value to search for
2864      * @return {Node} The found child or null if none was found
2865      */
2866     findChild : function(attribute, value){
2867         var cs = this.childNodes;
2868         for(var i = 0, len = cs.length; i < len; i++) {
2869                 if(cs[i].attributes[attribute] == value){
2870                     return cs[i];
2871                 }
2872         }
2873         return null;
2874     },
2875
2876     /**
2877      * Finds the first child by a custom function. The child matches if the function passed
2878      * returns true.
2879      * @param {Function} fn
2880      * @param {Object} scope (optional)
2881      * @return {Node} The found child or null if none was found
2882      */
2883     findChildBy : function(fn, scope){
2884         var cs = this.childNodes;
2885         for(var i = 0, len = cs.length; i < len; i++) {
2886                 if(fn.call(scope||cs[i], cs[i]) === true){
2887                     return cs[i];
2888                 }
2889         }
2890         return null;
2891     },
2892
2893     /**
2894      * Sorts this nodes children using the supplied sort function
2895      * @param {Function} fn
2896      * @param {Object} scope (optional)
2897      */
2898     sort : function(fn, scope){
2899         var cs = this.childNodes;
2900         var len = cs.length;
2901         if(len > 0){
2902             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2903             cs.sort(sortFn);
2904             for(var i = 0; i < len; i++){
2905                 var n = cs[i];
2906                 n.previousSibling = cs[i-1];
2907                 n.nextSibling = cs[i+1];
2908                 if(i == 0){
2909                     this.setFirstChild(n);
2910                 }
2911                 if(i == len-1){
2912                     this.setLastChild(n);
2913                 }
2914             }
2915         }
2916     },
2917
2918     /**
2919      * Returns true if this node is an ancestor (at any point) of the passed node.
2920      * @param {Node} node
2921      * @return {Boolean}
2922      */
2923     contains : function(node){
2924         return node.isAncestor(this);
2925     },
2926
2927     /**
2928      * Returns true if the passed node is an ancestor (at any point) of this node.
2929      * @param {Node} node
2930      * @return {Boolean}
2931      */
2932     isAncestor : function(node){
2933         var p = this.parentNode;
2934         while(p){
2935             if(p == node){
2936                 return true;
2937             }
2938             p = p.parentNode;
2939         }
2940         return false;
2941     },
2942
2943     toString : function(){
2944         return "[Node"+(this.id?" "+this.id:"")+"]";
2945     }
2946 });/*
2947  * Based on:
2948  * Ext JS Library 1.1.1
2949  * Copyright(c) 2006-2007, Ext JS, LLC.
2950  *
2951  * Originally Released Under LGPL - original licence link has changed is not relivant.
2952  *
2953  * Fork - LGPL
2954  * <script type="text/javascript">
2955  */
2956  (function(){ 
2957 /**
2958  * @class Roo.Layer
2959  * @extends Roo.Element
2960  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2961  * automatic maintaining of shadow/shim positions.
2962  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2963  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2964  * you can pass a string with a CSS class name. False turns off the shadow.
2965  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2966  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2967  * @cfg {String} cls CSS class to add to the element
2968  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2969  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2970  * @constructor
2971  * @param {Object} config An object with config options.
2972  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2973  */
2974
2975 Roo.Layer = function(config, existingEl){
2976     config = config || {};
2977     var dh = Roo.DomHelper;
2978     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2979     if(existingEl){
2980         this.dom = Roo.getDom(existingEl);
2981     }
2982     if(!this.dom){
2983         var o = config.dh || {tag: "div", cls: "x-layer"};
2984         this.dom = dh.append(pel, o);
2985     }
2986     if(config.cls){
2987         this.addClass(config.cls);
2988     }
2989     this.constrain = config.constrain !== false;
2990     this.visibilityMode = Roo.Element.VISIBILITY;
2991     if(config.id){
2992         this.id = this.dom.id = config.id;
2993     }else{
2994         this.id = Roo.id(this.dom);
2995     }
2996     this.zindex = config.zindex || this.getZIndex();
2997     this.position("absolute", this.zindex);
2998     if(config.shadow){
2999         this.shadowOffset = config.shadowOffset || 4;
3000         this.shadow = new Roo.Shadow({
3001             offset : this.shadowOffset,
3002             mode : config.shadow
3003         });
3004     }else{
3005         this.shadowOffset = 0;
3006     }
3007     this.useShim = config.shim !== false && Roo.useShims;
3008     this.useDisplay = config.useDisplay;
3009     this.hide();
3010 };
3011
3012 var supr = Roo.Element.prototype;
3013
3014 // shims are shared among layer to keep from having 100 iframes
3015 var shims = [];
3016
3017 Roo.extend(Roo.Layer, Roo.Element, {
3018
3019     getZIndex : function(){
3020         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3021     },
3022
3023     getShim : function(){
3024         if(!this.useShim){
3025             return null;
3026         }
3027         if(this.shim){
3028             return this.shim;
3029         }
3030         var shim = shims.shift();
3031         if(!shim){
3032             shim = this.createShim();
3033             shim.enableDisplayMode('block');
3034             shim.dom.style.display = 'none';
3035             shim.dom.style.visibility = 'visible';
3036         }
3037         var pn = this.dom.parentNode;
3038         if(shim.dom.parentNode != pn){
3039             pn.insertBefore(shim.dom, this.dom);
3040         }
3041         shim.setStyle('z-index', this.getZIndex()-2);
3042         this.shim = shim;
3043         return shim;
3044     },
3045
3046     hideShim : function(){
3047         if(this.shim){
3048             this.shim.setDisplayed(false);
3049             shims.push(this.shim);
3050             delete this.shim;
3051         }
3052     },
3053
3054     disableShadow : function(){
3055         if(this.shadow){
3056             this.shadowDisabled = true;
3057             this.shadow.hide();
3058             this.lastShadowOffset = this.shadowOffset;
3059             this.shadowOffset = 0;
3060         }
3061     },
3062
3063     enableShadow : function(show){
3064         if(this.shadow){
3065             this.shadowDisabled = false;
3066             this.shadowOffset = this.lastShadowOffset;
3067             delete this.lastShadowOffset;
3068             if(show){
3069                 this.sync(true);
3070             }
3071         }
3072     },
3073
3074     // private
3075     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3076     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3077     sync : function(doShow){
3078         var sw = this.shadow;
3079         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3080             var sh = this.getShim();
3081
3082             var w = this.getWidth(),
3083                 h = this.getHeight();
3084
3085             var l = this.getLeft(true),
3086                 t = this.getTop(true);
3087
3088             if(sw && !this.shadowDisabled){
3089                 if(doShow && !sw.isVisible()){
3090                     sw.show(this);
3091                 }else{
3092                     sw.realign(l, t, w, h);
3093                 }
3094                 if(sh){
3095                     if(doShow){
3096                        sh.show();
3097                     }
3098                     // fit the shim behind the shadow, so it is shimmed too
3099                     var a = sw.adjusts, s = sh.dom.style;
3100                     s.left = (Math.min(l, l+a.l))+"px";
3101                     s.top = (Math.min(t, t+a.t))+"px";
3102                     s.width = (w+a.w)+"px";
3103                     s.height = (h+a.h)+"px";
3104                 }
3105             }else if(sh){
3106                 if(doShow){
3107                    sh.show();
3108                 }
3109                 sh.setSize(w, h);
3110                 sh.setLeftTop(l, t);
3111             }
3112             
3113         }
3114     },
3115
3116     // private
3117     destroy : function(){
3118         this.hideShim();
3119         if(this.shadow){
3120             this.shadow.hide();
3121         }
3122         this.removeAllListeners();
3123         var pn = this.dom.parentNode;
3124         if(pn){
3125             pn.removeChild(this.dom);
3126         }
3127         Roo.Element.uncache(this.id);
3128     },
3129
3130     remove : function(){
3131         this.destroy();
3132     },
3133
3134     // private
3135     beginUpdate : function(){
3136         this.updating = true;
3137     },
3138
3139     // private
3140     endUpdate : function(){
3141         this.updating = false;
3142         this.sync(true);
3143     },
3144
3145     // private
3146     hideUnders : function(negOffset){
3147         if(this.shadow){
3148             this.shadow.hide();
3149         }
3150         this.hideShim();
3151     },
3152
3153     // private
3154     constrainXY : function(){
3155         if(this.constrain){
3156             var vw = Roo.lib.Dom.getViewWidth(),
3157                 vh = Roo.lib.Dom.getViewHeight();
3158             var s = Roo.get(document).getScroll();
3159
3160             var xy = this.getXY();
3161             var x = xy[0], y = xy[1];   
3162             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3163             // only move it if it needs it
3164             var moved = false;
3165             // first validate right/bottom
3166             if((x + w) > vw+s.left){
3167                 x = vw - w - this.shadowOffset;
3168                 moved = true;
3169             }
3170             if((y + h) > vh+s.top){
3171                 y = vh - h - this.shadowOffset;
3172                 moved = true;
3173             }
3174             // then make sure top/left isn't negative
3175             if(x < s.left){
3176                 x = s.left;
3177                 moved = true;
3178             }
3179             if(y < s.top){
3180                 y = s.top;
3181                 moved = true;
3182             }
3183             if(moved){
3184                 if(this.avoidY){
3185                     var ay = this.avoidY;
3186                     if(y <= ay && (y+h) >= ay){
3187                         y = ay-h-5;   
3188                     }
3189                 }
3190                 xy = [x, y];
3191                 this.storeXY(xy);
3192                 supr.setXY.call(this, xy);
3193                 this.sync();
3194             }
3195         }
3196     },
3197
3198     isVisible : function(){
3199         return this.visible;    
3200     },
3201
3202     // private
3203     showAction : function(){
3204         this.visible = true; // track visibility to prevent getStyle calls
3205         if(this.useDisplay === true){
3206             this.setDisplayed("");
3207         }else if(this.lastXY){
3208             supr.setXY.call(this, this.lastXY);
3209         }else if(this.lastLT){
3210             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3211         }
3212     },
3213
3214     // private
3215     hideAction : function(){
3216         this.visible = false;
3217         if(this.useDisplay === true){
3218             this.setDisplayed(false);
3219         }else{
3220             this.setLeftTop(-10000,-10000);
3221         }
3222     },
3223
3224     // overridden Element method
3225     setVisible : function(v, a, d, c, e){
3226         if(v){
3227             this.showAction();
3228         }
3229         if(a && v){
3230             var cb = function(){
3231                 this.sync(true);
3232                 if(c){
3233                     c();
3234                 }
3235             }.createDelegate(this);
3236             supr.setVisible.call(this, true, true, d, cb, e);
3237         }else{
3238             if(!v){
3239                 this.hideUnders(true);
3240             }
3241             var cb = c;
3242             if(a){
3243                 cb = function(){
3244                     this.hideAction();
3245                     if(c){
3246                         c();
3247                     }
3248                 }.createDelegate(this);
3249             }
3250             supr.setVisible.call(this, v, a, d, cb, e);
3251             if(v){
3252                 this.sync(true);
3253             }else if(!a){
3254                 this.hideAction();
3255             }
3256         }
3257     },
3258
3259     storeXY : function(xy){
3260         delete this.lastLT;
3261         this.lastXY = xy;
3262     },
3263
3264     storeLeftTop : function(left, top){
3265         delete this.lastXY;
3266         this.lastLT = [left, top];
3267     },
3268
3269     // private
3270     beforeFx : function(){
3271         this.beforeAction();
3272         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3273     },
3274
3275     // private
3276     afterFx : function(){
3277         Roo.Layer.superclass.afterFx.apply(this, arguments);
3278         this.sync(this.isVisible());
3279     },
3280
3281     // private
3282     beforeAction : function(){
3283         if(!this.updating && this.shadow){
3284             this.shadow.hide();
3285         }
3286     },
3287
3288     // overridden Element method
3289     setLeft : function(left){
3290         this.storeLeftTop(left, this.getTop(true));
3291         supr.setLeft.apply(this, arguments);
3292         this.sync();
3293     },
3294
3295     setTop : function(top){
3296         this.storeLeftTop(this.getLeft(true), top);
3297         supr.setTop.apply(this, arguments);
3298         this.sync();
3299     },
3300
3301     setLeftTop : function(left, top){
3302         this.storeLeftTop(left, top);
3303         supr.setLeftTop.apply(this, arguments);
3304         this.sync();
3305     },
3306
3307     setXY : function(xy, a, d, c, e){
3308         this.fixDisplay();
3309         this.beforeAction();
3310         this.storeXY(xy);
3311         var cb = this.createCB(c);
3312         supr.setXY.call(this, xy, a, d, cb, e);
3313         if(!a){
3314             cb();
3315         }
3316     },
3317
3318     // private
3319     createCB : function(c){
3320         var el = this;
3321         return function(){
3322             el.constrainXY();
3323             el.sync(true);
3324             if(c){
3325                 c();
3326             }
3327         };
3328     },
3329
3330     // overridden Element method
3331     setX : function(x, a, d, c, e){
3332         this.setXY([x, this.getY()], a, d, c, e);
3333     },
3334
3335     // overridden Element method
3336     setY : function(y, a, d, c, e){
3337         this.setXY([this.getX(), y], a, d, c, e);
3338     },
3339
3340     // overridden Element method
3341     setSize : function(w, h, a, d, c, e){
3342         this.beforeAction();
3343         var cb = this.createCB(c);
3344         supr.setSize.call(this, w, h, a, d, cb, e);
3345         if(!a){
3346             cb();
3347         }
3348     },
3349
3350     // overridden Element method
3351     setWidth : function(w, a, d, c, e){
3352         this.beforeAction();
3353         var cb = this.createCB(c);
3354         supr.setWidth.call(this, w, a, d, cb, e);
3355         if(!a){
3356             cb();
3357         }
3358     },
3359
3360     // overridden Element method
3361     setHeight : function(h, a, d, c, e){
3362         this.beforeAction();
3363         var cb = this.createCB(c);
3364         supr.setHeight.call(this, h, a, d, cb, e);
3365         if(!a){
3366             cb();
3367         }
3368     },
3369
3370     // overridden Element method
3371     setBounds : function(x, y, w, h, a, d, c, e){
3372         this.beforeAction();
3373         var cb = this.createCB(c);
3374         if(!a){
3375             this.storeXY([x, y]);
3376             supr.setXY.call(this, [x, y]);
3377             supr.setSize.call(this, w, h, a, d, cb, e);
3378             cb();
3379         }else{
3380             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3381         }
3382         return this;
3383     },
3384     
3385     /**
3386      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3387      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3388      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3389      * @param {Number} zindex The new z-index to set
3390      * @return {this} The Layer
3391      */
3392     setZIndex : function(zindex){
3393         this.zindex = zindex;
3394         this.setStyle("z-index", zindex + 2);
3395         if(this.shadow){
3396             this.shadow.setZIndex(zindex + 1);
3397         }
3398         if(this.shim){
3399             this.shim.setStyle("z-index", zindex);
3400         }
3401     }
3402 });
3403 })();/*
3404  * Based on:
3405  * Ext JS Library 1.1.1
3406  * Copyright(c) 2006-2007, Ext JS, LLC.
3407  *
3408  * Originally Released Under LGPL - original licence link has changed is not relivant.
3409  *
3410  * Fork - LGPL
3411  * <script type="text/javascript">
3412  */
3413
3414
3415 /**
3416  * @class Roo.Shadow
3417  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3418  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3419  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3420  * @constructor
3421  * Create a new Shadow
3422  * @param {Object} config The config object
3423  */
3424 Roo.Shadow = function(config){
3425     Roo.apply(this, config);
3426     if(typeof this.mode != "string"){
3427         this.mode = this.defaultMode;
3428     }
3429     var o = this.offset, a = {h: 0};
3430     var rad = Math.floor(this.offset/2);
3431     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3432         case "drop":
3433             a.w = 0;
3434             a.l = a.t = o;
3435             a.t -= 1;
3436             if(Roo.isIE){
3437                 a.l -= this.offset + rad;
3438                 a.t -= this.offset + rad;
3439                 a.w -= rad;
3440                 a.h -= rad;
3441                 a.t += 1;
3442             }
3443         break;
3444         case "sides":
3445             a.w = (o*2);
3446             a.l = -o;
3447             a.t = o-1;
3448             if(Roo.isIE){
3449                 a.l -= (this.offset - rad);
3450                 a.t -= this.offset + rad;
3451                 a.l += 1;
3452                 a.w -= (this.offset - rad)*2;
3453                 a.w -= rad + 1;
3454                 a.h -= 1;
3455             }
3456         break;
3457         case "frame":
3458             a.w = a.h = (o*2);
3459             a.l = a.t = -o;
3460             a.t += 1;
3461             a.h -= 2;
3462             if(Roo.isIE){
3463                 a.l -= (this.offset - rad);
3464                 a.t -= (this.offset - rad);
3465                 a.l += 1;
3466                 a.w -= (this.offset + rad + 1);
3467                 a.h -= (this.offset + rad);
3468                 a.h += 1;
3469             }
3470         break;
3471     };
3472
3473     this.adjusts = a;
3474 };
3475
3476 Roo.Shadow.prototype = {
3477     /**
3478      * @cfg {String} mode
3479      * The shadow display mode.  Supports the following options:<br />
3480      * sides: Shadow displays on both sides and bottom only<br />
3481      * frame: Shadow displays equally on all four sides<br />
3482      * drop: Traditional bottom-right drop shadow (default)
3483      */
3484     /**
3485      * @cfg {String} offset
3486      * The number of pixels to offset the shadow from the element (defaults to 4)
3487      */
3488     offset: 4,
3489
3490     // private
3491     defaultMode: "drop",
3492
3493     /**
3494      * Displays the shadow under the target element
3495      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3496      */
3497     show : function(target){
3498         target = Roo.get(target);
3499         if(!this.el){
3500             this.el = Roo.Shadow.Pool.pull();
3501             if(this.el.dom.nextSibling != target.dom){
3502                 this.el.insertBefore(target);
3503             }
3504         }
3505         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3506         if(Roo.isIE){
3507             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3508         }
3509         this.realign(
3510             target.getLeft(true),
3511             target.getTop(true),
3512             target.getWidth(),
3513             target.getHeight()
3514         );
3515         this.el.dom.style.display = "block";
3516     },
3517
3518     /**
3519      * Returns true if the shadow is visible, else false
3520      */
3521     isVisible : function(){
3522         return this.el ? true : false;  
3523     },
3524
3525     /**
3526      * Direct alignment when values are already available. Show must be called at least once before
3527      * calling this method to ensure it is initialized.
3528      * @param {Number} left The target element left position
3529      * @param {Number} top The target element top position
3530      * @param {Number} width The target element width
3531      * @param {Number} height The target element height
3532      */
3533     realign : function(l, t, w, h){
3534         if(!this.el){
3535             return;
3536         }
3537         var a = this.adjusts, d = this.el.dom, s = d.style;
3538         var iea = 0;
3539         s.left = (l+a.l)+"px";
3540         s.top = (t+a.t)+"px";
3541         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3542  
3543         if(s.width != sws || s.height != shs){
3544             s.width = sws;
3545             s.height = shs;
3546             if(!Roo.isIE){
3547                 var cn = d.childNodes;
3548                 var sww = Math.max(0, (sw-12))+"px";
3549                 cn[0].childNodes[1].style.width = sww;
3550                 cn[1].childNodes[1].style.width = sww;
3551                 cn[2].childNodes[1].style.width = sww;
3552                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3553             }
3554         }
3555     },
3556
3557     /**
3558      * Hides this shadow
3559      */
3560     hide : function(){
3561         if(this.el){
3562             this.el.dom.style.display = "none";
3563             Roo.Shadow.Pool.push(this.el);
3564             delete this.el;
3565         }
3566     },
3567
3568     /**
3569      * Adjust the z-index of this shadow
3570      * @param {Number} zindex The new z-index
3571      */
3572     setZIndex : function(z){
3573         this.zIndex = z;
3574         if(this.el){
3575             this.el.setStyle("z-index", z);
3576         }
3577     }
3578 };
3579
3580 // Private utility class that manages the internal Shadow cache
3581 Roo.Shadow.Pool = function(){
3582     var p = [];
3583     var markup = Roo.isIE ?
3584                  '<div class="x-ie-shadow"></div>' :
3585                  '<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>';
3586     return {
3587         pull : function(){
3588             var sh = p.shift();
3589             if(!sh){
3590                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3591                 sh.autoBoxAdjust = false;
3592             }
3593             return sh;
3594         },
3595
3596         push : function(sh){
3597             p.push(sh);
3598         }
3599     };
3600 }();/*
3601  * Based on:
3602  * Ext JS Library 1.1.1
3603  * Copyright(c) 2006-2007, Ext JS, LLC.
3604  *
3605  * Originally Released Under LGPL - original licence link has changed is not relivant.
3606  *
3607  * Fork - LGPL
3608  * <script type="text/javascript">
3609  */
3610
3611
3612 /**
3613  * @class Roo.SplitBar
3614  * @extends Roo.util.Observable
3615  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3616  * <br><br>
3617  * Usage:
3618  * <pre><code>
3619 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3620                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3621 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3622 split.minSize = 100;
3623 split.maxSize = 600;
3624 split.animate = true;
3625 split.on('moved', splitterMoved);
3626 </code></pre>
3627  * @constructor
3628  * Create a new SplitBar
3629  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3630  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3631  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3632  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3633                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3634                         position of the SplitBar).
3635  */
3636 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3637     
3638     /** @private */
3639     this.el = Roo.get(dragElement, true);
3640     this.el.dom.unselectable = "on";
3641     /** @private */
3642     this.resizingEl = Roo.get(resizingElement, true);
3643
3644     /**
3645      * @private
3646      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3647      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3648      * @type Number
3649      */
3650     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3651     
3652     /**
3653      * The minimum size of the resizing element. (Defaults to 0)
3654      * @type Number
3655      */
3656     this.minSize = 0;
3657     
3658     /**
3659      * The maximum size of the resizing element. (Defaults to 2000)
3660      * @type Number
3661      */
3662     this.maxSize = 2000;
3663     
3664     /**
3665      * Whether to animate the transition to the new size
3666      * @type Boolean
3667      */
3668     this.animate = false;
3669     
3670     /**
3671      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3672      * @type Boolean
3673      */
3674     this.useShim = false;
3675     
3676     /** @private */
3677     this.shim = null;
3678     
3679     if(!existingProxy){
3680         /** @private */
3681         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3682     }else{
3683         this.proxy = Roo.get(existingProxy).dom;
3684     }
3685     /** @private */
3686     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3687     
3688     /** @private */
3689     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3690     
3691     /** @private */
3692     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3693     
3694     /** @private */
3695     this.dragSpecs = {};
3696     
3697     /**
3698      * @private The adapter to use to positon and resize elements
3699      */
3700     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3701     this.adapter.init(this);
3702     
3703     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3704         /** @private */
3705         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3706         this.el.addClass("x-splitbar-h");
3707     }else{
3708         /** @private */
3709         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3710         this.el.addClass("x-splitbar-v");
3711     }
3712     
3713     this.addEvents({
3714         /**
3715          * @event resize
3716          * Fires when the splitter is moved (alias for {@link #event-moved})
3717          * @param {Roo.SplitBar} this
3718          * @param {Number} newSize the new width or height
3719          */
3720         "resize" : true,
3721         /**
3722          * @event moved
3723          * Fires when the splitter is moved
3724          * @param {Roo.SplitBar} this
3725          * @param {Number} newSize the new width or height
3726          */
3727         "moved" : true,
3728         /**
3729          * @event beforeresize
3730          * Fires before the splitter is dragged
3731          * @param {Roo.SplitBar} this
3732          */
3733         "beforeresize" : true,
3734
3735         "beforeapply" : true
3736     });
3737
3738     Roo.util.Observable.call(this);
3739 };
3740
3741 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3742     onStartProxyDrag : function(x, y){
3743         this.fireEvent("beforeresize", this);
3744         if(!this.overlay){
3745             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3746             o.unselectable();
3747             o.enableDisplayMode("block");
3748             // all splitbars share the same overlay
3749             Roo.SplitBar.prototype.overlay = o;
3750         }
3751         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3752         this.overlay.show();
3753         Roo.get(this.proxy).setDisplayed("block");
3754         var size = this.adapter.getElementSize(this);
3755         this.activeMinSize = this.getMinimumSize();;
3756         this.activeMaxSize = this.getMaximumSize();;
3757         var c1 = size - this.activeMinSize;
3758         var c2 = Math.max(this.activeMaxSize - size, 0);
3759         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3760             this.dd.resetConstraints();
3761             this.dd.setXConstraint(
3762                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3763                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3764             );
3765             this.dd.setYConstraint(0, 0);
3766         }else{
3767             this.dd.resetConstraints();
3768             this.dd.setXConstraint(0, 0);
3769             this.dd.setYConstraint(
3770                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3771                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3772             );
3773          }
3774         this.dragSpecs.startSize = size;
3775         this.dragSpecs.startPoint = [x, y];
3776         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3777     },
3778     
3779     /** 
3780      * @private Called after the drag operation by the DDProxy
3781      */
3782     onEndProxyDrag : function(e){
3783         Roo.get(this.proxy).setDisplayed(false);
3784         var endPoint = Roo.lib.Event.getXY(e);
3785         if(this.overlay){
3786             this.overlay.hide();
3787         }
3788         var newSize;
3789         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3790             newSize = this.dragSpecs.startSize + 
3791                 (this.placement == Roo.SplitBar.LEFT ?
3792                     endPoint[0] - this.dragSpecs.startPoint[0] :
3793                     this.dragSpecs.startPoint[0] - endPoint[0]
3794                 );
3795         }else{
3796             newSize = this.dragSpecs.startSize + 
3797                 (this.placement == Roo.SplitBar.TOP ?
3798                     endPoint[1] - this.dragSpecs.startPoint[1] :
3799                     this.dragSpecs.startPoint[1] - endPoint[1]
3800                 );
3801         }
3802         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3803         if(newSize != this.dragSpecs.startSize){
3804             if(this.fireEvent('beforeapply', this, newSize) !== false){
3805                 this.adapter.setElementSize(this, newSize);
3806                 this.fireEvent("moved", this, newSize);
3807                 this.fireEvent("resize", this, newSize);
3808             }
3809         }
3810     },
3811     
3812     /**
3813      * Get the adapter this SplitBar uses
3814      * @return The adapter object
3815      */
3816     getAdapter : function(){
3817         return this.adapter;
3818     },
3819     
3820     /**
3821      * Set the adapter this SplitBar uses
3822      * @param {Object} adapter A SplitBar adapter object
3823      */
3824     setAdapter : function(adapter){
3825         this.adapter = adapter;
3826         this.adapter.init(this);
3827     },
3828     
3829     /**
3830      * Gets the minimum size for the resizing element
3831      * @return {Number} The minimum size
3832      */
3833     getMinimumSize : function(){
3834         return this.minSize;
3835     },
3836     
3837     /**
3838      * Sets the minimum size for the resizing element
3839      * @param {Number} minSize The minimum size
3840      */
3841     setMinimumSize : function(minSize){
3842         this.minSize = minSize;
3843     },
3844     
3845     /**
3846      * Gets the maximum size for the resizing element
3847      * @return {Number} The maximum size
3848      */
3849     getMaximumSize : function(){
3850         return this.maxSize;
3851     },
3852     
3853     /**
3854      * Sets the maximum size for the resizing element
3855      * @param {Number} maxSize The maximum size
3856      */
3857     setMaximumSize : function(maxSize){
3858         this.maxSize = maxSize;
3859     },
3860     
3861     /**
3862      * Sets the initialize size for the resizing element
3863      * @param {Number} size The initial size
3864      */
3865     setCurrentSize : function(size){
3866         var oldAnimate = this.animate;
3867         this.animate = false;
3868         this.adapter.setElementSize(this, size);
3869         this.animate = oldAnimate;
3870     },
3871     
3872     /**
3873      * Destroy this splitbar. 
3874      * @param {Boolean} removeEl True to remove the element
3875      */
3876     destroy : function(removeEl){
3877         if(this.shim){
3878             this.shim.remove();
3879         }
3880         this.dd.unreg();
3881         this.proxy.parentNode.removeChild(this.proxy);
3882         if(removeEl){
3883             this.el.remove();
3884         }
3885     }
3886 });
3887
3888 /**
3889  * @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.
3890  */
3891 Roo.SplitBar.createProxy = function(dir){
3892     var proxy = new Roo.Element(document.createElement("div"));
3893     proxy.unselectable();
3894     var cls = 'x-splitbar-proxy';
3895     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3896     document.body.appendChild(proxy.dom);
3897     return proxy.dom;
3898 };
3899
3900 /** 
3901  * @class Roo.SplitBar.BasicLayoutAdapter
3902  * Default Adapter. It assumes the splitter and resizing element are not positioned
3903  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3904  */
3905 Roo.SplitBar.BasicLayoutAdapter = function(){
3906 };
3907
3908 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3909     // do nothing for now
3910     init : function(s){
3911     
3912     },
3913     /**
3914      * Called before drag operations to get the current size of the resizing element. 
3915      * @param {Roo.SplitBar} s The SplitBar using this adapter
3916      */
3917      getElementSize : function(s){
3918         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3919             return s.resizingEl.getWidth();
3920         }else{
3921             return s.resizingEl.getHeight();
3922         }
3923     },
3924     
3925     /**
3926      * Called after drag operations to set the size of the resizing element.
3927      * @param {Roo.SplitBar} s The SplitBar using this adapter
3928      * @param {Number} newSize The new size to set
3929      * @param {Function} onComplete A function to be invoked when resizing is complete
3930      */
3931     setElementSize : function(s, newSize, onComplete){
3932         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3933             if(!s.animate){
3934                 s.resizingEl.setWidth(newSize);
3935                 if(onComplete){
3936                     onComplete(s, newSize);
3937                 }
3938             }else{
3939                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3940             }
3941         }else{
3942             
3943             if(!s.animate){
3944                 s.resizingEl.setHeight(newSize);
3945                 if(onComplete){
3946                     onComplete(s, newSize);
3947                 }
3948             }else{
3949                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3950             }
3951         }
3952     }
3953 };
3954
3955 /** 
3956  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3957  * @extends Roo.SplitBar.BasicLayoutAdapter
3958  * Adapter that  moves the splitter element to align with the resized sizing element. 
3959  * Used with an absolute positioned SplitBar.
3960  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3961  * document.body, make sure you assign an id to the body element.
3962  */
3963 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3964     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3965     this.container = Roo.get(container);
3966 };
3967
3968 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3969     init : function(s){
3970         this.basic.init(s);
3971     },
3972     
3973     getElementSize : function(s){
3974         return this.basic.getElementSize(s);
3975     },
3976     
3977     setElementSize : function(s, newSize, onComplete){
3978         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3979     },
3980     
3981     moveSplitter : function(s){
3982         var yes = Roo.SplitBar;
3983         switch(s.placement){
3984             case yes.LEFT:
3985                 s.el.setX(s.resizingEl.getRight());
3986                 break;
3987             case yes.RIGHT:
3988                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3989                 break;
3990             case yes.TOP:
3991                 s.el.setY(s.resizingEl.getBottom());
3992                 break;
3993             case yes.BOTTOM:
3994                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3995                 break;
3996         }
3997     }
3998 };
3999
4000 /**
4001  * Orientation constant - Create a vertical SplitBar
4002  * @static
4003  * @type Number
4004  */
4005 Roo.SplitBar.VERTICAL = 1;
4006
4007 /**
4008  * Orientation constant - Create a horizontal SplitBar
4009  * @static
4010  * @type Number
4011  */
4012 Roo.SplitBar.HORIZONTAL = 2;
4013
4014 /**
4015  * Placement constant - The resizing element is to the left of the splitter element
4016  * @static
4017  * @type Number
4018  */
4019 Roo.SplitBar.LEFT = 1;
4020
4021 /**
4022  * Placement constant - The resizing element is to the right of the splitter element
4023  * @static
4024  * @type Number
4025  */
4026 Roo.SplitBar.RIGHT = 2;
4027
4028 /**
4029  * Placement constant - The resizing element is positioned above the splitter element
4030  * @static
4031  * @type Number
4032  */
4033 Roo.SplitBar.TOP = 3;
4034
4035 /**
4036  * Placement constant - The resizing element is positioned under splitter element
4037  * @static
4038  * @type Number
4039  */
4040 Roo.SplitBar.BOTTOM = 4;
4041 /*
4042  * Based on:
4043  * Ext JS Library 1.1.1
4044  * Copyright(c) 2006-2007, Ext JS, LLC.
4045  *
4046  * Originally Released Under LGPL - original licence link has changed is not relivant.
4047  *
4048  * Fork - LGPL
4049  * <script type="text/javascript">
4050  */
4051
4052 /**
4053  * @class Roo.View
4054  * @extends Roo.util.Observable
4055  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4056  * This class also supports single and multi selection modes. <br>
4057  * Create a data model bound view:
4058  <pre><code>
4059  var store = new Roo.data.Store(...);
4060
4061  var view = new Roo.View({
4062     el : "my-element",
4063     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4064  
4065     singleSelect: true,
4066     selectedClass: "ydataview-selected",
4067     store: store
4068  });
4069
4070  // listen for node click?
4071  view.on("click", function(vw, index, node, e){
4072  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4073  });
4074
4075  // load XML data
4076  dataModel.load("foobar.xml");
4077  </code></pre>
4078  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4079  * <br><br>
4080  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4081  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4082  * 
4083  * Note: old style constructor is still suported (container, template, config)
4084  * 
4085  * @constructor
4086  * Create a new View
4087  * @param {Object} config The config object
4088  * 
4089  */
4090 Roo.View = function(config, depreciated_tpl, depreciated_config){
4091     
4092     this.parent = false;
4093     
4094     if (typeof(depreciated_tpl) == 'undefined') {
4095         // new way.. - universal constructor.
4096         Roo.apply(this, config);
4097         this.el  = Roo.get(this.el);
4098     } else {
4099         // old format..
4100         this.el  = Roo.get(config);
4101         this.tpl = depreciated_tpl;
4102         Roo.apply(this, depreciated_config);
4103     }
4104     this.wrapEl  = this.el.wrap().wrap();
4105     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4106     
4107     
4108     if(typeof(this.tpl) == "string"){
4109         this.tpl = new Roo.Template(this.tpl);
4110     } else {
4111         // support xtype ctors..
4112         this.tpl = new Roo.factory(this.tpl, Roo);
4113     }
4114     
4115     
4116     this.tpl.compile();
4117     
4118     /** @private */
4119     this.addEvents({
4120         /**
4121          * @event beforeclick
4122          * Fires before a click is processed. Returns false to cancel the default action.
4123          * @param {Roo.View} this
4124          * @param {Number} index The index of the target node
4125          * @param {HTMLElement} node The target node
4126          * @param {Roo.EventObject} e The raw event object
4127          */
4128             "beforeclick" : true,
4129         /**
4130          * @event click
4131          * Fires when a template node is clicked.
4132          * @param {Roo.View} this
4133          * @param {Number} index The index of the target node
4134          * @param {HTMLElement} node The target node
4135          * @param {Roo.EventObject} e The raw event object
4136          */
4137             "click" : true,
4138         /**
4139          * @event dblclick
4140          * Fires when a template node is double clicked.
4141          * @param {Roo.View} this
4142          * @param {Number} index The index of the target node
4143          * @param {HTMLElement} node The target node
4144          * @param {Roo.EventObject} e The raw event object
4145          */
4146             "dblclick" : true,
4147         /**
4148          * @event contextmenu
4149          * Fires when a template node is right clicked.
4150          * @param {Roo.View} this
4151          * @param {Number} index The index of the target node
4152          * @param {HTMLElement} node The target node
4153          * @param {Roo.EventObject} e The raw event object
4154          */
4155             "contextmenu" : true,
4156         /**
4157          * @event selectionchange
4158          * Fires when the selected nodes change.
4159          * @param {Roo.View} this
4160          * @param {Array} selections Array of the selected nodes
4161          */
4162             "selectionchange" : true,
4163     
4164         /**
4165          * @event beforeselect
4166          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4167          * @param {Roo.View} this
4168          * @param {HTMLElement} node The node to be selected
4169          * @param {Array} selections Array of currently selected nodes
4170          */
4171             "beforeselect" : true,
4172         /**
4173          * @event preparedata
4174          * Fires on every row to render, to allow you to change the data.
4175          * @param {Roo.View} this
4176          * @param {Object} data to be rendered (change this)
4177          */
4178           "preparedata" : true
4179           
4180           
4181         });
4182
4183
4184
4185     this.el.on({
4186         "click": this.onClick,
4187         "dblclick": this.onDblClick,
4188         "contextmenu": this.onContextMenu,
4189         scope:this
4190     });
4191
4192     this.selections = [];
4193     this.nodes = [];
4194     this.cmp = new Roo.CompositeElementLite([]);
4195     if(this.store){
4196         this.store = Roo.factory(this.store, Roo.data);
4197         this.setStore(this.store, true);
4198     }
4199     
4200     if ( this.footer && this.footer.xtype) {
4201            
4202          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4203         
4204         this.footer.dataSource = this.store;
4205         this.footer.container = fctr;
4206         this.footer = Roo.factory(this.footer, Roo);
4207         fctr.insertFirst(this.el);
4208         
4209         // this is a bit insane - as the paging toolbar seems to detach the el..
4210 //        dom.parentNode.parentNode.parentNode
4211          // they get detached?
4212     }
4213     
4214     
4215     Roo.View.superclass.constructor.call(this);
4216     
4217     
4218 };
4219
4220 Roo.extend(Roo.View, Roo.util.Observable, {
4221     
4222      /**
4223      * @cfg {Roo.data.Store} store Data store to load data from.
4224      */
4225     store : false,
4226     
4227     /**
4228      * @cfg {String|Roo.Element} el The container element.
4229      */
4230     el : '',
4231     
4232     /**
4233      * @cfg {String|Roo.Template} tpl The template used by this View 
4234      */
4235     tpl : false,
4236     /**
4237      * @cfg {String} dataName the named area of the template to use as the data area
4238      *                          Works with domtemplates roo-name="name"
4239      */
4240     dataName: false,
4241     /**
4242      * @cfg {String} selectedClass The css class to add to selected nodes
4243      */
4244     selectedClass : "x-view-selected",
4245      /**
4246      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4247      */
4248     emptyText : "",
4249     
4250     /**
4251      * @cfg {String} text to display on mask (default Loading)
4252      */
4253     mask : false,
4254     /**
4255      * @cfg {Boolean} multiSelect Allow multiple selection
4256      */
4257     multiSelect : false,
4258     /**
4259      * @cfg {Boolean} singleSelect Allow single selection
4260      */
4261     singleSelect:  false,
4262     
4263     /**
4264      * @cfg {Boolean} toggleSelect - selecting 
4265      */
4266     toggleSelect : false,
4267     
4268     /**
4269      * @cfg {Boolean} tickable - selecting 
4270      */
4271     tickable : false,
4272     
4273     /**
4274      * Returns the element this view is bound to.
4275      * @return {Roo.Element}
4276      */
4277     getEl : function(){
4278         return this.wrapEl;
4279     },
4280     
4281     
4282
4283     /**
4284      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4285      */
4286     refresh : function(){
4287         //Roo.log('refresh');
4288         var t = this.tpl;
4289         
4290         // if we are using something like 'domtemplate', then
4291         // the what gets used is:
4292         // t.applySubtemplate(NAME, data, wrapping data..)
4293         // the outer template then get' applied with
4294         //     the store 'extra data'
4295         // and the body get's added to the
4296         //      roo-name="data" node?
4297         //      <span class='roo-tpl-{name}'></span> ?????
4298         
4299         
4300         
4301         this.clearSelections();
4302         this.el.update("");
4303         var html = [];
4304         var records = this.store.getRange();
4305         if(records.length < 1) {
4306             
4307             // is this valid??  = should it render a template??
4308             
4309             this.el.update(this.emptyText);
4310             return;
4311         }
4312         var el = this.el;
4313         if (this.dataName) {
4314             this.el.update(t.apply(this.store.meta)); //????
4315             el = this.el.child('.roo-tpl-' + this.dataName);
4316         }
4317         
4318         for(var i = 0, len = records.length; i < len; i++){
4319             var data = this.prepareData(records[i].data, i, records[i]);
4320             this.fireEvent("preparedata", this, data, i, records[i]);
4321             
4322             var d = Roo.apply({}, data);
4323             
4324             if(this.tickable){
4325                 Roo.apply(d, {'roo-id' : Roo.id()});
4326                 
4327                 var _this = this;
4328             
4329                 Roo.each(this.parent.item, function(item){
4330                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4331                         return;
4332                     }
4333                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4334                 });
4335             }
4336             
4337             html[html.length] = Roo.util.Format.trim(
4338                 this.dataName ?
4339                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4340                     t.apply(d)
4341             );
4342         }
4343         
4344         
4345         
4346         el.update(html.join(""));
4347         this.nodes = el.dom.childNodes;
4348         this.updateIndexes(0);
4349     },
4350     
4351
4352     /**
4353      * Function to override to reformat the data that is sent to
4354      * the template for each node.
4355      * DEPRICATED - use the preparedata event handler.
4356      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4357      * a JSON object for an UpdateManager bound view).
4358      */
4359     prepareData : function(data, index, record)
4360     {
4361         this.fireEvent("preparedata", this, data, index, record);
4362         return data;
4363     },
4364
4365     onUpdate : function(ds, record){
4366         // Roo.log('on update');   
4367         this.clearSelections();
4368         var index = this.store.indexOf(record);
4369         var n = this.nodes[index];
4370         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4371         n.parentNode.removeChild(n);
4372         this.updateIndexes(index, index);
4373     },
4374
4375     
4376     
4377 // --------- FIXME     
4378     onAdd : function(ds, records, index)
4379     {
4380         //Roo.log(['on Add', ds, records, index] );        
4381         this.clearSelections();
4382         if(this.nodes.length == 0){
4383             this.refresh();
4384             return;
4385         }
4386         var n = this.nodes[index];
4387         for(var i = 0, len = records.length; i < len; i++){
4388             var d = this.prepareData(records[i].data, i, records[i]);
4389             if(n){
4390                 this.tpl.insertBefore(n, d);
4391             }else{
4392                 
4393                 this.tpl.append(this.el, d);
4394             }
4395         }
4396         this.updateIndexes(index);
4397     },
4398
4399     onRemove : function(ds, record, index){
4400        // Roo.log('onRemove');
4401         this.clearSelections();
4402         var el = this.dataName  ?
4403             this.el.child('.roo-tpl-' + this.dataName) :
4404             this.el; 
4405         
4406         el.dom.removeChild(this.nodes[index]);
4407         this.updateIndexes(index);
4408     },
4409
4410     /**
4411      * Refresh an individual node.
4412      * @param {Number} index
4413      */
4414     refreshNode : function(index){
4415         this.onUpdate(this.store, this.store.getAt(index));
4416     },
4417
4418     updateIndexes : function(startIndex, endIndex){
4419         var ns = this.nodes;
4420         startIndex = startIndex || 0;
4421         endIndex = endIndex || ns.length - 1;
4422         for(var i = startIndex; i <= endIndex; i++){
4423             ns[i].nodeIndex = i;
4424         }
4425     },
4426
4427     /**
4428      * Changes the data store this view uses and refresh the view.
4429      * @param {Store} store
4430      */
4431     setStore : function(store, initial){
4432         if(!initial && this.store){
4433             this.store.un("datachanged", this.refresh);
4434             this.store.un("add", this.onAdd);
4435             this.store.un("remove", this.onRemove);
4436             this.store.un("update", this.onUpdate);
4437             this.store.un("clear", this.refresh);
4438             this.store.un("beforeload", this.onBeforeLoad);
4439             this.store.un("load", this.onLoad);
4440             this.store.un("loadexception", this.onLoad);
4441         }
4442         if(store){
4443           
4444             store.on("datachanged", this.refresh, this);
4445             store.on("add", this.onAdd, this);
4446             store.on("remove", this.onRemove, this);
4447             store.on("update", this.onUpdate, this);
4448             store.on("clear", this.refresh, this);
4449             store.on("beforeload", this.onBeforeLoad, this);
4450             store.on("load", this.onLoad, this);
4451             store.on("loadexception", this.onLoad, this);
4452         }
4453         
4454         if(store){
4455             this.refresh();
4456         }
4457     },
4458     /**
4459      * onbeforeLoad - masks the loading area.
4460      *
4461      */
4462     onBeforeLoad : function(store,opts)
4463     {
4464          //Roo.log('onBeforeLoad');   
4465         if (!opts.add) {
4466             this.el.update("");
4467         }
4468         this.el.mask(this.mask ? this.mask : "Loading" ); 
4469     },
4470     onLoad : function ()
4471     {
4472         this.el.unmask();
4473     },
4474     
4475
4476     /**
4477      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4478      * @param {HTMLElement} node
4479      * @return {HTMLElement} The template node
4480      */
4481     findItemFromChild : function(node){
4482         var el = this.dataName  ?
4483             this.el.child('.roo-tpl-' + this.dataName,true) :
4484             this.el.dom; 
4485         
4486         if(!node || node.parentNode == el){
4487                     return node;
4488             }
4489             var p = node.parentNode;
4490             while(p && p != el){
4491             if(p.parentNode == el){
4492                 return p;
4493             }
4494             p = p.parentNode;
4495         }
4496             return null;
4497     },
4498
4499     /** @ignore */
4500     onClick : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             var index = this.indexOf(item);
4504             if(this.onItemClick(item, index, e) !== false){
4505                 this.fireEvent("click", this, index, item, e);
4506             }
4507         }else{
4508             this.clearSelections();
4509         }
4510     },
4511
4512     /** @ignore */
4513     onContextMenu : function(e){
4514         var item = this.findItemFromChild(e.getTarget());
4515         if(item){
4516             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4517         }
4518     },
4519
4520     /** @ignore */
4521     onDblClick : function(e){
4522         var item = this.findItemFromChild(e.getTarget());
4523         if(item){
4524             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4525         }
4526     },
4527
4528     onItemClick : function(item, index, e)
4529     {
4530         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4531             return false;
4532         }
4533         if (this.toggleSelect) {
4534             var m = this.isSelected(item) ? 'unselect' : 'select';
4535             //Roo.log(m);
4536             var _t = this;
4537             _t[m](item, true, false);
4538             return true;
4539         }
4540         if(this.multiSelect || this.singleSelect){
4541             if(this.multiSelect && e.shiftKey && this.lastSelection){
4542                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4543             }else{
4544                 this.select(item, this.multiSelect && e.ctrlKey);
4545                 this.lastSelection = item;
4546             }
4547             
4548             if(!this.tickable){
4549                 e.preventDefault();
4550             }
4551             
4552         }
4553         return true;
4554     },
4555
4556     /**
4557      * Get the number of selected nodes.
4558      * @return {Number}
4559      */
4560     getSelectionCount : function(){
4561         return this.selections.length;
4562     },
4563
4564     /**
4565      * Get the currently selected nodes.
4566      * @return {Array} An array of HTMLElements
4567      */
4568     getSelectedNodes : function(){
4569         return this.selections;
4570     },
4571
4572     /**
4573      * Get the indexes of the selected nodes.
4574      * @return {Array}
4575      */
4576     getSelectedIndexes : function(){
4577         var indexes = [], s = this.selections;
4578         for(var i = 0, len = s.length; i < len; i++){
4579             indexes.push(s[i].nodeIndex);
4580         }
4581         return indexes;
4582     },
4583
4584     /**
4585      * Clear all selections
4586      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4587      */
4588     clearSelections : function(suppressEvent){
4589         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4590             this.cmp.elements = this.selections;
4591             this.cmp.removeClass(this.selectedClass);
4592             this.selections = [];
4593             if(!suppressEvent){
4594                 this.fireEvent("selectionchange", this, this.selections);
4595             }
4596         }
4597     },
4598
4599     /**
4600      * Returns true if the passed node is selected
4601      * @param {HTMLElement/Number} node The node or node index
4602      * @return {Boolean}
4603      */
4604     isSelected : function(node){
4605         var s = this.selections;
4606         if(s.length < 1){
4607             return false;
4608         }
4609         node = this.getNode(node);
4610         return s.indexOf(node) !== -1;
4611     },
4612
4613     /**
4614      * Selects nodes.
4615      * @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
4616      * @param {Boolean} keepExisting (optional) true to keep existing selections
4617      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4618      */
4619     select : function(nodeInfo, keepExisting, suppressEvent){
4620         if(nodeInfo instanceof Array){
4621             if(!keepExisting){
4622                 this.clearSelections(true);
4623             }
4624             for(var i = 0, len = nodeInfo.length; i < len; i++){
4625                 this.select(nodeInfo[i], true, true);
4626             }
4627             return;
4628         } 
4629         var node = this.getNode(nodeInfo);
4630         if(!node || this.isSelected(node)){
4631             return; // already selected.
4632         }
4633         if(!keepExisting){
4634             this.clearSelections(true);
4635         }
4636         
4637         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4638             Roo.fly(node).addClass(this.selectedClass);
4639             this.selections.push(node);
4640             if(!suppressEvent){
4641                 this.fireEvent("selectionchange", this, this.selections);
4642             }
4643         }
4644         
4645         
4646     },
4647       /**
4648      * Unselects nodes.
4649      * @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
4650      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4651      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4652      */
4653     unselect : function(nodeInfo, keepExisting, suppressEvent)
4654     {
4655         if(nodeInfo instanceof Array){
4656             Roo.each(this.selections, function(s) {
4657                 this.unselect(s, nodeInfo);
4658             }, this);
4659             return;
4660         }
4661         var node = this.getNode(nodeInfo);
4662         if(!node || !this.isSelected(node)){
4663             //Roo.log("not selected");
4664             return; // not selected.
4665         }
4666         // fireevent???
4667         var ns = [];
4668         Roo.each(this.selections, function(s) {
4669             if (s == node ) {
4670                 Roo.fly(node).removeClass(this.selectedClass);
4671
4672                 return;
4673             }
4674             ns.push(s);
4675         },this);
4676         
4677         this.selections= ns;
4678         this.fireEvent("selectionchange", this, this.selections);
4679     },
4680
4681     /**
4682      * Gets a template node.
4683      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4684      * @return {HTMLElement} The node or null if it wasn't found
4685      */
4686     getNode : function(nodeInfo){
4687         if(typeof nodeInfo == "string"){
4688             return document.getElementById(nodeInfo);
4689         }else if(typeof nodeInfo == "number"){
4690             return this.nodes[nodeInfo];
4691         }
4692         return nodeInfo;
4693     },
4694
4695     /**
4696      * Gets a range template nodes.
4697      * @param {Number} startIndex
4698      * @param {Number} endIndex
4699      * @return {Array} An array of nodes
4700      */
4701     getNodes : function(start, end){
4702         var ns = this.nodes;
4703         start = start || 0;
4704         end = typeof end == "undefined" ? ns.length - 1 : end;
4705         var nodes = [];
4706         if(start <= end){
4707             for(var i = start; i <= end; i++){
4708                 nodes.push(ns[i]);
4709             }
4710         } else{
4711             for(var i = start; i >= end; i--){
4712                 nodes.push(ns[i]);
4713             }
4714         }
4715         return nodes;
4716     },
4717
4718     /**
4719      * Finds the index of the passed 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 {Number} The index of the node or -1
4722      */
4723     indexOf : function(node){
4724         node = this.getNode(node);
4725         if(typeof node.nodeIndex == "number"){
4726             return node.nodeIndex;
4727         }
4728         var ns = this.nodes;
4729         for(var i = 0, len = ns.length; i < len; i++){
4730             if(ns[i] == node){
4731                 return i;
4732             }
4733         }
4734         return -1;
4735     }
4736 });
4737 /*
4738  * Based on:
4739  * Ext JS Library 1.1.1
4740  * Copyright(c) 2006-2007, Ext JS, LLC.
4741  *
4742  * Originally Released Under LGPL - original licence link has changed is not relivant.
4743  *
4744  * Fork - LGPL
4745  * <script type="text/javascript">
4746  */
4747
4748 /**
4749  * @class Roo.JsonView
4750  * @extends Roo.View
4751  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4752 <pre><code>
4753 var view = new Roo.JsonView({
4754     container: "my-element",
4755     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4756     multiSelect: true, 
4757     jsonRoot: "data" 
4758 });
4759
4760 // listen for node click?
4761 view.on("click", function(vw, index, node, e){
4762     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4763 });
4764
4765 // direct load of JSON data
4766 view.load("foobar.php");
4767
4768 // Example from my blog list
4769 var tpl = new Roo.Template(
4770     '&lt;div class="entry"&gt;' +
4771     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4772     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4773     "&lt;/div&gt;&lt;hr /&gt;"
4774 );
4775
4776 var moreView = new Roo.JsonView({
4777     container :  "entry-list", 
4778     template : tpl,
4779     jsonRoot: "posts"
4780 });
4781 moreView.on("beforerender", this.sortEntries, this);
4782 moreView.load({
4783     url: "/blog/get-posts.php",
4784     params: "allposts=true",
4785     text: "Loading Blog Entries..."
4786 });
4787 </code></pre>
4788
4789 * Note: old code is supported with arguments : (container, template, config)
4790
4791
4792  * @constructor
4793  * Create a new JsonView
4794  * 
4795  * @param {Object} config The config object
4796  * 
4797  */
4798 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4799     
4800     
4801     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4802
4803     var um = this.el.getUpdateManager();
4804     um.setRenderer(this);
4805     um.on("update", this.onLoad, this);
4806     um.on("failure", this.onLoadException, this);
4807
4808     /**
4809      * @event beforerender
4810      * Fires before rendering of the downloaded JSON data.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} data The JSON data loaded
4813      */
4814     /**
4815      * @event load
4816      * Fires when data is loaded.
4817      * @param {Roo.JsonView} this
4818      * @param {Object} data The JSON data loaded
4819      * @param {Object} response The raw Connect response object
4820      */
4821     /**
4822      * @event loadexception
4823      * Fires when loading fails.
4824      * @param {Roo.JsonView} this
4825      * @param {Object} response The raw Connect response object
4826      */
4827     this.addEvents({
4828         'beforerender' : true,
4829         'load' : true,
4830         'loadexception' : true
4831     });
4832 };
4833 Roo.extend(Roo.JsonView, Roo.View, {
4834     /**
4835      * @type {String} The root property in the loaded JSON object that contains the data
4836      */
4837     jsonRoot : "",
4838
4839     /**
4840      * Refreshes the view.
4841      */
4842     refresh : function(){
4843         this.clearSelections();
4844         this.el.update("");
4845         var html = [];
4846         var o = this.jsonData;
4847         if(o && o.length > 0){
4848             for(var i = 0, len = o.length; i < len; i++){
4849                 var data = this.prepareData(o[i], i, o);
4850                 html[html.length] = this.tpl.apply(data);
4851             }
4852         }else{
4853             html.push(this.emptyText);
4854         }
4855         this.el.update(html.join(""));
4856         this.nodes = this.el.dom.childNodes;
4857         this.updateIndexes(0);
4858     },
4859
4860     /**
4861      * 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.
4862      * @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:
4863      <pre><code>
4864      view.load({
4865          url: "your-url.php",
4866          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4867          callback: yourFunction,
4868          scope: yourObject, //(optional scope)
4869          discardUrl: false,
4870          nocache: false,
4871          text: "Loading...",
4872          timeout: 30,
4873          scripts: false
4874      });
4875      </code></pre>
4876      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4877      * 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.
4878      * @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}
4879      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4880      * @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.
4881      */
4882     load : function(){
4883         var um = this.el.getUpdateManager();
4884         um.update.apply(um, arguments);
4885     },
4886
4887     // note - render is a standard framework call...
4888     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4889     render : function(el, response){
4890         
4891         this.clearSelections();
4892         this.el.update("");
4893         var o;
4894         try{
4895             if (response != '') {
4896                 o = Roo.util.JSON.decode(response.responseText);
4897                 if(this.jsonRoot){
4898                     
4899                     o = o[this.jsonRoot];
4900                 }
4901             }
4902         } catch(e){
4903         }
4904         /**
4905          * The current JSON data or null
4906          */
4907         this.jsonData = o;
4908         this.beforeRender();
4909         this.refresh();
4910     },
4911
4912 /**
4913  * Get the number of records in the current JSON dataset
4914  * @return {Number}
4915  */
4916     getCount : function(){
4917         return this.jsonData ? this.jsonData.length : 0;
4918     },
4919
4920 /**
4921  * Returns the JSON object for the specified node(s)
4922  * @param {HTMLElement/Array} node The node or an array of nodes
4923  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4924  * you get the JSON object for the node
4925  */
4926     getNodeData : function(node){
4927         if(node instanceof Array){
4928             var data = [];
4929             for(var i = 0, len = node.length; i < len; i++){
4930                 data.push(this.getNodeData(node[i]));
4931             }
4932             return data;
4933         }
4934         return this.jsonData[this.indexOf(node)] || null;
4935     },
4936
4937     beforeRender : function(){
4938         this.snapshot = this.jsonData;
4939         if(this.sortInfo){
4940             this.sort.apply(this, this.sortInfo);
4941         }
4942         this.fireEvent("beforerender", this, this.jsonData);
4943     },
4944
4945     onLoad : function(el, o){
4946         this.fireEvent("load", this, this.jsonData, o);
4947     },
4948
4949     onLoadException : function(el, o){
4950         this.fireEvent("loadexception", this, o);
4951     },
4952
4953 /**
4954  * Filter the data by a specific property.
4955  * @param {String} property A property on your JSON objects
4956  * @param {String/RegExp} value Either string that the property values
4957  * should start with, or a RegExp to test against the property
4958  */
4959     filter : function(property, value){
4960         if(this.jsonData){
4961             var data = [];
4962             var ss = this.snapshot;
4963             if(typeof value == "string"){
4964                 var vlen = value.length;
4965                 if(vlen == 0){
4966                     this.clearFilter();
4967                     return;
4968                 }
4969                 value = value.toLowerCase();
4970                 for(var i = 0, len = ss.length; i < len; i++){
4971                     var o = ss[i];
4972                     if(o[property].substr(0, vlen).toLowerCase() == value){
4973                         data.push(o);
4974                     }
4975                 }
4976             } else if(value.exec){ // regex?
4977                 for(var i = 0, len = ss.length; i < len; i++){
4978                     var o = ss[i];
4979                     if(value.test(o[property])){
4980                         data.push(o);
4981                     }
4982                 }
4983             } else{
4984                 return;
4985             }
4986             this.jsonData = data;
4987             this.refresh();
4988         }
4989     },
4990
4991 /**
4992  * Filter by a function. The passed function will be called with each
4993  * object in the current dataset. If the function returns true the value is kept,
4994  * otherwise it is filtered.
4995  * @param {Function} fn
4996  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4997  */
4998     filterBy : function(fn, scope){
4999         if(this.jsonData){
5000             var data = [];
5001             var ss = this.snapshot;
5002             for(var i = 0, len = ss.length; i < len; i++){
5003                 var o = ss[i];
5004                 if(fn.call(scope || this, o)){
5005                     data.push(o);
5006                 }
5007             }
5008             this.jsonData = data;
5009             this.refresh();
5010         }
5011     },
5012
5013 /**
5014  * Clears the current filter.
5015  */
5016     clearFilter : function(){
5017         if(this.snapshot && this.jsonData != this.snapshot){
5018             this.jsonData = this.snapshot;
5019             this.refresh();
5020         }
5021     },
5022
5023
5024 /**
5025  * Sorts the data for this view and refreshes it.
5026  * @param {String} property A property on your JSON objects to sort on
5027  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5028  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5029  */
5030     sort : function(property, dir, sortType){
5031         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5032         if(this.jsonData){
5033             var p = property;
5034             var dsc = dir && dir.toLowerCase() == "desc";
5035             var f = function(o1, o2){
5036                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5037                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5038                 ;
5039                 if(v1 < v2){
5040                     return dsc ? +1 : -1;
5041                 } else if(v1 > v2){
5042                     return dsc ? -1 : +1;
5043                 } else{
5044                     return 0;
5045                 }
5046             };
5047             this.jsonData.sort(f);
5048             this.refresh();
5049             if(this.jsonData != this.snapshot){
5050                 this.snapshot.sort(f);
5051             }
5052         }
5053     }
5054 });/*
5055  * Based on:
5056  * Ext JS Library 1.1.1
5057  * Copyright(c) 2006-2007, Ext JS, LLC.
5058  *
5059  * Originally Released Under LGPL - original licence link has changed is not relivant.
5060  *
5061  * Fork - LGPL
5062  * <script type="text/javascript">
5063  */
5064  
5065
5066 /**
5067  * @class Roo.ColorPalette
5068  * @extends Roo.Component
5069  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5070  * Here's an example of typical usage:
5071  * <pre><code>
5072 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5073 cp.render('my-div');
5074
5075 cp.on('select', function(palette, selColor){
5076     // do something with selColor
5077 });
5078 </code></pre>
5079  * @constructor
5080  * Create a new ColorPalette
5081  * @param {Object} config The config object
5082  */
5083 Roo.ColorPalette = function(config){
5084     Roo.ColorPalette.superclass.constructor.call(this, config);
5085     this.addEvents({
5086         /**
5087              * @event select
5088              * Fires when a color is selected
5089              * @param {ColorPalette} this
5090              * @param {String} color The 6-digit color hex code (without the # symbol)
5091              */
5092         select: true
5093     });
5094
5095     if(this.handler){
5096         this.on("select", this.handler, this.scope, true);
5097     }
5098 };
5099 Roo.extend(Roo.ColorPalette, Roo.Component, {
5100     /**
5101      * @cfg {String} itemCls
5102      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5103      */
5104     itemCls : "x-color-palette",
5105     /**
5106      * @cfg {String} value
5107      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5108      * the hex codes are case-sensitive.
5109      */
5110     value : null,
5111     clickEvent:'click',
5112     // private
5113     ctype: "Roo.ColorPalette",
5114
5115     /**
5116      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5117      */
5118     allowReselect : false,
5119
5120     /**
5121      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5122      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5123      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5124      * of colors with the width setting until the box is symmetrical.</p>
5125      * <p>You can override individual colors if needed:</p>
5126      * <pre><code>
5127 var cp = new Roo.ColorPalette();
5128 cp.colors[0] = "FF0000";  // change the first box to red
5129 </code></pre>
5130
5131 Or you can provide a custom array of your own for complete control:
5132 <pre><code>
5133 var cp = new Roo.ColorPalette();
5134 cp.colors = ["000000", "993300", "333300"];
5135 </code></pre>
5136      * @type Array
5137      */
5138     colors : [
5139         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5140         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5141         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5142         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5143         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5144     ],
5145
5146     // private
5147     onRender : function(container, position){
5148         var t = new Roo.MasterTemplate(
5149             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5150         );
5151         var c = this.colors;
5152         for(var i = 0, len = c.length; i < len; i++){
5153             t.add([c[i]]);
5154         }
5155         var el = document.createElement("div");
5156         el.className = this.itemCls;
5157         t.overwrite(el);
5158         container.dom.insertBefore(el, position);
5159         this.el = Roo.get(el);
5160         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5161         if(this.clickEvent != 'click'){
5162             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5163         }
5164     },
5165
5166     // private
5167     afterRender : function(){
5168         Roo.ColorPalette.superclass.afterRender.call(this);
5169         if(this.value){
5170             var s = this.value;
5171             this.value = null;
5172             this.select(s);
5173         }
5174     },
5175
5176     // private
5177     handleClick : function(e, t){
5178         e.preventDefault();
5179         if(!this.disabled){
5180             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5181             this.select(c.toUpperCase());
5182         }
5183     },
5184
5185     /**
5186      * Selects the specified color in the palette (fires the select event)
5187      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5188      */
5189     select : function(color){
5190         color = color.replace("#", "");
5191         if(color != this.value || this.allowReselect){
5192             var el = this.el;
5193             if(this.value){
5194                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5195             }
5196             el.child("a.color-"+color).addClass("x-color-palette-sel");
5197             this.value = color;
5198             this.fireEvent("select", this, color);
5199         }
5200     }
5201 });/*
5202  * Based on:
5203  * Ext JS Library 1.1.1
5204  * Copyright(c) 2006-2007, Ext JS, LLC.
5205  *
5206  * Originally Released Under LGPL - original licence link has changed is not relivant.
5207  *
5208  * Fork - LGPL
5209  * <script type="text/javascript">
5210  */
5211  
5212 /**
5213  * @class Roo.DatePicker
5214  * @extends Roo.Component
5215  * Simple date picker class.
5216  * @constructor
5217  * Create a new DatePicker
5218  * @param {Object} config The config object
5219  */
5220 Roo.DatePicker = function(config){
5221     Roo.DatePicker.superclass.constructor.call(this, config);
5222
5223     this.value = config && config.value ?
5224                  config.value.clearTime() : new Date().clearTime();
5225
5226     this.addEvents({
5227         /**
5228              * @event select
5229              * Fires when a date is selected
5230              * @param {DatePicker} this
5231              * @param {Date} date The selected date
5232              */
5233         'select': true,
5234         /**
5235              * @event monthchange
5236              * Fires when the displayed month changes 
5237              * @param {DatePicker} this
5238              * @param {Date} date The selected month
5239              */
5240         'monthchange': true
5241     });
5242
5243     if(this.handler){
5244         this.on("select", this.handler,  this.scope || this);
5245     }
5246     // build the disabledDatesRE
5247     if(!this.disabledDatesRE && this.disabledDates){
5248         var dd = this.disabledDates;
5249         var re = "(?:";
5250         for(var i = 0; i < dd.length; i++){
5251             re += dd[i];
5252             if(i != dd.length-1) {
5253                 re += "|";
5254             }
5255         }
5256         this.disabledDatesRE = new RegExp(re + ")");
5257     }
5258 };
5259
5260 Roo.extend(Roo.DatePicker, Roo.Component, {
5261     /**
5262      * @cfg {String} todayText
5263      * The text to display on the button that selects the current date (defaults to "Today")
5264      */
5265     todayText : "Today",
5266     /**
5267      * @cfg {String} okText
5268      * The text to display on the ok button
5269      */
5270     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5271     /**
5272      * @cfg {String} cancelText
5273      * The text to display on the cancel button
5274      */
5275     cancelText : "Cancel",
5276     /**
5277      * @cfg {String} todayTip
5278      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5279      */
5280     todayTip : "{0} (Spacebar)",
5281     /**
5282      * @cfg {Date} minDate
5283      * Minimum allowable date (JavaScript date object, defaults to null)
5284      */
5285     minDate : null,
5286     /**
5287      * @cfg {Date} maxDate
5288      * Maximum allowable date (JavaScript date object, defaults to null)
5289      */
5290     maxDate : null,
5291     /**
5292      * @cfg {String} minText
5293      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5294      */
5295     minText : "This date is before the minimum date",
5296     /**
5297      * @cfg {String} maxText
5298      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5299      */
5300     maxText : "This date is after the maximum date",
5301     /**
5302      * @cfg {String} format
5303      * The default date format string which can be overriden for localization support.  The format must be
5304      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5305      */
5306     format : "m/d/y",
5307     /**
5308      * @cfg {Array} disabledDays
5309      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5310      */
5311     disabledDays : null,
5312     /**
5313      * @cfg {String} disabledDaysText
5314      * The tooltip to display when the date falls on a disabled day (defaults to "")
5315      */
5316     disabledDaysText : "",
5317     /**
5318      * @cfg {RegExp} disabledDatesRE
5319      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5320      */
5321     disabledDatesRE : null,
5322     /**
5323      * @cfg {String} disabledDatesText
5324      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5325      */
5326     disabledDatesText : "",
5327     /**
5328      * @cfg {Boolean} constrainToViewport
5329      * True to constrain the date picker to the viewport (defaults to true)
5330      */
5331     constrainToViewport : true,
5332     /**
5333      * @cfg {Array} monthNames
5334      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5335      */
5336     monthNames : Date.monthNames,
5337     /**
5338      * @cfg {Array} dayNames
5339      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5340      */
5341     dayNames : Date.dayNames,
5342     /**
5343      * @cfg {String} nextText
5344      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5345      */
5346     nextText: 'Next Month (Control+Right)',
5347     /**
5348      * @cfg {String} prevText
5349      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5350      */
5351     prevText: 'Previous Month (Control+Left)',
5352     /**
5353      * @cfg {String} monthYearText
5354      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5355      */
5356     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5357     /**
5358      * @cfg {Number} startDay
5359      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5360      */
5361     startDay : 0,
5362     /**
5363      * @cfg {Bool} showClear
5364      * Show a clear button (usefull for date form elements that can be blank.)
5365      */
5366     
5367     showClear: false,
5368     
5369     /**
5370      * Sets the value of the date field
5371      * @param {Date} value The date to set
5372      */
5373     setValue : function(value){
5374         var old = this.value;
5375         
5376         if (typeof(value) == 'string') {
5377          
5378             value = Date.parseDate(value, this.format);
5379         }
5380         if (!value) {
5381             value = new Date();
5382         }
5383         
5384         this.value = value.clearTime(true);
5385         if(this.el){
5386             this.update(this.value);
5387         }
5388     },
5389
5390     /**
5391      * Gets the current selected value of the date field
5392      * @return {Date} The selected date
5393      */
5394     getValue : function(){
5395         return this.value;
5396     },
5397
5398     // private
5399     focus : function(){
5400         if(this.el){
5401             this.update(this.activeDate);
5402         }
5403     },
5404
5405     // privateval
5406     onRender : function(container, position){
5407         
5408         var m = [
5409              '<table cellspacing="0">',
5410                 '<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>',
5411                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5412         var dn = this.dayNames;
5413         for(var i = 0; i < 7; i++){
5414             var d = this.startDay+i;
5415             if(d > 6){
5416                 d = d-7;
5417             }
5418             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5419         }
5420         m[m.length] = "</tr></thead><tbody><tr>";
5421         for(var i = 0; i < 42; i++) {
5422             if(i % 7 == 0 && i != 0){
5423                 m[m.length] = "</tr><tr>";
5424             }
5425             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5426         }
5427         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5428             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5429
5430         var el = document.createElement("div");
5431         el.className = "x-date-picker";
5432         el.innerHTML = m.join("");
5433
5434         container.dom.insertBefore(el, position);
5435
5436         this.el = Roo.get(el);
5437         this.eventEl = Roo.get(el.firstChild);
5438
5439         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5440             handler: this.showPrevMonth,
5441             scope: this,
5442             preventDefault:true,
5443             stopDefault:true
5444         });
5445
5446         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5447             handler: this.showNextMonth,
5448             scope: this,
5449             preventDefault:true,
5450             stopDefault:true
5451         });
5452
5453         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5454
5455         this.monthPicker = this.el.down('div.x-date-mp');
5456         this.monthPicker.enableDisplayMode('block');
5457         
5458         var kn = new Roo.KeyNav(this.eventEl, {
5459             "left" : function(e){
5460                 e.ctrlKey ?
5461                     this.showPrevMonth() :
5462                     this.update(this.activeDate.add("d", -1));
5463             },
5464
5465             "right" : function(e){
5466                 e.ctrlKey ?
5467                     this.showNextMonth() :
5468                     this.update(this.activeDate.add("d", 1));
5469             },
5470
5471             "up" : function(e){
5472                 e.ctrlKey ?
5473                     this.showNextYear() :
5474                     this.update(this.activeDate.add("d", -7));
5475             },
5476
5477             "down" : function(e){
5478                 e.ctrlKey ?
5479                     this.showPrevYear() :
5480                     this.update(this.activeDate.add("d", 7));
5481             },
5482
5483             "pageUp" : function(e){
5484                 this.showNextMonth();
5485             },
5486
5487             "pageDown" : function(e){
5488                 this.showPrevMonth();
5489             },
5490
5491             "enter" : function(e){
5492                 e.stopPropagation();
5493                 return true;
5494             },
5495
5496             scope : this
5497         });
5498
5499         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5500
5501         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5502
5503         this.el.unselectable();
5504         
5505         this.cells = this.el.select("table.x-date-inner tbody td");
5506         this.textNodes = this.el.query("table.x-date-inner tbody span");
5507
5508         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5509             text: "&#160;",
5510             tooltip: this.monthYearText
5511         });
5512
5513         this.mbtn.on('click', this.showMonthPicker, this);
5514         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5515
5516
5517         var today = (new Date()).dateFormat(this.format);
5518         
5519         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5520         if (this.showClear) {
5521             baseTb.add( new Roo.Toolbar.Fill());
5522         }
5523         baseTb.add({
5524             text: String.format(this.todayText, today),
5525             tooltip: String.format(this.todayTip, today),
5526             handler: this.selectToday,
5527             scope: this
5528         });
5529         
5530         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5531             
5532         //});
5533         if (this.showClear) {
5534             
5535             baseTb.add( new Roo.Toolbar.Fill());
5536             baseTb.add({
5537                 text: '&#160;',
5538                 cls: 'x-btn-icon x-btn-clear',
5539                 handler: function() {
5540                     //this.value = '';
5541                     this.fireEvent("select", this, '');
5542                 },
5543                 scope: this
5544             });
5545         }
5546         
5547         
5548         if(Roo.isIE){
5549             this.el.repaint();
5550         }
5551         this.update(this.value);
5552     },
5553
5554     createMonthPicker : function(){
5555         if(!this.monthPicker.dom.firstChild){
5556             var buf = ['<table border="0" cellspacing="0">'];
5557             for(var i = 0; i < 6; i++){
5558                 buf.push(
5559                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5560                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5561                     i == 0 ?
5562                     '<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>' :
5563                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5564                 );
5565             }
5566             buf.push(
5567                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5568                     this.okText,
5569                     '</button><button type="button" class="x-date-mp-cancel">',
5570                     this.cancelText,
5571                     '</button></td></tr>',
5572                 '</table>'
5573             );
5574             this.monthPicker.update(buf.join(''));
5575             this.monthPicker.on('click', this.onMonthClick, this);
5576             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5577
5578             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5579             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5580
5581             this.mpMonths.each(function(m, a, i){
5582                 i += 1;
5583                 if((i%2) == 0){
5584                     m.dom.xmonth = 5 + Math.round(i * .5);
5585                 }else{
5586                     m.dom.xmonth = Math.round((i-1) * .5);
5587                 }
5588             });
5589         }
5590     },
5591
5592     showMonthPicker : function(){
5593         this.createMonthPicker();
5594         var size = this.el.getSize();
5595         this.monthPicker.setSize(size);
5596         this.monthPicker.child('table').setSize(size);
5597
5598         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5599         this.updateMPMonth(this.mpSelMonth);
5600         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5601         this.updateMPYear(this.mpSelYear);
5602
5603         this.monthPicker.slideIn('t', {duration:.2});
5604     },
5605
5606     updateMPYear : function(y){
5607         this.mpyear = y;
5608         var ys = this.mpYears.elements;
5609         for(var i = 1; i <= 10; i++){
5610             var td = ys[i-1], y2;
5611             if((i%2) == 0){
5612                 y2 = y + Math.round(i * .5);
5613                 td.firstChild.innerHTML = y2;
5614                 td.xyear = y2;
5615             }else{
5616                 y2 = y - (5-Math.round(i * .5));
5617                 td.firstChild.innerHTML = y2;
5618                 td.xyear = y2;
5619             }
5620             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5621         }
5622     },
5623
5624     updateMPMonth : function(sm){
5625         this.mpMonths.each(function(m, a, i){
5626             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5627         });
5628     },
5629
5630     selectMPMonth: function(m){
5631         
5632     },
5633
5634     onMonthClick : function(e, t){
5635         e.stopEvent();
5636         var el = new Roo.Element(t), pn;
5637         if(el.is('button.x-date-mp-cancel')){
5638             this.hideMonthPicker();
5639         }
5640         else if(el.is('button.x-date-mp-ok')){
5641             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5642             this.hideMonthPicker();
5643         }
5644         else if(pn = el.up('td.x-date-mp-month', 2)){
5645             this.mpMonths.removeClass('x-date-mp-sel');
5646             pn.addClass('x-date-mp-sel');
5647             this.mpSelMonth = pn.dom.xmonth;
5648         }
5649         else if(pn = el.up('td.x-date-mp-year', 2)){
5650             this.mpYears.removeClass('x-date-mp-sel');
5651             pn.addClass('x-date-mp-sel');
5652             this.mpSelYear = pn.dom.xyear;
5653         }
5654         else if(el.is('a.x-date-mp-prev')){
5655             this.updateMPYear(this.mpyear-10);
5656         }
5657         else if(el.is('a.x-date-mp-next')){
5658             this.updateMPYear(this.mpyear+10);
5659         }
5660     },
5661
5662     onMonthDblClick : function(e, t){
5663         e.stopEvent();
5664         var el = new Roo.Element(t), pn;
5665         if(pn = el.up('td.x-date-mp-month', 2)){
5666             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5667             this.hideMonthPicker();
5668         }
5669         else if(pn = el.up('td.x-date-mp-year', 2)){
5670             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5671             this.hideMonthPicker();
5672         }
5673     },
5674
5675     hideMonthPicker : function(disableAnim){
5676         if(this.monthPicker){
5677             if(disableAnim === true){
5678                 this.monthPicker.hide();
5679             }else{
5680                 this.monthPicker.slideOut('t', {duration:.2});
5681             }
5682         }
5683     },
5684
5685     // private
5686     showPrevMonth : function(e){
5687         this.update(this.activeDate.add("mo", -1));
5688     },
5689
5690     // private
5691     showNextMonth : function(e){
5692         this.update(this.activeDate.add("mo", 1));
5693     },
5694
5695     // private
5696     showPrevYear : function(){
5697         this.update(this.activeDate.add("y", -1));
5698     },
5699
5700     // private
5701     showNextYear : function(){
5702         this.update(this.activeDate.add("y", 1));
5703     },
5704
5705     // private
5706     handleMouseWheel : function(e){
5707         var delta = e.getWheelDelta();
5708         if(delta > 0){
5709             this.showPrevMonth();
5710             e.stopEvent();
5711         } else if(delta < 0){
5712             this.showNextMonth();
5713             e.stopEvent();
5714         }
5715     },
5716
5717     // private
5718     handleDateClick : function(e, t){
5719         e.stopEvent();
5720         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5721             this.setValue(new Date(t.dateValue));
5722             this.fireEvent("select", this, this.value);
5723         }
5724     },
5725
5726     // private
5727     selectToday : function(){
5728         this.setValue(new Date().clearTime());
5729         this.fireEvent("select", this, this.value);
5730     },
5731
5732     // private
5733     update : function(date)
5734     {
5735         var vd = this.activeDate;
5736         this.activeDate = date;
5737         if(vd && this.el){
5738             var t = date.getTime();
5739             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5740                 this.cells.removeClass("x-date-selected");
5741                 this.cells.each(function(c){
5742                    if(c.dom.firstChild.dateValue == t){
5743                        c.addClass("x-date-selected");
5744                        setTimeout(function(){
5745                             try{c.dom.firstChild.focus();}catch(e){}
5746                        }, 50);
5747                        return false;
5748                    }
5749                 });
5750                 return;
5751             }
5752         }
5753         
5754         var days = date.getDaysInMonth();
5755         var firstOfMonth = date.getFirstDateOfMonth();
5756         var startingPos = firstOfMonth.getDay()-this.startDay;
5757
5758         if(startingPos <= this.startDay){
5759             startingPos += 7;
5760         }
5761
5762         var pm = date.add("mo", -1);
5763         var prevStart = pm.getDaysInMonth()-startingPos;
5764
5765         var cells = this.cells.elements;
5766         var textEls = this.textNodes;
5767         days += startingPos;
5768
5769         // convert everything to numbers so it's fast
5770         var day = 86400000;
5771         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5772         var today = new Date().clearTime().getTime();
5773         var sel = date.clearTime().getTime();
5774         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5775         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5776         var ddMatch = this.disabledDatesRE;
5777         var ddText = this.disabledDatesText;
5778         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5779         var ddaysText = this.disabledDaysText;
5780         var format = this.format;
5781
5782         var setCellClass = function(cal, cell){
5783             cell.title = "";
5784             var t = d.getTime();
5785             cell.firstChild.dateValue = t;
5786             if(t == today){
5787                 cell.className += " x-date-today";
5788                 cell.title = cal.todayText;
5789             }
5790             if(t == sel){
5791                 cell.className += " x-date-selected";
5792                 setTimeout(function(){
5793                     try{cell.firstChild.focus();}catch(e){}
5794                 }, 50);
5795             }
5796             // disabling
5797             if(t < min) {
5798                 cell.className = " x-date-disabled";
5799                 cell.title = cal.minText;
5800                 return;
5801             }
5802             if(t > max) {
5803                 cell.className = " x-date-disabled";
5804                 cell.title = cal.maxText;
5805                 return;
5806             }
5807             if(ddays){
5808                 if(ddays.indexOf(d.getDay()) != -1){
5809                     cell.title = ddaysText;
5810                     cell.className = " x-date-disabled";
5811                 }
5812             }
5813             if(ddMatch && format){
5814                 var fvalue = d.dateFormat(format);
5815                 if(ddMatch.test(fvalue)){
5816                     cell.title = ddText.replace("%0", fvalue);
5817                     cell.className = " x-date-disabled";
5818                 }
5819             }
5820         };
5821
5822         var i = 0;
5823         for(; i < startingPos; i++) {
5824             textEls[i].innerHTML = (++prevStart);
5825             d.setDate(d.getDate()+1);
5826             cells[i].className = "x-date-prevday";
5827             setCellClass(this, cells[i]);
5828         }
5829         for(; i < days; i++){
5830             intDay = i - startingPos + 1;
5831             textEls[i].innerHTML = (intDay);
5832             d.setDate(d.getDate()+1);
5833             cells[i].className = "x-date-active";
5834             setCellClass(this, cells[i]);
5835         }
5836         var extraDays = 0;
5837         for(; i < 42; i++) {
5838              textEls[i].innerHTML = (++extraDays);
5839              d.setDate(d.getDate()+1);
5840              cells[i].className = "x-date-nextday";
5841              setCellClass(this, cells[i]);
5842         }
5843
5844         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5845         this.fireEvent('monthchange', this, date);
5846         
5847         if(!this.internalRender){
5848             var main = this.el.dom.firstChild;
5849             var w = main.offsetWidth;
5850             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5851             Roo.fly(main).setWidth(w);
5852             this.internalRender = true;
5853             // opera does not respect the auto grow header center column
5854             // then, after it gets a width opera refuses to recalculate
5855             // without a second pass
5856             if(Roo.isOpera && !this.secondPass){
5857                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5858                 this.secondPass = true;
5859                 this.update.defer(10, this, [date]);
5860             }
5861         }
5862         
5863         
5864     }
5865 });        /*
5866  * Based on:
5867  * Ext JS Library 1.1.1
5868  * Copyright(c) 2006-2007, Ext JS, LLC.
5869  *
5870  * Originally Released Under LGPL - original licence link has changed is not relivant.
5871  *
5872  * Fork - LGPL
5873  * <script type="text/javascript">
5874  */
5875 /**
5876  * @class Roo.TabPanel
5877  * @extends Roo.util.Observable
5878  * A lightweight tab container.
5879  * <br><br>
5880  * Usage:
5881  * <pre><code>
5882 // basic tabs 1, built from existing content
5883 var tabs = new Roo.TabPanel("tabs1");
5884 tabs.addTab("script", "View Script");
5885 tabs.addTab("markup", "View Markup");
5886 tabs.activate("script");
5887
5888 // more advanced tabs, built from javascript
5889 var jtabs = new Roo.TabPanel("jtabs");
5890 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5891
5892 // set up the UpdateManager
5893 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5894 var updater = tab2.getUpdateManager();
5895 updater.setDefaultUrl("ajax1.htm");
5896 tab2.on('activate', updater.refresh, updater, true);
5897
5898 // Use setUrl for Ajax loading
5899 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5900 tab3.setUrl("ajax2.htm", null, true);
5901
5902 // Disabled tab
5903 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5904 tab4.disable();
5905
5906 jtabs.activate("jtabs-1");
5907  * </code></pre>
5908  * @constructor
5909  * Create a new TabPanel.
5910  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5911  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5912  */
5913 Roo.TabPanel = function(container, config){
5914     /**
5915     * The container element for this TabPanel.
5916     * @type Roo.Element
5917     */
5918     this.el = Roo.get(container, true);
5919     if(config){
5920         if(typeof config == "boolean"){
5921             this.tabPosition = config ? "bottom" : "top";
5922         }else{
5923             Roo.apply(this, config);
5924         }
5925     }
5926     if(this.tabPosition == "bottom"){
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-bottom");
5929     }
5930     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5931     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5932     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5933     if(Roo.isIE){
5934         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5935     }
5936     if(this.tabPosition != "bottom"){
5937         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5938          * @type Roo.Element
5939          */
5940         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5941         this.el.addClass("x-tabs-top");
5942     }
5943     this.items = [];
5944
5945     this.bodyEl.setStyle("position", "relative");
5946
5947     this.active = null;
5948     this.activateDelegate = this.activate.createDelegate(this);
5949
5950     this.addEvents({
5951         /**
5952          * @event tabchange
5953          * Fires when the active tab changes
5954          * @param {Roo.TabPanel} this
5955          * @param {Roo.TabPanelItem} activePanel The new active tab
5956          */
5957         "tabchange": true,
5958         /**
5959          * @event beforetabchange
5960          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5961          * @param {Roo.TabPanel} this
5962          * @param {Object} e Set cancel to true on this object to cancel the tab change
5963          * @param {Roo.TabPanelItem} tab The tab being changed to
5964          */
5965         "beforetabchange" : true
5966     });
5967
5968     Roo.EventManager.onWindowResize(this.onResize, this);
5969     this.cpad = this.el.getPadding("lr");
5970     this.hiddenCount = 0;
5971
5972
5973     // toolbar on the tabbar support...
5974     if (this.toolbar) {
5975         var tcfg = this.toolbar;
5976         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5977         this.toolbar = new Roo.Toolbar(tcfg);
5978         if (Roo.isSafari) {
5979             var tbl = tcfg.container.child('table', true);
5980             tbl.setAttribute('width', '100%');
5981         }
5982         
5983     }
5984    
5985
5986
5987     Roo.TabPanel.superclass.constructor.call(this);
5988 };
5989
5990 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5991     /*
5992      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5993      */
5994     tabPosition : "top",
5995     /*
5996      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5997      */
5998     currentTabWidth : 0,
5999     /*
6000      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6001      */
6002     minTabWidth : 40,
6003     /*
6004      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6005      */
6006     maxTabWidth : 250,
6007     /*
6008      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6009      */
6010     preferredTabWidth : 175,
6011     /*
6012      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6013      */
6014     resizeTabs : false,
6015     /*
6016      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6017      */
6018     monitorResize : true,
6019     /*
6020      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6021      */
6022     toolbar : false,
6023
6024     /**
6025      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6026      * @param {String} id The id of the div to use <b>or create</b>
6027      * @param {String} text The text for the tab
6028      * @param {String} content (optional) Content to put in the TabPanelItem body
6029      * @param {Boolean} closable (optional) True to create a close icon on the tab
6030      * @return {Roo.TabPanelItem} The created TabPanelItem
6031      */
6032     addTab : function(id, text, content, closable){
6033         var item = new Roo.TabPanelItem(this, id, text, closable);
6034         this.addTabItem(item);
6035         if(content){
6036             item.setContent(content);
6037         }
6038         return item;
6039     },
6040
6041     /**
6042      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6043      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6044      * @return {Roo.TabPanelItem}
6045      */
6046     getTab : function(id){
6047         return this.items[id];
6048     },
6049
6050     /**
6051      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6052      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6053      */
6054     hideTab : function(id){
6055         var t = this.items[id];
6056         if(!t.isHidden()){
6057            t.setHidden(true);
6058            this.hiddenCount++;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6065      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6066      */
6067     unhideTab : function(id){
6068         var t = this.items[id];
6069         if(t.isHidden()){
6070            t.setHidden(false);
6071            this.hiddenCount--;
6072            this.autoSizeTabs();
6073         }
6074     },
6075
6076     /**
6077      * Adds an existing {@link Roo.TabPanelItem}.
6078      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6079      */
6080     addTabItem : function(item){
6081         this.items[item.id] = item;
6082         this.items.push(item);
6083         if(this.resizeTabs){
6084            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6085            this.autoSizeTabs();
6086         }else{
6087             item.autoSize();
6088         }
6089     },
6090
6091     /**
6092      * Removes a {@link Roo.TabPanelItem}.
6093      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6094      */
6095     removeTab : function(id){
6096         var items = this.items;
6097         var tab = items[id];
6098         if(!tab) { return; }
6099         var index = items.indexOf(tab);
6100         if(this.active == tab && items.length > 1){
6101             var newTab = this.getNextAvailable(index);
6102             if(newTab) {
6103                 newTab.activate();
6104             }
6105         }
6106         this.stripEl.dom.removeChild(tab.pnode.dom);
6107         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6108             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6109         }
6110         items.splice(index, 1);
6111         delete this.items[tab.id];
6112         tab.fireEvent("close", tab);
6113         tab.purgeListeners();
6114         this.autoSizeTabs();
6115     },
6116
6117     getNextAvailable : function(start){
6118         var items = this.items;
6119         var index = start;
6120         // look for a next tab that will slide over to
6121         // replace the one being removed
6122         while(index < items.length){
6123             var item = items[++index];
6124             if(item && !item.isHidden()){
6125                 return item;
6126             }
6127         }
6128         // if one isn't found select the previous tab (on the left)
6129         index = start;
6130         while(index >= 0){
6131             var item = items[--index];
6132             if(item && !item.isHidden()){
6133                 return item;
6134             }
6135         }
6136         return null;
6137     },
6138
6139     /**
6140      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6141      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6142      */
6143     disableTab : function(id){
6144         var tab = this.items[id];
6145         if(tab && this.active != tab){
6146             tab.disable();
6147         }
6148     },
6149
6150     /**
6151      * Enables a {@link Roo.TabPanelItem} that is disabled.
6152      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6153      */
6154     enableTab : function(id){
6155         var tab = this.items[id];
6156         tab.enable();
6157     },
6158
6159     /**
6160      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6161      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6162      * @return {Roo.TabPanelItem} The TabPanelItem.
6163      */
6164     activate : function(id){
6165         var tab = this.items[id];
6166         if(!tab){
6167             return null;
6168         }
6169         if(tab == this.active || tab.disabled){
6170             return tab;
6171         }
6172         var e = {};
6173         this.fireEvent("beforetabchange", this, e, tab);
6174         if(e.cancel !== true && !tab.disabled){
6175             if(this.active){
6176                 this.active.hide();
6177             }
6178             this.active = this.items[id];
6179             this.active.show();
6180             this.fireEvent("tabchange", this, this.active);
6181         }
6182         return tab;
6183     },
6184
6185     /**
6186      * Gets the active {@link Roo.TabPanelItem}.
6187      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6188      */
6189     getActiveTab : function(){
6190         return this.active;
6191     },
6192
6193     /**
6194      * Updates the tab body element to fit the height of the container element
6195      * for overflow scrolling
6196      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6197      */
6198     syncHeight : function(targetHeight){
6199         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6200         var bm = this.bodyEl.getMargins();
6201         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6202         this.bodyEl.setHeight(newHeight);
6203         return newHeight;
6204     },
6205
6206     onResize : function(){
6207         if(this.monitorResize){
6208             this.autoSizeTabs();
6209         }
6210     },
6211
6212     /**
6213      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6214      */
6215     beginUpdate : function(){
6216         this.updating = true;
6217     },
6218
6219     /**
6220      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6221      */
6222     endUpdate : function(){
6223         this.updating = false;
6224         this.autoSizeTabs();
6225     },
6226
6227     /**
6228      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6229      */
6230     autoSizeTabs : function(){
6231         var count = this.items.length;
6232         var vcount = count - this.hiddenCount;
6233         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6234             return;
6235         }
6236         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6237         var availWidth = Math.floor(w / vcount);
6238         var b = this.stripBody;
6239         if(b.getWidth() > w){
6240             var tabs = this.items;
6241             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6242             if(availWidth < this.minTabWidth){
6243                 /*if(!this.sleft){    // incomplete scrolling code
6244                     this.createScrollButtons();
6245                 }
6246                 this.showScroll();
6247                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6248             }
6249         }else{
6250             if(this.currentTabWidth < this.preferredTabWidth){
6251                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6252             }
6253         }
6254     },
6255
6256     /**
6257      * Returns the number of tabs in this TabPanel.
6258      * @return {Number}
6259      */
6260      getCount : function(){
6261          return this.items.length;
6262      },
6263
6264     /**
6265      * Resizes all the tabs to the passed width
6266      * @param {Number} The new width
6267      */
6268     setTabWidth : function(width){
6269         this.currentTabWidth = width;
6270         for(var i = 0, len = this.items.length; i < len; i++) {
6271                 if(!this.items[i].isHidden()) {
6272                 this.items[i].setWidth(width);
6273             }
6274         }
6275     },
6276
6277     /**
6278      * Destroys this TabPanel
6279      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6280      */
6281     destroy : function(removeEl){
6282         Roo.EventManager.removeResizeListener(this.onResize, this);
6283         for(var i = 0, len = this.items.length; i < len; i++){
6284             this.items[i].purgeListeners();
6285         }
6286         if(removeEl === true){
6287             this.el.update("");
6288             this.el.remove();
6289         }
6290     }
6291 });
6292
6293 /**
6294  * @class Roo.TabPanelItem
6295  * @extends Roo.util.Observable
6296  * Represents an individual item (tab plus body) in a TabPanel.
6297  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6298  * @param {String} id The id of this TabPanelItem
6299  * @param {String} text The text for the tab of this TabPanelItem
6300  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6301  */
6302 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6303     /**
6304      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6305      * @type Roo.TabPanel
6306      */
6307     this.tabPanel = tabPanel;
6308     /**
6309      * The id for this TabPanelItem
6310      * @type String
6311      */
6312     this.id = id;
6313     /** @private */
6314     this.disabled = false;
6315     /** @private */
6316     this.text = text;
6317     /** @private */
6318     this.loaded = false;
6319     this.closable = closable;
6320
6321     /**
6322      * The body element for this TabPanelItem.
6323      * @type Roo.Element
6324      */
6325     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6326     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6327     this.bodyEl.setStyle("display", "block");
6328     this.bodyEl.setStyle("zoom", "1");
6329     this.hideAction();
6330
6331     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6332     /** @private */
6333     this.el = Roo.get(els.el, true);
6334     this.inner = Roo.get(els.inner, true);
6335     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6336     this.pnode = Roo.get(els.el.parentNode, true);
6337     this.el.on("mousedown", this.onTabMouseDown, this);
6338     this.el.on("click", this.onTabClick, this);
6339     /** @private */
6340     if(closable){
6341         var c = Roo.get(els.close, true);
6342         c.dom.title = this.closeText;
6343         c.addClassOnOver("close-over");
6344         c.on("click", this.closeClick, this);
6345      }
6346
6347     this.addEvents({
6348          /**
6349          * @event activate
6350          * Fires when this tab becomes the active tab.
6351          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6352          * @param {Roo.TabPanelItem} this
6353          */
6354         "activate": true,
6355         /**
6356          * @event beforeclose
6357          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6358          * @param {Roo.TabPanelItem} this
6359          * @param {Object} e Set cancel to true on this object to cancel the close.
6360          */
6361         "beforeclose": true,
6362         /**
6363          * @event close
6364          * Fires when this tab is closed.
6365          * @param {Roo.TabPanelItem} this
6366          */
6367          "close": true,
6368         /**
6369          * @event deactivate
6370          * Fires when this tab is no longer the active tab.
6371          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6372          * @param {Roo.TabPanelItem} this
6373          */
6374          "deactivate" : true
6375     });
6376     this.hidden = false;
6377
6378     Roo.TabPanelItem.superclass.constructor.call(this);
6379 };
6380
6381 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6382     purgeListeners : function(){
6383        Roo.util.Observable.prototype.purgeListeners.call(this);
6384        this.el.removeAllListeners();
6385     },
6386     /**
6387      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6388      */
6389     show : function(){
6390         this.pnode.addClass("on");
6391         this.showAction();
6392         if(Roo.isOpera){
6393             this.tabPanel.stripWrap.repaint();
6394         }
6395         this.fireEvent("activate", this.tabPanel, this);
6396     },
6397
6398     /**
6399      * Returns true if this tab is the active tab.
6400      * @return {Boolean}
6401      */
6402     isActive : function(){
6403         return this.tabPanel.getActiveTab() == this;
6404     },
6405
6406     /**
6407      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6408      */
6409     hide : function(){
6410         this.pnode.removeClass("on");
6411         this.hideAction();
6412         this.fireEvent("deactivate", this.tabPanel, this);
6413     },
6414
6415     hideAction : function(){
6416         this.bodyEl.hide();
6417         this.bodyEl.setStyle("position", "absolute");
6418         this.bodyEl.setLeft("-20000px");
6419         this.bodyEl.setTop("-20000px");
6420     },
6421
6422     showAction : function(){
6423         this.bodyEl.setStyle("position", "relative");
6424         this.bodyEl.setTop("");
6425         this.bodyEl.setLeft("");
6426         this.bodyEl.show();
6427     },
6428
6429     /**
6430      * Set the tooltip for the tab.
6431      * @param {String} tooltip The tab's tooltip
6432      */
6433     setTooltip : function(text){
6434         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6435             this.textEl.dom.qtip = text;
6436             this.textEl.dom.removeAttribute('title');
6437         }else{
6438             this.textEl.dom.title = text;
6439         }
6440     },
6441
6442     onTabClick : function(e){
6443         e.preventDefault();
6444         this.tabPanel.activate(this.id);
6445     },
6446
6447     onTabMouseDown : function(e){
6448         e.preventDefault();
6449         this.tabPanel.activate(this.id);
6450     },
6451
6452     getWidth : function(){
6453         return this.inner.getWidth();
6454     },
6455
6456     setWidth : function(width){
6457         var iwidth = width - this.pnode.getPadding("lr");
6458         this.inner.setWidth(iwidth);
6459         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6460         this.pnode.setWidth(width);
6461     },
6462
6463     /**
6464      * Show or hide the tab
6465      * @param {Boolean} hidden True to hide or false to show.
6466      */
6467     setHidden : function(hidden){
6468         this.hidden = hidden;
6469         this.pnode.setStyle("display", hidden ? "none" : "");
6470     },
6471
6472     /**
6473      * Returns true if this tab is "hidden"
6474      * @return {Boolean}
6475      */
6476     isHidden : function(){
6477         return this.hidden;
6478     },
6479
6480     /**
6481      * Returns the text for this tab
6482      * @return {String}
6483      */
6484     getText : function(){
6485         return this.text;
6486     },
6487
6488     autoSize : function(){
6489         //this.el.beginMeasure();
6490         this.textEl.setWidth(1);
6491         /*
6492          *  #2804 [new] Tabs in Roojs
6493          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6494          */
6495         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6496         //this.el.endMeasure();
6497     },
6498
6499     /**
6500      * Sets the text for the tab (Note: this also sets the tooltip text)
6501      * @param {String} text The tab's text and tooltip
6502      */
6503     setText : function(text){
6504         this.text = text;
6505         this.textEl.update(text);
6506         this.setTooltip(text);
6507         if(!this.tabPanel.resizeTabs){
6508             this.autoSize();
6509         }
6510     },
6511     /**
6512      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6513      */
6514     activate : function(){
6515         this.tabPanel.activate(this.id);
6516     },
6517
6518     /**
6519      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6520      */
6521     disable : function(){
6522         if(this.tabPanel.active != this){
6523             this.disabled = true;
6524             this.pnode.addClass("disabled");
6525         }
6526     },
6527
6528     /**
6529      * Enables this TabPanelItem if it was previously disabled.
6530      */
6531     enable : function(){
6532         this.disabled = false;
6533         this.pnode.removeClass("disabled");
6534     },
6535
6536     /**
6537      * Sets the content for this TabPanelItem.
6538      * @param {String} content The content
6539      * @param {Boolean} loadScripts true to look for and load scripts
6540      */
6541     setContent : function(content, loadScripts){
6542         this.bodyEl.update(content, loadScripts);
6543     },
6544
6545     /**
6546      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6547      * @return {Roo.UpdateManager} The UpdateManager
6548      */
6549     getUpdateManager : function(){
6550         return this.bodyEl.getUpdateManager();
6551     },
6552
6553     /**
6554      * Set a URL to be used to load the content for this TabPanelItem.
6555      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6556      * @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)
6557      * @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)
6558      * @return {Roo.UpdateManager} The UpdateManager
6559      */
6560     setUrl : function(url, params, loadOnce){
6561         if(this.refreshDelegate){
6562             this.un('activate', this.refreshDelegate);
6563         }
6564         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6565         this.on("activate", this.refreshDelegate);
6566         return this.bodyEl.getUpdateManager();
6567     },
6568
6569     /** @private */
6570     _handleRefresh : function(url, params, loadOnce){
6571         if(!loadOnce || !this.loaded){
6572             var updater = this.bodyEl.getUpdateManager();
6573             updater.update(url, params, this._setLoaded.createDelegate(this));
6574         }
6575     },
6576
6577     /**
6578      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6579      *   Will fail silently if the setUrl method has not been called.
6580      *   This does not activate the panel, just updates its content.
6581      */
6582     refresh : function(){
6583         if(this.refreshDelegate){
6584            this.loaded = false;
6585            this.refreshDelegate();
6586         }
6587     },
6588
6589     /** @private */
6590     _setLoaded : function(){
6591         this.loaded = true;
6592     },
6593
6594     /** @private */
6595     closeClick : function(e){
6596         var o = {};
6597         e.stopEvent();
6598         this.fireEvent("beforeclose", this, o);
6599         if(o.cancel !== true){
6600             this.tabPanel.removeTab(this.id);
6601         }
6602     },
6603     /**
6604      * The text displayed in the tooltip for the close icon.
6605      * @type String
6606      */
6607     closeText : "Close this tab"
6608 });
6609
6610 /** @private */
6611 Roo.TabPanel.prototype.createStrip = function(container){
6612     var strip = document.createElement("div");
6613     strip.className = "x-tabs-wrap";
6614     container.appendChild(strip);
6615     return strip;
6616 };
6617 /** @private */
6618 Roo.TabPanel.prototype.createStripList = function(strip){
6619     // div wrapper for retard IE
6620     // returns the "tr" element.
6621     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6622         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6623         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6624     return strip.firstChild.firstChild.firstChild.firstChild;
6625 };
6626 /** @private */
6627 Roo.TabPanel.prototype.createBody = function(container){
6628     var body = document.createElement("div");
6629     Roo.id(body, "tab-body");
6630     Roo.fly(body).addClass("x-tabs-body");
6631     container.appendChild(body);
6632     return body;
6633 };
6634 /** @private */
6635 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6636     var body = Roo.getDom(id);
6637     if(!body){
6638         body = document.createElement("div");
6639         body.id = id;
6640     }
6641     Roo.fly(body).addClass("x-tabs-item-body");
6642     bodyEl.insertBefore(body, bodyEl.firstChild);
6643     return body;
6644 };
6645 /** @private */
6646 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6647     var td = document.createElement("td");
6648     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6649     //stripEl.appendChild(td);
6650     if(closable){
6651         td.className = "x-tabs-closable";
6652         if(!this.closeTpl){
6653             this.closeTpl = new Roo.Template(
6654                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6655                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6656                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6657             );
6658         }
6659         var el = this.closeTpl.overwrite(td, {"text": text});
6660         var close = el.getElementsByTagName("div")[0];
6661         var inner = el.getElementsByTagName("em")[0];
6662         return {"el": el, "close": close, "inner": inner};
6663     } else {
6664         if(!this.tabTpl){
6665             this.tabTpl = new Roo.Template(
6666                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6667                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6668             );
6669         }
6670         var el = this.tabTpl.overwrite(td, {"text": text});
6671         var inner = el.getElementsByTagName("em")[0];
6672         return {"el": el, "inner": inner};
6673     }
6674 };/*
6675  * Based on:
6676  * Ext JS Library 1.1.1
6677  * Copyright(c) 2006-2007, Ext JS, LLC.
6678  *
6679  * Originally Released Under LGPL - original licence link has changed is not relivant.
6680  *
6681  * Fork - LGPL
6682  * <script type="text/javascript">
6683  */
6684
6685 /**
6686  * @class Roo.Button
6687  * @extends Roo.util.Observable
6688  * Simple Button class
6689  * @cfg {String} text The button text
6690  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6691  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6692  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6693  * @cfg {Object} scope The scope of the handler
6694  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6695  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6696  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6697  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6698  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6699  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6700    applies if enableToggle = true)
6701  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6702  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6703   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6704  * @constructor
6705  * Create a new button
6706  * @param {Object} config The config object
6707  */
6708 Roo.Button = function(renderTo, config)
6709 {
6710     if (!config) {
6711         config = renderTo;
6712         renderTo = config.renderTo || false;
6713     }
6714     
6715     Roo.apply(this, config);
6716     this.addEvents({
6717         /**
6718              * @event click
6719              * Fires when this button is clicked
6720              * @param {Button} this
6721              * @param {EventObject} e The click event
6722              */
6723             "click" : true,
6724         /**
6725              * @event toggle
6726              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6727              * @param {Button} this
6728              * @param {Boolean} pressed
6729              */
6730             "toggle" : true,
6731         /**
6732              * @event mouseover
6733              * Fires when the mouse hovers over the button
6734              * @param {Button} this
6735              * @param {Event} e The event object
6736              */
6737         'mouseover' : true,
6738         /**
6739              * @event mouseout
6740              * Fires when the mouse exits the button
6741              * @param {Button} this
6742              * @param {Event} e The event object
6743              */
6744         'mouseout': true,
6745          /**
6746              * @event render
6747              * Fires when the button is rendered
6748              * @param {Button} this
6749              */
6750         'render': true
6751     });
6752     if(this.menu){
6753         this.menu = Roo.menu.MenuMgr.get(this.menu);
6754     }
6755     // register listeners first!!  - so render can be captured..
6756     Roo.util.Observable.call(this);
6757     if(renderTo){
6758         this.render(renderTo);
6759     }
6760     
6761   
6762 };
6763
6764 Roo.extend(Roo.Button, Roo.util.Observable, {
6765     /**
6766      * 
6767      */
6768     
6769     /**
6770      * Read-only. True if this button is hidden
6771      * @type Boolean
6772      */
6773     hidden : false,
6774     /**
6775      * Read-only. True if this button is disabled
6776      * @type Boolean
6777      */
6778     disabled : false,
6779     /**
6780      * Read-only. True if this button is pressed (only if enableToggle = true)
6781      * @type Boolean
6782      */
6783     pressed : false,
6784
6785     /**
6786      * @cfg {Number} tabIndex 
6787      * The DOM tabIndex for this button (defaults to undefined)
6788      */
6789     tabIndex : undefined,
6790
6791     /**
6792      * @cfg {Boolean} enableToggle
6793      * True to enable pressed/not pressed toggling (defaults to false)
6794      */
6795     enableToggle: false,
6796     /**
6797      * @cfg {Mixed} menu
6798      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6799      */
6800     menu : undefined,
6801     /**
6802      * @cfg {String} menuAlign
6803      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6804      */
6805     menuAlign : "tl-bl?",
6806
6807     /**
6808      * @cfg {String} iconCls
6809      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6810      */
6811     iconCls : undefined,
6812     /**
6813      * @cfg {String} type
6814      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6815      */
6816     type : 'button',
6817
6818     // private
6819     menuClassTarget: 'tr',
6820
6821     /**
6822      * @cfg {String} clickEvent
6823      * The type of event to map to the button's event handler (defaults to 'click')
6824      */
6825     clickEvent : 'click',
6826
6827     /**
6828      * @cfg {Boolean} handleMouseEvents
6829      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6830      */
6831     handleMouseEvents : true,
6832
6833     /**
6834      * @cfg {String} tooltipType
6835      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6836      */
6837     tooltipType : 'qtip',
6838
6839     /**
6840      * @cfg {String} cls
6841      * A CSS class to apply to the button's main element.
6842      */
6843     
6844     /**
6845      * @cfg {Roo.Template} template (Optional)
6846      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6847      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6848      * require code modifications if required elements (e.g. a button) aren't present.
6849      */
6850
6851     // private
6852     render : function(renderTo){
6853         var btn;
6854         if(this.hideParent){
6855             this.parentEl = Roo.get(renderTo);
6856         }
6857         if(!this.dhconfig){
6858             if(!this.template){
6859                 if(!Roo.Button.buttonTemplate){
6860                     // hideous table template
6861                     Roo.Button.buttonTemplate = new Roo.Template(
6862                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6863                         '<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>',
6864                         "</tr></tbody></table>");
6865                 }
6866                 this.template = Roo.Button.buttonTemplate;
6867             }
6868             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6869             var btnEl = btn.child("button:first");
6870             btnEl.on('focus', this.onFocus, this);
6871             btnEl.on('blur', this.onBlur, this);
6872             if(this.cls){
6873                 btn.addClass(this.cls);
6874             }
6875             if(this.icon){
6876                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6877             }
6878             if(this.iconCls){
6879                 btnEl.addClass(this.iconCls);
6880                 if(!this.cls){
6881                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6882                 }
6883             }
6884             if(this.tabIndex !== undefined){
6885                 btnEl.dom.tabIndex = this.tabIndex;
6886             }
6887             if(this.tooltip){
6888                 if(typeof this.tooltip == 'object'){
6889                     Roo.QuickTips.tips(Roo.apply({
6890                           target: btnEl.id
6891                     }, this.tooltip));
6892                 } else {
6893                     btnEl.dom[this.tooltipType] = this.tooltip;
6894                 }
6895             }
6896         }else{
6897             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6898         }
6899         this.el = btn;
6900         if(this.id){
6901             this.el.dom.id = this.el.id = this.id;
6902         }
6903         if(this.menu){
6904             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6905             this.menu.on("show", this.onMenuShow, this);
6906             this.menu.on("hide", this.onMenuHide, this);
6907         }
6908         btn.addClass("x-btn");
6909         if(Roo.isIE && !Roo.isIE7){
6910             this.autoWidth.defer(1, this);
6911         }else{
6912             this.autoWidth();
6913         }
6914         if(this.handleMouseEvents){
6915             btn.on("mouseover", this.onMouseOver, this);
6916             btn.on("mouseout", this.onMouseOut, this);
6917             btn.on("mousedown", this.onMouseDown, this);
6918         }
6919         btn.on(this.clickEvent, this.onClick, this);
6920         //btn.on("mouseup", this.onMouseUp, this);
6921         if(this.hidden){
6922             this.hide();
6923         }
6924         if(this.disabled){
6925             this.disable();
6926         }
6927         Roo.ButtonToggleMgr.register(this);
6928         if(this.pressed){
6929             this.el.addClass("x-btn-pressed");
6930         }
6931         if(this.repeat){
6932             var repeater = new Roo.util.ClickRepeater(btn,
6933                 typeof this.repeat == "object" ? this.repeat : {}
6934             );
6935             repeater.on("click", this.onClick,  this);
6936         }
6937         
6938         this.fireEvent('render', this);
6939         
6940     },
6941     /**
6942      * Returns the button's underlying element
6943      * @return {Roo.Element} The element
6944      */
6945     getEl : function(){
6946         return this.el;  
6947     },
6948     
6949     /**
6950      * Destroys this Button and removes any listeners.
6951      */
6952     destroy : function(){
6953         Roo.ButtonToggleMgr.unregister(this);
6954         this.el.removeAllListeners();
6955         this.purgeListeners();
6956         this.el.remove();
6957     },
6958
6959     // private
6960     autoWidth : function(){
6961         if(this.el){
6962             this.el.setWidth("auto");
6963             if(Roo.isIE7 && Roo.isStrict){
6964                 var ib = this.el.child('button');
6965                 if(ib && ib.getWidth() > 20){
6966                     ib.clip();
6967                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6968                 }
6969             }
6970             if(this.minWidth){
6971                 if(this.hidden){
6972                     this.el.beginMeasure();
6973                 }
6974                 if(this.el.getWidth() < this.minWidth){
6975                     this.el.setWidth(this.minWidth);
6976                 }
6977                 if(this.hidden){
6978                     this.el.endMeasure();
6979                 }
6980             }
6981         }
6982     },
6983
6984     /**
6985      * Assigns this button's click handler
6986      * @param {Function} handler The function to call when the button is clicked
6987      * @param {Object} scope (optional) Scope for the function passed in
6988      */
6989     setHandler : function(handler, scope){
6990         this.handler = handler;
6991         this.scope = scope;  
6992     },
6993     
6994     /**
6995      * Sets this button's text
6996      * @param {String} text The button text
6997      */
6998     setText : function(text){
6999         this.text = text;
7000         if(this.el){
7001             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7002         }
7003         this.autoWidth();
7004     },
7005     
7006     /**
7007      * Gets the text for this button
7008      * @return {String} The button text
7009      */
7010     getText : function(){
7011         return this.text;  
7012     },
7013     
7014     /**
7015      * Show this button
7016      */
7017     show: function(){
7018         this.hidden = false;
7019         if(this.el){
7020             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7021         }
7022     },
7023     
7024     /**
7025      * Hide this button
7026      */
7027     hide: function(){
7028         this.hidden = true;
7029         if(this.el){
7030             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7031         }
7032     },
7033     
7034     /**
7035      * Convenience function for boolean show/hide
7036      * @param {Boolean} visible True to show, false to hide
7037      */
7038     setVisible: function(visible){
7039         if(visible) {
7040             this.show();
7041         }else{
7042             this.hide();
7043         }
7044     },
7045     
7046     /**
7047      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7048      * @param {Boolean} state (optional) Force a particular state
7049      */
7050     toggle : function(state){
7051         state = state === undefined ? !this.pressed : state;
7052         if(state != this.pressed){
7053             if(state){
7054                 this.el.addClass("x-btn-pressed");
7055                 this.pressed = true;
7056                 this.fireEvent("toggle", this, true);
7057             }else{
7058                 this.el.removeClass("x-btn-pressed");
7059                 this.pressed = false;
7060                 this.fireEvent("toggle", this, false);
7061             }
7062             if(this.toggleHandler){
7063                 this.toggleHandler.call(this.scope || this, this, state);
7064             }
7065         }
7066     },
7067     
7068     /**
7069      * Focus the button
7070      */
7071     focus : function(){
7072         this.el.child('button:first').focus();
7073     },
7074     
7075     /**
7076      * Disable this button
7077      */
7078     disable : function(){
7079         if(this.el){
7080             this.el.addClass("x-btn-disabled");
7081         }
7082         this.disabled = true;
7083     },
7084     
7085     /**
7086      * Enable this button
7087      */
7088     enable : function(){
7089         if(this.el){
7090             this.el.removeClass("x-btn-disabled");
7091         }
7092         this.disabled = false;
7093     },
7094
7095     /**
7096      * Convenience function for boolean enable/disable
7097      * @param {Boolean} enabled True to enable, false to disable
7098      */
7099     setDisabled : function(v){
7100         this[v !== true ? "enable" : "disable"]();
7101     },
7102
7103     // private
7104     onClick : function(e)
7105     {
7106         if(e){
7107             e.preventDefault();
7108         }
7109         if(e.button != 0){
7110             return;
7111         }
7112         if(!this.disabled){
7113             if(this.enableToggle){
7114                 this.toggle();
7115             }
7116             if(this.menu && !this.menu.isVisible()){
7117                 this.menu.show(this.el, this.menuAlign);
7118             }
7119             this.fireEvent("click", this, e);
7120             if(this.handler){
7121                 this.el.removeClass("x-btn-over");
7122                 this.handler.call(this.scope || this, this, e);
7123             }
7124         }
7125     },
7126     // private
7127     onMouseOver : function(e){
7128         if(!this.disabled){
7129             this.el.addClass("x-btn-over");
7130             this.fireEvent('mouseover', this, e);
7131         }
7132     },
7133     // private
7134     onMouseOut : function(e){
7135         if(!e.within(this.el,  true)){
7136             this.el.removeClass("x-btn-over");
7137             this.fireEvent('mouseout', this, e);
7138         }
7139     },
7140     // private
7141     onFocus : function(e){
7142         if(!this.disabled){
7143             this.el.addClass("x-btn-focus");
7144         }
7145     },
7146     // private
7147     onBlur : function(e){
7148         this.el.removeClass("x-btn-focus");
7149     },
7150     // private
7151     onMouseDown : function(e){
7152         if(!this.disabled && e.button == 0){
7153             this.el.addClass("x-btn-click");
7154             Roo.get(document).on('mouseup', this.onMouseUp, this);
7155         }
7156     },
7157     // private
7158     onMouseUp : function(e){
7159         if(e.button == 0){
7160             this.el.removeClass("x-btn-click");
7161             Roo.get(document).un('mouseup', this.onMouseUp, this);
7162         }
7163     },
7164     // private
7165     onMenuShow : function(e){
7166         this.el.addClass("x-btn-menu-active");
7167     },
7168     // private
7169     onMenuHide : function(e){
7170         this.el.removeClass("x-btn-menu-active");
7171     }   
7172 });
7173
7174 // Private utility class used by Button
7175 Roo.ButtonToggleMgr = function(){
7176    var groups = {};
7177    
7178    function toggleGroup(btn, state){
7179        if(state){
7180            var g = groups[btn.toggleGroup];
7181            for(var i = 0, l = g.length; i < l; i++){
7182                if(g[i] != btn){
7183                    g[i].toggle(false);
7184                }
7185            }
7186        }
7187    }
7188    
7189    return {
7190        register : function(btn){
7191            if(!btn.toggleGroup){
7192                return;
7193            }
7194            var g = groups[btn.toggleGroup];
7195            if(!g){
7196                g = groups[btn.toggleGroup] = [];
7197            }
7198            g.push(btn);
7199            btn.on("toggle", toggleGroup);
7200        },
7201        
7202        unregister : function(btn){
7203            if(!btn.toggleGroup){
7204                return;
7205            }
7206            var g = groups[btn.toggleGroup];
7207            if(g){
7208                g.remove(btn);
7209                btn.un("toggle", toggleGroup);
7210            }
7211        }
7212    };
7213 }();/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224 /**
7225  * @class Roo.SplitButton
7226  * @extends Roo.Button
7227  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7228  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7229  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7230  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7231  * @cfg {String} arrowTooltip The title attribute of the arrow
7232  * @constructor
7233  * Create a new menu button
7234  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7235  * @param {Object} config The config object
7236  */
7237 Roo.SplitButton = function(renderTo, config){
7238     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7239     /**
7240      * @event arrowclick
7241      * Fires when this button's arrow is clicked
7242      * @param {SplitButton} this
7243      * @param {EventObject} e The click event
7244      */
7245     this.addEvents({"arrowclick":true});
7246 };
7247
7248 Roo.extend(Roo.SplitButton, Roo.Button, {
7249     render : function(renderTo){
7250         // this is one sweet looking template!
7251         var tpl = new Roo.Template(
7252             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7253             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7254             '<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>',
7255             "</tbody></table></td><td>",
7256             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7257             '<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>',
7258             "</tbody></table></td></tr></table>"
7259         );
7260         var btn = tpl.append(renderTo, [this.text, this.type], true);
7261         var btnEl = btn.child("button");
7262         if(this.cls){
7263             btn.addClass(this.cls);
7264         }
7265         if(this.icon){
7266             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7267         }
7268         if(this.iconCls){
7269             btnEl.addClass(this.iconCls);
7270             if(!this.cls){
7271                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7272             }
7273         }
7274         this.el = btn;
7275         if(this.handleMouseEvents){
7276             btn.on("mouseover", this.onMouseOver, this);
7277             btn.on("mouseout", this.onMouseOut, this);
7278             btn.on("mousedown", this.onMouseDown, this);
7279             btn.on("mouseup", this.onMouseUp, this);
7280         }
7281         btn.on(this.clickEvent, this.onClick, this);
7282         if(this.tooltip){
7283             if(typeof this.tooltip == 'object'){
7284                 Roo.QuickTips.tips(Roo.apply({
7285                       target: btnEl.id
7286                 }, this.tooltip));
7287             } else {
7288                 btnEl.dom[this.tooltipType] = this.tooltip;
7289             }
7290         }
7291         if(this.arrowTooltip){
7292             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7293         }
7294         if(this.hidden){
7295             this.hide();
7296         }
7297         if(this.disabled){
7298             this.disable();
7299         }
7300         if(this.pressed){
7301             this.el.addClass("x-btn-pressed");
7302         }
7303         if(Roo.isIE && !Roo.isIE7){
7304             this.autoWidth.defer(1, this);
7305         }else{
7306             this.autoWidth();
7307         }
7308         if(this.menu){
7309             this.menu.on("show", this.onMenuShow, this);
7310             this.menu.on("hide", this.onMenuHide, this);
7311         }
7312         this.fireEvent('render', this);
7313     },
7314
7315     // private
7316     autoWidth : function(){
7317         if(this.el){
7318             var tbl = this.el.child("table:first");
7319             var tbl2 = this.el.child("table:last");
7320             this.el.setWidth("auto");
7321             tbl.setWidth("auto");
7322             if(Roo.isIE7 && Roo.isStrict){
7323                 var ib = this.el.child('button:first');
7324                 if(ib && ib.getWidth() > 20){
7325                     ib.clip();
7326                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7327                 }
7328             }
7329             if(this.minWidth){
7330                 if(this.hidden){
7331                     this.el.beginMeasure();
7332                 }
7333                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7334                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7335                 }
7336                 if(this.hidden){
7337                     this.el.endMeasure();
7338                 }
7339             }
7340             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7341         } 
7342     },
7343     /**
7344      * Sets this button's click handler
7345      * @param {Function} handler The function to call when the button is clicked
7346      * @param {Object} scope (optional) Scope for the function passed above
7347      */
7348     setHandler : function(handler, scope){
7349         this.handler = handler;
7350         this.scope = scope;  
7351     },
7352     
7353     /**
7354      * Sets this button's arrow click handler
7355      * @param {Function} handler The function to call when the arrow is clicked
7356      * @param {Object} scope (optional) Scope for the function passed above
7357      */
7358     setArrowHandler : function(handler, scope){
7359         this.arrowHandler = handler;
7360         this.scope = scope;  
7361     },
7362     
7363     /**
7364      * Focus the button
7365      */
7366     focus : function(){
7367         if(this.el){
7368             this.el.child("button:first").focus();
7369         }
7370     },
7371
7372     // private
7373     onClick : function(e){
7374         e.preventDefault();
7375         if(!this.disabled){
7376             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7377                 if(this.menu && !this.menu.isVisible()){
7378                     this.menu.show(this.el, this.menuAlign);
7379                 }
7380                 this.fireEvent("arrowclick", this, e);
7381                 if(this.arrowHandler){
7382                     this.arrowHandler.call(this.scope || this, this, e);
7383                 }
7384             }else{
7385                 this.fireEvent("click", this, e);
7386                 if(this.handler){
7387                     this.handler.call(this.scope || this, this, e);
7388                 }
7389             }
7390         }
7391     },
7392     // private
7393     onMouseDown : function(e){
7394         if(!this.disabled){
7395             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7396         }
7397     },
7398     // private
7399     onMouseUp : function(e){
7400         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7401     }   
7402 });
7403
7404
7405 // backwards compat
7406 Roo.MenuButton = Roo.SplitButton;/*
7407  * Based on:
7408  * Ext JS Library 1.1.1
7409  * Copyright(c) 2006-2007, Ext JS, LLC.
7410  *
7411  * Originally Released Under LGPL - original licence link has changed is not relivant.
7412  *
7413  * Fork - LGPL
7414  * <script type="text/javascript">
7415  */
7416
7417 /**
7418  * @class Roo.Toolbar
7419  * Basic Toolbar class.
7420  * @constructor
7421  * Creates a new Toolbar
7422  * @param {Object} container The config object
7423  */ 
7424 Roo.Toolbar = function(container, buttons, config)
7425 {
7426     /// old consturctor format still supported..
7427     if(container instanceof Array){ // omit the container for later rendering
7428         buttons = container;
7429         config = buttons;
7430         container = null;
7431     }
7432     if (typeof(container) == 'object' && container.xtype) {
7433         config = container;
7434         container = config.container;
7435         buttons = config.buttons || []; // not really - use items!!
7436     }
7437     var xitems = [];
7438     if (config && config.items) {
7439         xitems = config.items;
7440         delete config.items;
7441     }
7442     Roo.apply(this, config);
7443     this.buttons = buttons;
7444     
7445     if(container){
7446         this.render(container);
7447     }
7448     this.xitems = xitems;
7449     Roo.each(xitems, function(b) {
7450         this.add(b);
7451     }, this);
7452     
7453 };
7454
7455 Roo.Toolbar.prototype = {
7456     /**
7457      * @cfg {Array} items
7458      * array of button configs or elements to add (will be converted to a MixedCollection)
7459      */
7460     
7461     /**
7462      * @cfg {String/HTMLElement/Element} container
7463      * The id or element that will contain the toolbar
7464      */
7465     // private
7466     render : function(ct){
7467         this.el = Roo.get(ct);
7468         if(this.cls){
7469             this.el.addClass(this.cls);
7470         }
7471         // using a table allows for vertical alignment
7472         // 100% width is needed by Safari...
7473         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7474         this.tr = this.el.child("tr", true);
7475         var autoId = 0;
7476         this.items = new Roo.util.MixedCollection(false, function(o){
7477             return o.id || ("item" + (++autoId));
7478         });
7479         if(this.buttons){
7480             this.add.apply(this, this.buttons);
7481             delete this.buttons;
7482         }
7483     },
7484
7485     /**
7486      * Adds element(s) to the toolbar -- this function takes a variable number of 
7487      * arguments of mixed type and adds them to the toolbar.
7488      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7489      * <ul>
7490      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7491      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7492      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7493      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7494      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7495      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7496      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7497      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7498      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7499      * </ul>
7500      * @param {Mixed} arg2
7501      * @param {Mixed} etc.
7502      */
7503     add : function(){
7504         var a = arguments, l = a.length;
7505         for(var i = 0; i < l; i++){
7506             this._add(a[i]);
7507         }
7508     },
7509     // private..
7510     _add : function(el) {
7511         
7512         if (el.xtype) {
7513             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7514         }
7515         
7516         if (el.applyTo){ // some kind of form field
7517             return this.addField(el);
7518         } 
7519         if (el.render){ // some kind of Toolbar.Item
7520             return this.addItem(el);
7521         }
7522         if (typeof el == "string"){ // string
7523             if(el == "separator" || el == "-"){
7524                 return this.addSeparator();
7525             }
7526             if (el == " "){
7527                 return this.addSpacer();
7528             }
7529             if(el == "->"){
7530                 return this.addFill();
7531             }
7532             return this.addText(el);
7533             
7534         }
7535         if(el.tagName){ // element
7536             return this.addElement(el);
7537         }
7538         if(typeof el == "object"){ // must be button config?
7539             return this.addButton(el);
7540         }
7541         // and now what?!?!
7542         return false;
7543         
7544     },
7545     
7546     /**
7547      * Add an Xtype element
7548      * @param {Object} xtype Xtype Object
7549      * @return {Object} created Object
7550      */
7551     addxtype : function(e){
7552         return this.add(e);  
7553     },
7554     
7555     /**
7556      * Returns the Element for this toolbar.
7557      * @return {Roo.Element}
7558      */
7559     getEl : function(){
7560         return this.el;  
7561     },
7562     
7563     /**
7564      * Adds a separator
7565      * @return {Roo.Toolbar.Item} The separator item
7566      */
7567     addSeparator : function(){
7568         return this.addItem(new Roo.Toolbar.Separator());
7569     },
7570
7571     /**
7572      * Adds a spacer element
7573      * @return {Roo.Toolbar.Spacer} The spacer item
7574      */
7575     addSpacer : function(){
7576         return this.addItem(new Roo.Toolbar.Spacer());
7577     },
7578
7579     /**
7580      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7581      * @return {Roo.Toolbar.Fill} The fill item
7582      */
7583     addFill : function(){
7584         return this.addItem(new Roo.Toolbar.Fill());
7585     },
7586
7587     /**
7588      * Adds any standard HTML element to the toolbar
7589      * @param {String/HTMLElement/Element} el The element or id of the element to add
7590      * @return {Roo.Toolbar.Item} The element's item
7591      */
7592     addElement : function(el){
7593         return this.addItem(new Roo.Toolbar.Item(el));
7594     },
7595     /**
7596      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7597      * @type Roo.util.MixedCollection  
7598      */
7599     items : false,
7600      
7601     /**
7602      * Adds any Toolbar.Item or subclass
7603      * @param {Roo.Toolbar.Item} item
7604      * @return {Roo.Toolbar.Item} The item
7605      */
7606     addItem : function(item){
7607         var td = this.nextBlock();
7608         item.render(td);
7609         this.items.add(item);
7610         return item;
7611     },
7612     
7613     /**
7614      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7615      * @param {Object/Array} config A button config or array of configs
7616      * @return {Roo.Toolbar.Button/Array}
7617      */
7618     addButton : function(config){
7619         if(config instanceof Array){
7620             var buttons = [];
7621             for(var i = 0, len = config.length; i < len; i++) {
7622                 buttons.push(this.addButton(config[i]));
7623             }
7624             return buttons;
7625         }
7626         var b = config;
7627         if(!(config instanceof Roo.Toolbar.Button)){
7628             b = config.split ?
7629                 new Roo.Toolbar.SplitButton(config) :
7630                 new Roo.Toolbar.Button(config);
7631         }
7632         var td = this.nextBlock();
7633         b.render(td);
7634         this.items.add(b);
7635         return b;
7636     },
7637     
7638     /**
7639      * Adds text to the toolbar
7640      * @param {String} text The text to add
7641      * @return {Roo.Toolbar.Item} The element's item
7642      */
7643     addText : function(text){
7644         return this.addItem(new Roo.Toolbar.TextItem(text));
7645     },
7646     
7647     /**
7648      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7649      * @param {Number} index The index where the item is to be inserted
7650      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7651      * @return {Roo.Toolbar.Button/Item}
7652      */
7653     insertButton : function(index, item){
7654         if(item instanceof Array){
7655             var buttons = [];
7656             for(var i = 0, len = item.length; i < len; i++) {
7657                buttons.push(this.insertButton(index + i, item[i]));
7658             }
7659             return buttons;
7660         }
7661         if (!(item instanceof Roo.Toolbar.Button)){
7662            item = new Roo.Toolbar.Button(item);
7663         }
7664         var td = document.createElement("td");
7665         this.tr.insertBefore(td, this.tr.childNodes[index]);
7666         item.render(td);
7667         this.items.insert(index, item);
7668         return item;
7669     },
7670     
7671     /**
7672      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7673      * @param {Object} config
7674      * @return {Roo.Toolbar.Item} The element's item
7675      */
7676     addDom : function(config, returnEl){
7677         var td = this.nextBlock();
7678         Roo.DomHelper.overwrite(td, config);
7679         var ti = new Roo.Toolbar.Item(td.firstChild);
7680         ti.render(td);
7681         this.items.add(ti);
7682         return ti;
7683     },
7684
7685     /**
7686      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7687      * @type Roo.util.MixedCollection  
7688      */
7689     fields : false,
7690     
7691     /**
7692      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7693      * Note: the field should not have been rendered yet. For a field that has already been
7694      * rendered, use {@link #addElement}.
7695      * @param {Roo.form.Field} field
7696      * @return {Roo.ToolbarItem}
7697      */
7698      
7699       
7700     addField : function(field) {
7701         if (!this.fields) {
7702             var autoId = 0;
7703             this.fields = new Roo.util.MixedCollection(false, function(o){
7704                 return o.id || ("item" + (++autoId));
7705             });
7706
7707         }
7708         
7709         var td = this.nextBlock();
7710         field.render(td);
7711         var ti = new Roo.Toolbar.Item(td.firstChild);
7712         ti.render(td);
7713         this.items.add(ti);
7714         this.fields.add(field);
7715         return ti;
7716     },
7717     /**
7718      * Hide the toolbar
7719      * @method hide
7720      */
7721      
7722       
7723     hide : function()
7724     {
7725         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7726         this.el.child('div').hide();
7727     },
7728     /**
7729      * Show the toolbar
7730      * @method show
7731      */
7732     show : function()
7733     {
7734         this.el.child('div').show();
7735     },
7736       
7737     // private
7738     nextBlock : function(){
7739         var td = document.createElement("td");
7740         this.tr.appendChild(td);
7741         return td;
7742     },
7743
7744     // private
7745     destroy : function(){
7746         if(this.items){ // rendered?
7747             Roo.destroy.apply(Roo, this.items.items);
7748         }
7749         if(this.fields){ // rendered?
7750             Roo.destroy.apply(Roo, this.fields.items);
7751         }
7752         Roo.Element.uncache(this.el, this.tr);
7753     }
7754 };
7755
7756 /**
7757  * @class Roo.Toolbar.Item
7758  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7759  * @constructor
7760  * Creates a new Item
7761  * @param {HTMLElement} el 
7762  */
7763 Roo.Toolbar.Item = function(el){
7764     var cfg = {};
7765     if (typeof (el.xtype) != 'undefined') {
7766         cfg = el;
7767         el = cfg.el;
7768     }
7769     
7770     this.el = Roo.getDom(el);
7771     this.id = Roo.id(this.el);
7772     this.hidden = false;
7773     
7774     this.addEvents({
7775          /**
7776              * @event render
7777              * Fires when the button is rendered
7778              * @param {Button} this
7779              */
7780         'render': true
7781     });
7782     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7783 };
7784 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7785 //Roo.Toolbar.Item.prototype = {
7786     
7787     /**
7788      * Get this item's HTML Element
7789      * @return {HTMLElement}
7790      */
7791     getEl : function(){
7792        return this.el;  
7793     },
7794
7795     // private
7796     render : function(td){
7797         
7798          this.td = td;
7799         td.appendChild(this.el);
7800         
7801         this.fireEvent('render', this);
7802     },
7803     
7804     /**
7805      * Removes and destroys this item.
7806      */
7807     destroy : function(){
7808         this.td.parentNode.removeChild(this.td);
7809     },
7810     
7811     /**
7812      * Shows this item.
7813      */
7814     show: function(){
7815         this.hidden = false;
7816         this.td.style.display = "";
7817     },
7818     
7819     /**
7820      * Hides this item.
7821      */
7822     hide: function(){
7823         this.hidden = true;
7824         this.td.style.display = "none";
7825     },
7826     
7827     /**
7828      * Convenience function for boolean show/hide.
7829      * @param {Boolean} visible true to show/false to hide
7830      */
7831     setVisible: function(visible){
7832         if(visible) {
7833             this.show();
7834         }else{
7835             this.hide();
7836         }
7837     },
7838     
7839     /**
7840      * Try to focus this item.
7841      */
7842     focus : function(){
7843         Roo.fly(this.el).focus();
7844     },
7845     
7846     /**
7847      * Disables this item.
7848      */
7849     disable : function(){
7850         Roo.fly(this.td).addClass("x-item-disabled");
7851         this.disabled = true;
7852         this.el.disabled = true;
7853     },
7854     
7855     /**
7856      * Enables this item.
7857      */
7858     enable : function(){
7859         Roo.fly(this.td).removeClass("x-item-disabled");
7860         this.disabled = false;
7861         this.el.disabled = false;
7862     }
7863 });
7864
7865
7866 /**
7867  * @class Roo.Toolbar.Separator
7868  * @extends Roo.Toolbar.Item
7869  * A simple toolbar separator class
7870  * @constructor
7871  * Creates a new Separator
7872  */
7873 Roo.Toolbar.Separator = function(cfg){
7874     
7875     var s = document.createElement("span");
7876     s.className = "ytb-sep";
7877     if (cfg) {
7878         cfg.el = s;
7879     }
7880     
7881     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7882 };
7883 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7884     enable:Roo.emptyFn,
7885     disable:Roo.emptyFn,
7886     focus:Roo.emptyFn
7887 });
7888
7889 /**
7890  * @class Roo.Toolbar.Spacer
7891  * @extends Roo.Toolbar.Item
7892  * A simple element that adds extra horizontal space to a toolbar.
7893  * @constructor
7894  * Creates a new Spacer
7895  */
7896 Roo.Toolbar.Spacer = function(cfg){
7897     var s = document.createElement("div");
7898     s.className = "ytb-spacer";
7899     if (cfg) {
7900         cfg.el = s;
7901     }
7902     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7903 };
7904 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7905     enable:Roo.emptyFn,
7906     disable:Roo.emptyFn,
7907     focus:Roo.emptyFn
7908 });
7909
7910 /**
7911  * @class Roo.Toolbar.Fill
7912  * @extends Roo.Toolbar.Spacer
7913  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7914  * @constructor
7915  * Creates a new Spacer
7916  */
7917 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7918     // private
7919     render : function(td){
7920         td.style.width = '100%';
7921         Roo.Toolbar.Fill.superclass.render.call(this, td);
7922     }
7923 });
7924
7925 /**
7926  * @class Roo.Toolbar.TextItem
7927  * @extends Roo.Toolbar.Item
7928  * A simple class that renders text directly into a toolbar.
7929  * @constructor
7930  * Creates a new TextItem
7931  * @param {String} text
7932  */
7933 Roo.Toolbar.TextItem = function(cfg){
7934     var  text = cfg || "";
7935     if (typeof(cfg) == 'object') {
7936         text = cfg.text || "";
7937     }  else {
7938         cfg = null;
7939     }
7940     var s = document.createElement("span");
7941     s.className = "ytb-text";
7942     s.innerHTML = text;
7943     if (cfg) {
7944         cfg.el  = s;
7945     }
7946     
7947     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7948 };
7949 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7950     
7951      
7952     enable:Roo.emptyFn,
7953     disable:Roo.emptyFn,
7954     focus:Roo.emptyFn
7955 });
7956
7957 /**
7958  * @class Roo.Toolbar.Button
7959  * @extends Roo.Button
7960  * A button that renders into a toolbar.
7961  * @constructor
7962  * Creates a new Button
7963  * @param {Object} config A standard {@link Roo.Button} config object
7964  */
7965 Roo.Toolbar.Button = function(config){
7966     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7967 };
7968 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7969     render : function(td){
7970         this.td = td;
7971         Roo.Toolbar.Button.superclass.render.call(this, td);
7972     },
7973     
7974     /**
7975      * Removes and destroys this button
7976      */
7977     destroy : function(){
7978         Roo.Toolbar.Button.superclass.destroy.call(this);
7979         this.td.parentNode.removeChild(this.td);
7980     },
7981     
7982     /**
7983      * Shows this button
7984      */
7985     show: function(){
7986         this.hidden = false;
7987         this.td.style.display = "";
7988     },
7989     
7990     /**
7991      * Hides this button
7992      */
7993     hide: function(){
7994         this.hidden = true;
7995         this.td.style.display = "none";
7996     },
7997
7998     /**
7999      * Disables this item
8000      */
8001     disable : function(){
8002         Roo.fly(this.td).addClass("x-item-disabled");
8003         this.disabled = true;
8004     },
8005
8006     /**
8007      * Enables this item
8008      */
8009     enable : function(){
8010         Roo.fly(this.td).removeClass("x-item-disabled");
8011         this.disabled = false;
8012     }
8013 });
8014 // backwards compat
8015 Roo.ToolbarButton = Roo.Toolbar.Button;
8016
8017 /**
8018  * @class Roo.Toolbar.SplitButton
8019  * @extends Roo.SplitButton
8020  * A menu button that renders into a toolbar.
8021  * @constructor
8022  * Creates a new SplitButton
8023  * @param {Object} config A standard {@link Roo.SplitButton} config object
8024  */
8025 Roo.Toolbar.SplitButton = function(config){
8026     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8027 };
8028 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8029     render : function(td){
8030         this.td = td;
8031         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8032     },
8033     
8034     /**
8035      * Removes and destroys this button
8036      */
8037     destroy : function(){
8038         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8039         this.td.parentNode.removeChild(this.td);
8040     },
8041     
8042     /**
8043      * Shows this button
8044      */
8045     show: function(){
8046         this.hidden = false;
8047         this.td.style.display = "";
8048     },
8049     
8050     /**
8051      * Hides this button
8052      */
8053     hide: function(){
8054         this.hidden = true;
8055         this.td.style.display = "none";
8056     }
8057 });
8058
8059 // backwards compat
8060 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8061  * Based on:
8062  * Ext JS Library 1.1.1
8063  * Copyright(c) 2006-2007, Ext JS, LLC.
8064  *
8065  * Originally Released Under LGPL - original licence link has changed is not relivant.
8066  *
8067  * Fork - LGPL
8068  * <script type="text/javascript">
8069  */
8070  
8071 /**
8072  * @class Roo.PagingToolbar
8073  * @extends Roo.Toolbar
8074  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8075  * @constructor
8076  * Create a new PagingToolbar
8077  * @param {Object} config The config object
8078  */
8079 Roo.PagingToolbar = function(el, ds, config)
8080 {
8081     // old args format still supported... - xtype is prefered..
8082     if (typeof(el) == 'object' && el.xtype) {
8083         // created from xtype...
8084         config = el;
8085         ds = el.dataSource;
8086         el = config.container;
8087     }
8088     var items = [];
8089     if (config.items) {
8090         items = config.items;
8091         config.items = [];
8092     }
8093     
8094     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8095     this.ds = ds;
8096     this.cursor = 0;
8097     this.renderButtons(this.el);
8098     this.bind(ds);
8099     
8100     // supprot items array.
8101    
8102     Roo.each(items, function(e) {
8103         this.add(Roo.factory(e));
8104     },this);
8105     
8106 };
8107
8108 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8109     /**
8110      * @cfg {Roo.data.Store} dataSource
8111      * The underlying data store providing the paged data
8112      */
8113     /**
8114      * @cfg {String/HTMLElement/Element} container
8115      * container The id or element that will contain the toolbar
8116      */
8117     /**
8118      * @cfg {Boolean} displayInfo
8119      * True to display the displayMsg (defaults to false)
8120      */
8121     /**
8122      * @cfg {Number} pageSize
8123      * The number of records to display per page (defaults to 20)
8124      */
8125     pageSize: 20,
8126     /**
8127      * @cfg {String} displayMsg
8128      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8129      */
8130     displayMsg : 'Displaying {0} - {1} of {2}',
8131     /**
8132      * @cfg {String} emptyMsg
8133      * The message to display when no records are found (defaults to "No data to display")
8134      */
8135     emptyMsg : 'No data to display',
8136     /**
8137      * Customizable piece of the default paging text (defaults to "Page")
8138      * @type String
8139      */
8140     beforePageText : "Page",
8141     /**
8142      * Customizable piece of the default paging text (defaults to "of %0")
8143      * @type String
8144      */
8145     afterPageText : "of {0}",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "First Page")
8148      * @type String
8149      */
8150     firstText : "First Page",
8151     /**
8152      * Customizable piece of the default paging text (defaults to "Previous Page")
8153      * @type String
8154      */
8155     prevText : "Previous Page",
8156     /**
8157      * Customizable piece of the default paging text (defaults to "Next Page")
8158      * @type String
8159      */
8160     nextText : "Next Page",
8161     /**
8162      * Customizable piece of the default paging text (defaults to "Last Page")
8163      * @type String
8164      */
8165     lastText : "Last Page",
8166     /**
8167      * Customizable piece of the default paging text (defaults to "Refresh")
8168      * @type String
8169      */
8170     refreshText : "Refresh",
8171
8172     // private
8173     renderButtons : function(el){
8174         Roo.PagingToolbar.superclass.render.call(this, el);
8175         this.first = this.addButton({
8176             tooltip: this.firstText,
8177             cls: "x-btn-icon x-grid-page-first",
8178             disabled: true,
8179             handler: this.onClick.createDelegate(this, ["first"])
8180         });
8181         this.prev = this.addButton({
8182             tooltip: this.prevText,
8183             cls: "x-btn-icon x-grid-page-prev",
8184             disabled: true,
8185             handler: this.onClick.createDelegate(this, ["prev"])
8186         });
8187         //this.addSeparator();
8188         this.add(this.beforePageText);
8189         this.field = Roo.get(this.addDom({
8190            tag: "input",
8191            type: "text",
8192            size: "3",
8193            value: "1",
8194            cls: "x-grid-page-number"
8195         }).el);
8196         this.field.on("keydown", this.onPagingKeydown, this);
8197         this.field.on("focus", function(){this.dom.select();});
8198         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8199         this.field.setHeight(18);
8200         //this.addSeparator();
8201         this.next = this.addButton({
8202             tooltip: this.nextText,
8203             cls: "x-btn-icon x-grid-page-next",
8204             disabled: true,
8205             handler: this.onClick.createDelegate(this, ["next"])
8206         });
8207         this.last = this.addButton({
8208             tooltip: this.lastText,
8209             cls: "x-btn-icon x-grid-page-last",
8210             disabled: true,
8211             handler: this.onClick.createDelegate(this, ["last"])
8212         });
8213         //this.addSeparator();
8214         this.loading = this.addButton({
8215             tooltip: this.refreshText,
8216             cls: "x-btn-icon x-grid-loading",
8217             handler: this.onClick.createDelegate(this, ["refresh"])
8218         });
8219
8220         if(this.displayInfo){
8221             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8222         }
8223     },
8224
8225     // private
8226     updateInfo : function(){
8227         if(this.displayEl){
8228             var count = this.ds.getCount();
8229             var msg = count == 0 ?
8230                 this.emptyMsg :
8231                 String.format(
8232                     this.displayMsg,
8233                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8234                 );
8235             this.displayEl.update(msg);
8236         }
8237     },
8238
8239     // private
8240     onLoad : function(ds, r, o){
8241        this.cursor = o.params ? o.params.start : 0;
8242        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8243
8244        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8245        this.field.dom.value = ap;
8246        this.first.setDisabled(ap == 1);
8247        this.prev.setDisabled(ap == 1);
8248        this.next.setDisabled(ap == ps);
8249        this.last.setDisabled(ap == ps);
8250        this.loading.enable();
8251        this.updateInfo();
8252     },
8253
8254     // private
8255     getPageData : function(){
8256         var total = this.ds.getTotalCount();
8257         return {
8258             total : total,
8259             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8260             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8261         };
8262     },
8263
8264     // private
8265     onLoadError : function(){
8266         this.loading.enable();
8267     },
8268
8269     // private
8270     onPagingKeydown : function(e){
8271         var k = e.getKey();
8272         var d = this.getPageData();
8273         if(k == e.RETURN){
8274             var v = this.field.dom.value, pageNum;
8275             if(!v || isNaN(pageNum = parseInt(v, 10))){
8276                 this.field.dom.value = d.activePage;
8277                 return;
8278             }
8279             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8280             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8281             e.stopEvent();
8282         }
8283         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))
8284         {
8285           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8286           this.field.dom.value = pageNum;
8287           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8288           e.stopEvent();
8289         }
8290         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8291         {
8292           var v = this.field.dom.value, pageNum; 
8293           var increment = (e.shiftKey) ? 10 : 1;
8294           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8295             increment *= -1;
8296           }
8297           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8298             this.field.dom.value = d.activePage;
8299             return;
8300           }
8301           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8302           {
8303             this.field.dom.value = parseInt(v, 10) + increment;
8304             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8305             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8306           }
8307           e.stopEvent();
8308         }
8309     },
8310
8311     // private
8312     beforeLoad : function(){
8313         if(this.loading){
8314             this.loading.disable();
8315         }
8316     },
8317
8318     // private
8319     onClick : function(which){
8320         var ds = this.ds;
8321         switch(which){
8322             case "first":
8323                 ds.load({params:{start: 0, limit: this.pageSize}});
8324             break;
8325             case "prev":
8326                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8327             break;
8328             case "next":
8329                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8330             break;
8331             case "last":
8332                 var total = ds.getTotalCount();
8333                 var extra = total % this.pageSize;
8334                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8335                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8336             break;
8337             case "refresh":
8338                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8339             break;
8340         }
8341     },
8342
8343     /**
8344      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to unbind
8346      */
8347     unbind : function(ds){
8348         ds.un("beforeload", this.beforeLoad, this);
8349         ds.un("load", this.onLoad, this);
8350         ds.un("loadexception", this.onLoadError, this);
8351         ds.un("remove", this.updateInfo, this);
8352         ds.un("add", this.updateInfo, this);
8353         this.ds = undefined;
8354     },
8355
8356     /**
8357      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8358      * @param {Roo.data.Store} store The data store to bind
8359      */
8360     bind : function(ds){
8361         ds.on("beforeload", this.beforeLoad, this);
8362         ds.on("load", this.onLoad, this);
8363         ds.on("loadexception", this.onLoadError, this);
8364         ds.on("remove", this.updateInfo, this);
8365         ds.on("add", this.updateInfo, this);
8366         this.ds = ds;
8367     }
8368 });/*
8369  * Based on:
8370  * Ext JS Library 1.1.1
8371  * Copyright(c) 2006-2007, Ext JS, LLC.
8372  *
8373  * Originally Released Under LGPL - original licence link has changed is not relivant.
8374  *
8375  * Fork - LGPL
8376  * <script type="text/javascript">
8377  */
8378
8379 /**
8380  * @class Roo.Resizable
8381  * @extends Roo.util.Observable
8382  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8383  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8384  * 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
8385  * the element will be wrapped for you automatically.</p>
8386  * <p>Here is the list of valid resize handles:</p>
8387  * <pre>
8388 Value   Description
8389 ------  -------------------
8390  'n'     north
8391  's'     south
8392  'e'     east
8393  'w'     west
8394  'nw'    northwest
8395  'sw'    southwest
8396  'se'    southeast
8397  'ne'    northeast
8398  'hd'    horizontal drag
8399  'all'   all
8400 </pre>
8401  * <p>Here's an example showing the creation of a typical Resizable:</p>
8402  * <pre><code>
8403 var resizer = new Roo.Resizable("element-id", {
8404     handles: 'all',
8405     minWidth: 200,
8406     minHeight: 100,
8407     maxWidth: 500,
8408     maxHeight: 400,
8409     pinned: true
8410 });
8411 resizer.on("resize", myHandler);
8412 </code></pre>
8413  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8414  * resizer.east.setDisplayed(false);</p>
8415  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8416  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8417  * resize operation's new size (defaults to [0, 0])
8418  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8419  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8420  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8421  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8422  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8423  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8424  * @cfg {Number} width The width of the element in pixels (defaults to null)
8425  * @cfg {Number} height The height of the element in pixels (defaults to null)
8426  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8427  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8428  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8429  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8430  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8431  * in favor of the handles config option (defaults to false)
8432  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8433  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8434  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8435  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8436  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8437  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8438  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8439  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8440  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8441  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8442  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8443  * @constructor
8444  * Create a new resizable component
8445  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8446  * @param {Object} config configuration options
8447   */
8448 Roo.Resizable = function(el, config)
8449 {
8450     this.el = Roo.get(el);
8451
8452     if(config && config.wrap){
8453         config.resizeChild = this.el;
8454         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8455         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8456         this.el.setStyle("overflow", "hidden");
8457         this.el.setPositioning(config.resizeChild.getPositioning());
8458         config.resizeChild.clearPositioning();
8459         if(!config.width || !config.height){
8460             var csize = config.resizeChild.getSize();
8461             this.el.setSize(csize.width, csize.height);
8462         }
8463         if(config.pinned && !config.adjustments){
8464             config.adjustments = "auto";
8465         }
8466     }
8467
8468     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8469     this.proxy.unselectable();
8470     this.proxy.enableDisplayMode('block');
8471
8472     Roo.apply(this, config);
8473
8474     if(this.pinned){
8475         this.disableTrackOver = true;
8476         this.el.addClass("x-resizable-pinned");
8477     }
8478     // if the element isn't positioned, make it relative
8479     var position = this.el.getStyle("position");
8480     if(position != "absolute" && position != "fixed"){
8481         this.el.setStyle("position", "relative");
8482     }
8483     if(!this.handles){ // no handles passed, must be legacy style
8484         this.handles = 's,e,se';
8485         if(this.multiDirectional){
8486             this.handles += ',n,w';
8487         }
8488     }
8489     if(this.handles == "all"){
8490         this.handles = "n s e w ne nw se sw";
8491     }
8492     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8493     var ps = Roo.Resizable.positions;
8494     for(var i = 0, len = hs.length; i < len; i++){
8495         if(hs[i] && ps[hs[i]]){
8496             var pos = ps[hs[i]];
8497             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8498         }
8499     }
8500     // legacy
8501     this.corner = this.southeast;
8502     
8503     // updateBox = the box can move..
8504     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8505         this.updateBox = true;
8506     }
8507
8508     this.activeHandle = null;
8509
8510     if(this.resizeChild){
8511         if(typeof this.resizeChild == "boolean"){
8512             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8513         }else{
8514             this.resizeChild = Roo.get(this.resizeChild, true);
8515         }
8516     }
8517     
8518     if(this.adjustments == "auto"){
8519         var rc = this.resizeChild;
8520         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8521         if(rc && (hw || hn)){
8522             rc.position("relative");
8523             rc.setLeft(hw ? hw.el.getWidth() : 0);
8524             rc.setTop(hn ? hn.el.getHeight() : 0);
8525         }
8526         this.adjustments = [
8527             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8528             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8529         ];
8530     }
8531
8532     if(this.draggable){
8533         this.dd = this.dynamic ?
8534             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8535         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8536     }
8537
8538     // public events
8539     this.addEvents({
8540         /**
8541          * @event beforeresize
8542          * Fired before resize is allowed. Set enabled to false to cancel resize.
8543          * @param {Roo.Resizable} this
8544          * @param {Roo.EventObject} e The mousedown event
8545          */
8546         "beforeresize" : true,
8547         /**
8548          * @event resizing
8549          * Fired a resizing.
8550          * @param {Roo.Resizable} this
8551          * @param {Number} x The new x position
8552          * @param {Number} y The new y position
8553          * @param {Number} w The new w width
8554          * @param {Number} h The new h hight
8555          * @param {Roo.EventObject} e The mouseup event
8556          */
8557         "resizing" : true,
8558         /**
8559          * @event resize
8560          * Fired after a resize.
8561          * @param {Roo.Resizable} this
8562          * @param {Number} width The new width
8563          * @param {Number} height The new height
8564          * @param {Roo.EventObject} e The mouseup event
8565          */
8566         "resize" : true
8567     });
8568
8569     if(this.width !== null && this.height !== null){
8570         this.resizeTo(this.width, this.height);
8571     }else{
8572         this.updateChildSize();
8573     }
8574     if(Roo.isIE){
8575         this.el.dom.style.zoom = 1;
8576     }
8577     Roo.Resizable.superclass.constructor.call(this);
8578 };
8579
8580 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8581         resizeChild : false,
8582         adjustments : [0, 0],
8583         minWidth : 5,
8584         minHeight : 5,
8585         maxWidth : 10000,
8586         maxHeight : 10000,
8587         enabled : true,
8588         animate : false,
8589         duration : .35,
8590         dynamic : false,
8591         handles : false,
8592         multiDirectional : false,
8593         disableTrackOver : false,
8594         easing : 'easeOutStrong',
8595         widthIncrement : 0,
8596         heightIncrement : 0,
8597         pinned : false,
8598         width : null,
8599         height : null,
8600         preserveRatio : false,
8601         transparent: false,
8602         minX: 0,
8603         minY: 0,
8604         draggable: false,
8605
8606         /**
8607          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8608          */
8609         constrainTo: undefined,
8610         /**
8611          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8612          */
8613         resizeRegion: undefined,
8614
8615
8616     /**
8617      * Perform a manual resize
8618      * @param {Number} width
8619      * @param {Number} height
8620      */
8621     resizeTo : function(width, height){
8622         this.el.setSize(width, height);
8623         this.updateChildSize();
8624         this.fireEvent("resize", this, width, height, null);
8625     },
8626
8627     // private
8628     startSizing : function(e, handle){
8629         this.fireEvent("beforeresize", this, e);
8630         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8631
8632             if(!this.overlay){
8633                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8634                 this.overlay.unselectable();
8635                 this.overlay.enableDisplayMode("block");
8636                 this.overlay.on("mousemove", this.onMouseMove, this);
8637                 this.overlay.on("mouseup", this.onMouseUp, this);
8638             }
8639             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8640
8641             this.resizing = true;
8642             this.startBox = this.el.getBox();
8643             this.startPoint = e.getXY();
8644             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8645                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8646
8647             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8648             this.overlay.show();
8649
8650             if(this.constrainTo) {
8651                 var ct = Roo.get(this.constrainTo);
8652                 this.resizeRegion = ct.getRegion().adjust(
8653                     ct.getFrameWidth('t'),
8654                     ct.getFrameWidth('l'),
8655                     -ct.getFrameWidth('b'),
8656                     -ct.getFrameWidth('r')
8657                 );
8658             }
8659
8660             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8661             this.proxy.show();
8662             this.proxy.setBox(this.startBox);
8663             if(!this.dynamic){
8664                 this.proxy.setStyle('visibility', 'visible');
8665             }
8666         }
8667     },
8668
8669     // private
8670     onMouseDown : function(handle, e){
8671         if(this.enabled){
8672             e.stopEvent();
8673             this.activeHandle = handle;
8674             this.startSizing(e, handle);
8675         }
8676     },
8677
8678     // private
8679     onMouseUp : function(e){
8680         var size = this.resizeElement();
8681         this.resizing = false;
8682         this.handleOut();
8683         this.overlay.hide();
8684         this.proxy.hide();
8685         this.fireEvent("resize", this, size.width, size.height, e);
8686     },
8687
8688     // private
8689     updateChildSize : function(){
8690         
8691         if(this.resizeChild){
8692             var el = this.el;
8693             var child = this.resizeChild;
8694             var adj = this.adjustments;
8695             if(el.dom.offsetWidth){
8696                 var b = el.getSize(true);
8697                 child.setSize(b.width+adj[0], b.height+adj[1]);
8698             }
8699             // Second call here for IE
8700             // The first call enables instant resizing and
8701             // the second call corrects scroll bars if they
8702             // exist
8703             if(Roo.isIE){
8704                 setTimeout(function(){
8705                     if(el.dom.offsetWidth){
8706                         var b = el.getSize(true);
8707                         child.setSize(b.width+adj[0], b.height+adj[1]);
8708                     }
8709                 }, 10);
8710             }
8711         }
8712     },
8713
8714     // private
8715     snap : function(value, inc, min){
8716         if(!inc || !value) {
8717             return value;
8718         }
8719         var newValue = value;
8720         var m = value % inc;
8721         if(m > 0){
8722             if(m > (inc/2)){
8723                 newValue = value + (inc-m);
8724             }else{
8725                 newValue = value - m;
8726             }
8727         }
8728         return Math.max(min, newValue);
8729     },
8730
8731     // private
8732     resizeElement : function(){
8733         var box = this.proxy.getBox();
8734         if(this.updateBox){
8735             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8736         }else{
8737             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8738         }
8739         this.updateChildSize();
8740         if(!this.dynamic){
8741             this.proxy.hide();
8742         }
8743         return box;
8744     },
8745
8746     // private
8747     constrain : function(v, diff, m, mx){
8748         if(v - diff < m){
8749             diff = v - m;
8750         }else if(v - diff > mx){
8751             diff = mx - v;
8752         }
8753         return diff;
8754     },
8755
8756     // private
8757     onMouseMove : function(e){
8758         
8759         if(this.enabled){
8760             try{// try catch so if something goes wrong the user doesn't get hung
8761
8762             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8763                 return;
8764             }
8765
8766             //var curXY = this.startPoint;
8767             var curSize = this.curSize || this.startBox;
8768             var x = this.startBox.x, y = this.startBox.y;
8769             var ox = x, oy = y;
8770             var w = curSize.width, h = curSize.height;
8771             var ow = w, oh = h;
8772             var mw = this.minWidth, mh = this.minHeight;
8773             var mxw = this.maxWidth, mxh = this.maxHeight;
8774             var wi = this.widthIncrement;
8775             var hi = this.heightIncrement;
8776
8777             var eventXY = e.getXY();
8778             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8779             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8780
8781             var pos = this.activeHandle.position;
8782
8783             switch(pos){
8784                 case "east":
8785                     w += diffX;
8786                     w = Math.min(Math.max(mw, w), mxw);
8787                     break;
8788              
8789                 case "south":
8790                     h += diffY;
8791                     h = Math.min(Math.max(mh, h), mxh);
8792                     break;
8793                 case "southeast":
8794                     w += diffX;
8795                     h += diffY;
8796                     w = Math.min(Math.max(mw, w), mxw);
8797                     h = Math.min(Math.max(mh, h), mxh);
8798                     break;
8799                 case "north":
8800                     diffY = this.constrain(h, diffY, mh, mxh);
8801                     y += diffY;
8802                     h -= diffY;
8803                     break;
8804                 case "hdrag":
8805                     
8806                     if (wi) {
8807                         var adiffX = Math.abs(diffX);
8808                         var sub = (adiffX % wi); // how much 
8809                         if (sub > (wi/2)) { // far enough to snap
8810                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8811                         } else {
8812                             // remove difference.. 
8813                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8814                         }
8815                     }
8816                     x += diffX;
8817                     x = Math.max(this.minX, x);
8818                     break;
8819                 case "west":
8820                     diffX = this.constrain(w, diffX, mw, mxw);
8821                     x += diffX;
8822                     w -= diffX;
8823                     break;
8824                 case "northeast":
8825                     w += diffX;
8826                     w = Math.min(Math.max(mw, w), mxw);
8827                     diffY = this.constrain(h, diffY, mh, mxh);
8828                     y += diffY;
8829                     h -= diffY;
8830                     break;
8831                 case "northwest":
8832                     diffX = this.constrain(w, diffX, mw, mxw);
8833                     diffY = this.constrain(h, diffY, mh, mxh);
8834                     y += diffY;
8835                     h -= diffY;
8836                     x += diffX;
8837                     w -= diffX;
8838                     break;
8839                case "southwest":
8840                     diffX = this.constrain(w, diffX, mw, mxw);
8841                     h += diffY;
8842                     h = Math.min(Math.max(mh, h), mxh);
8843                     x += diffX;
8844                     w -= diffX;
8845                     break;
8846             }
8847
8848             var sw = this.snap(w, wi, mw);
8849             var sh = this.snap(h, hi, mh);
8850             if(sw != w || sh != h){
8851                 switch(pos){
8852                     case "northeast":
8853                         y -= sh - h;
8854                     break;
8855                     case "north":
8856                         y -= sh - h;
8857                         break;
8858                     case "southwest":
8859                         x -= sw - w;
8860                     break;
8861                     case "west":
8862                         x -= sw - w;
8863                         break;
8864                     case "northwest":
8865                         x -= sw - w;
8866                         y -= sh - h;
8867                     break;
8868                 }
8869                 w = sw;
8870                 h = sh;
8871             }
8872
8873             if(this.preserveRatio){
8874                 switch(pos){
8875                     case "southeast":
8876                     case "east":
8877                         h = oh * (w/ow);
8878                         h = Math.min(Math.max(mh, h), mxh);
8879                         w = ow * (h/oh);
8880                        break;
8881                     case "south":
8882                         w = ow * (h/oh);
8883                         w = Math.min(Math.max(mw, w), mxw);
8884                         h = oh * (w/ow);
8885                         break;
8886                     case "northeast":
8887                         w = ow * (h/oh);
8888                         w = Math.min(Math.max(mw, w), mxw);
8889                         h = oh * (w/ow);
8890                     break;
8891                     case "north":
8892                         var tw = w;
8893                         w = ow * (h/oh);
8894                         w = Math.min(Math.max(mw, w), mxw);
8895                         h = oh * (w/ow);
8896                         x += (tw - w) / 2;
8897                         break;
8898                     case "southwest":
8899                         h = oh * (w/ow);
8900                         h = Math.min(Math.max(mh, h), mxh);
8901                         var tw = w;
8902                         w = ow * (h/oh);
8903                         x += tw - w;
8904                         break;
8905                     case "west":
8906                         var th = h;
8907                         h = oh * (w/ow);
8908                         h = Math.min(Math.max(mh, h), mxh);
8909                         y += (th - h) / 2;
8910                         var tw = w;
8911                         w = ow * (h/oh);
8912                         x += tw - w;
8913                        break;
8914                     case "northwest":
8915                         var tw = w;
8916                         var th = h;
8917                         h = oh * (w/ow);
8918                         h = Math.min(Math.max(mh, h), mxh);
8919                         w = ow * (h/oh);
8920                         y += th - h;
8921                         x += tw - w;
8922                        break;
8923
8924                 }
8925             }
8926             if (pos == 'hdrag') {
8927                 w = ow;
8928             }
8929             this.proxy.setBounds(x, y, w, h);
8930             if(this.dynamic){
8931                 this.resizeElement();
8932             }
8933             }catch(e){}
8934         }
8935         this.fireEvent("resizing", this, x, y, w, h, e);
8936     },
8937
8938     // private
8939     handleOver : function(){
8940         if(this.enabled){
8941             this.el.addClass("x-resizable-over");
8942         }
8943     },
8944
8945     // private
8946     handleOut : function(){
8947         if(!this.resizing){
8948             this.el.removeClass("x-resizable-over");
8949         }
8950     },
8951
8952     /**
8953      * Returns the element this component is bound to.
8954      * @return {Roo.Element}
8955      */
8956     getEl : function(){
8957         return this.el;
8958     },
8959
8960     /**
8961      * Returns the resizeChild element (or null).
8962      * @return {Roo.Element}
8963      */
8964     getResizeChild : function(){
8965         return this.resizeChild;
8966     },
8967     groupHandler : function()
8968     {
8969         
8970     },
8971     /**
8972      * Destroys this resizable. If the element was wrapped and
8973      * removeEl is not true then the element remains.
8974      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8975      */
8976     destroy : function(removeEl){
8977         this.proxy.remove();
8978         if(this.overlay){
8979             this.overlay.removeAllListeners();
8980             this.overlay.remove();
8981         }
8982         var ps = Roo.Resizable.positions;
8983         for(var k in ps){
8984             if(typeof ps[k] != "function" && this[ps[k]]){
8985                 var h = this[ps[k]];
8986                 h.el.removeAllListeners();
8987                 h.el.remove();
8988             }
8989         }
8990         if(removeEl){
8991             this.el.update("");
8992             this.el.remove();
8993         }
8994     }
8995 });
8996
8997 // private
8998 // hash to map config positions to true positions
8999 Roo.Resizable.positions = {
9000     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9001     hd: "hdrag"
9002 };
9003
9004 // private
9005 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9006     if(!this.tpl){
9007         // only initialize the template if resizable is used
9008         var tpl = Roo.DomHelper.createTemplate(
9009             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9010         );
9011         tpl.compile();
9012         Roo.Resizable.Handle.prototype.tpl = tpl;
9013     }
9014     this.position = pos;
9015     this.rz = rz;
9016     // show north drag fro topdra
9017     var handlepos = pos == 'hdrag' ? 'north' : pos;
9018     
9019     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9020     if (pos == 'hdrag') {
9021         this.el.setStyle('cursor', 'pointer');
9022     }
9023     this.el.unselectable();
9024     if(transparent){
9025         this.el.setOpacity(0);
9026     }
9027     this.el.on("mousedown", this.onMouseDown, this);
9028     if(!disableTrackOver){
9029         this.el.on("mouseover", this.onMouseOver, this);
9030         this.el.on("mouseout", this.onMouseOut, this);
9031     }
9032 };
9033
9034 // private
9035 Roo.Resizable.Handle.prototype = {
9036     afterResize : function(rz){
9037         Roo.log('after?');
9038         // do nothing
9039     },
9040     // private
9041     onMouseDown : function(e){
9042         this.rz.onMouseDown(this, e);
9043     },
9044     // private
9045     onMouseOver : function(e){
9046         this.rz.handleOver(this, e);
9047     },
9048     // private
9049     onMouseOut : function(e){
9050         this.rz.handleOut(this, e);
9051     }
9052 };/*
9053  * Based on:
9054  * Ext JS Library 1.1.1
9055  * Copyright(c) 2006-2007, Ext JS, LLC.
9056  *
9057  * Originally Released Under LGPL - original licence link has changed is not relivant.
9058  *
9059  * Fork - LGPL
9060  * <script type="text/javascript">
9061  */
9062
9063 /**
9064  * @class Roo.Editor
9065  * @extends Roo.Component
9066  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9067  * @constructor
9068  * Create a new Editor
9069  * @param {Roo.form.Field} field The Field object (or descendant)
9070  * @param {Object} config The config object
9071  */
9072 Roo.Editor = function(field, config){
9073     Roo.Editor.superclass.constructor.call(this, config);
9074     this.field = field;
9075     this.addEvents({
9076         /**
9077              * @event beforestartedit
9078              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9079              * false from the handler of this event.
9080              * @param {Editor} this
9081              * @param {Roo.Element} boundEl The underlying element bound to this editor
9082              * @param {Mixed} value The field value being set
9083              */
9084         "beforestartedit" : true,
9085         /**
9086              * @event startedit
9087              * Fires when this editor is displayed
9088              * @param {Roo.Element} boundEl The underlying element bound to this editor
9089              * @param {Mixed} value The starting field value
9090              */
9091         "startedit" : true,
9092         /**
9093              * @event beforecomplete
9094              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9095              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9096              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9097              * event will not fire since no edit actually occurred.
9098              * @param {Editor} this
9099              * @param {Mixed} value The current field value
9100              * @param {Mixed} startValue The original field value
9101              */
9102         "beforecomplete" : true,
9103         /**
9104              * @event complete
9105              * Fires after editing is complete and any changed value has been written to the underlying field.
9106              * @param {Editor} this
9107              * @param {Mixed} value The current field value
9108              * @param {Mixed} startValue The original field value
9109              */
9110         "complete" : true,
9111         /**
9112          * @event specialkey
9113          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9114          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9115          * @param {Roo.form.Field} this
9116          * @param {Roo.EventObject} e The event object
9117          */
9118         "specialkey" : true
9119     });
9120 };
9121
9122 Roo.extend(Roo.Editor, Roo.Component, {
9123     /**
9124      * @cfg {Boolean/String} autosize
9125      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9126      * or "height" to adopt the height only (defaults to false)
9127      */
9128     /**
9129      * @cfg {Boolean} revertInvalid
9130      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9131      * validation fails (defaults to true)
9132      */
9133     /**
9134      * @cfg {Boolean} ignoreNoChange
9135      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9136      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9137      * will never be ignored.
9138      */
9139     /**
9140      * @cfg {Boolean} hideEl
9141      * False to keep the bound element visible while the editor is displayed (defaults to true)
9142      */
9143     /**
9144      * @cfg {Mixed} value
9145      * The data value of the underlying field (defaults to "")
9146      */
9147     value : "",
9148     /**
9149      * @cfg {String} alignment
9150      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9151      */
9152     alignment: "c-c?",
9153     /**
9154      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9155      * for bottom-right shadow (defaults to "frame")
9156      */
9157     shadow : "frame",
9158     /**
9159      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9160      */
9161     constrain : false,
9162     /**
9163      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9164      */
9165     completeOnEnter : false,
9166     /**
9167      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9168      */
9169     cancelOnEsc : false,
9170     /**
9171      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9172      */
9173     updateEl : false,
9174
9175     // private
9176     onRender : function(ct, position){
9177         this.el = new Roo.Layer({
9178             shadow: this.shadow,
9179             cls: "x-editor",
9180             parentEl : ct,
9181             shim : this.shim,
9182             shadowOffset:4,
9183             id: this.id,
9184             constrain: this.constrain
9185         });
9186         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9187         if(this.field.msgTarget != 'title'){
9188             this.field.msgTarget = 'qtip';
9189         }
9190         this.field.render(this.el);
9191         if(Roo.isGecko){
9192             this.field.el.dom.setAttribute('autocomplete', 'off');
9193         }
9194         this.field.on("specialkey", this.onSpecialKey, this);
9195         if(this.swallowKeys){
9196             this.field.el.swallowEvent(['keydown','keypress']);
9197         }
9198         this.field.show();
9199         this.field.on("blur", this.onBlur, this);
9200         if(this.field.grow){
9201             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9202         }
9203     },
9204
9205     onSpecialKey : function(field, e)
9206     {
9207         //Roo.log('editor onSpecialKey');
9208         if(this.completeOnEnter && e.getKey() == e.ENTER){
9209             e.stopEvent();
9210             this.completeEdit();
9211             return;
9212         }
9213         // do not fire special key otherwise it might hide close the editor...
9214         if(e.getKey() == e.ENTER){    
9215             return;
9216         }
9217         if(this.cancelOnEsc && e.getKey() == e.ESC){
9218             this.cancelEdit();
9219             return;
9220         } 
9221         this.fireEvent('specialkey', field, e);
9222     
9223     },
9224
9225     /**
9226      * Starts the editing process and shows the editor.
9227      * @param {String/HTMLElement/Element} el The element to edit
9228      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9229       * to the innerHTML of el.
9230      */
9231     startEdit : function(el, value){
9232         if(this.editing){
9233             this.completeEdit();
9234         }
9235         this.boundEl = Roo.get(el);
9236         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9237         if(!this.rendered){
9238             this.render(this.parentEl || document.body);
9239         }
9240         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9241             return;
9242         }
9243         this.startValue = v;
9244         this.field.setValue(v);
9245         if(this.autoSize){
9246             var sz = this.boundEl.getSize();
9247             switch(this.autoSize){
9248                 case "width":
9249                 this.setSize(sz.width,  "");
9250                 break;
9251                 case "height":
9252                 this.setSize("",  sz.height);
9253                 break;
9254                 default:
9255                 this.setSize(sz.width,  sz.height);
9256             }
9257         }
9258         this.el.alignTo(this.boundEl, this.alignment);
9259         this.editing = true;
9260         if(Roo.QuickTips){
9261             Roo.QuickTips.disable();
9262         }
9263         this.show();
9264     },
9265
9266     /**
9267      * Sets the height and width of this editor.
9268      * @param {Number} width The new width
9269      * @param {Number} height The new height
9270      */
9271     setSize : function(w, h){
9272         this.field.setSize(w, h);
9273         if(this.el){
9274             this.el.sync();
9275         }
9276     },
9277
9278     /**
9279      * Realigns the editor to the bound field based on the current alignment config value.
9280      */
9281     realign : function(){
9282         this.el.alignTo(this.boundEl, this.alignment);
9283     },
9284
9285     /**
9286      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9287      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9288      */
9289     completeEdit : function(remainVisible){
9290         if(!this.editing){
9291             return;
9292         }
9293         var v = this.getValue();
9294         if(this.revertInvalid !== false && !this.field.isValid()){
9295             v = this.startValue;
9296             this.cancelEdit(true);
9297         }
9298         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9299             this.editing = false;
9300             this.hide();
9301             return;
9302         }
9303         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9304             this.editing = false;
9305             if(this.updateEl && this.boundEl){
9306                 this.boundEl.update(v);
9307             }
9308             if(remainVisible !== true){
9309                 this.hide();
9310             }
9311             this.fireEvent("complete", this, v, this.startValue);
9312         }
9313     },
9314
9315     // private
9316     onShow : function(){
9317         this.el.show();
9318         if(this.hideEl !== false){
9319             this.boundEl.hide();
9320         }
9321         this.field.show();
9322         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9323             this.fixIEFocus = true;
9324             this.deferredFocus.defer(50, this);
9325         }else{
9326             this.field.focus();
9327         }
9328         this.fireEvent("startedit", this.boundEl, this.startValue);
9329     },
9330
9331     deferredFocus : function(){
9332         if(this.editing){
9333             this.field.focus();
9334         }
9335     },
9336
9337     /**
9338      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9339      * reverted to the original starting value.
9340      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9341      * cancel (defaults to false)
9342      */
9343     cancelEdit : function(remainVisible){
9344         if(this.editing){
9345             this.setValue(this.startValue);
9346             if(remainVisible !== true){
9347                 this.hide();
9348             }
9349         }
9350     },
9351
9352     // private
9353     onBlur : function(){
9354         if(this.allowBlur !== true && this.editing){
9355             this.completeEdit();
9356         }
9357     },
9358
9359     // private
9360     onHide : function(){
9361         if(this.editing){
9362             this.completeEdit();
9363             return;
9364         }
9365         this.field.blur();
9366         if(this.field.collapse){
9367             this.field.collapse();
9368         }
9369         this.el.hide();
9370         if(this.hideEl !== false){
9371             this.boundEl.show();
9372         }
9373         if(Roo.QuickTips){
9374             Roo.QuickTips.enable();
9375         }
9376     },
9377
9378     /**
9379      * Sets the data value of the editor
9380      * @param {Mixed} value Any valid value supported by the underlying field
9381      */
9382     setValue : function(v){
9383         this.field.setValue(v);
9384     },
9385
9386     /**
9387      * Gets the data value of the editor
9388      * @return {Mixed} The data value
9389      */
9390     getValue : function(){
9391         return this.field.getValue();
9392     }
9393 });/*
9394  * Based on:
9395  * Ext JS Library 1.1.1
9396  * Copyright(c) 2006-2007, Ext JS, LLC.
9397  *
9398  * Originally Released Under LGPL - original licence link has changed is not relivant.
9399  *
9400  * Fork - LGPL
9401  * <script type="text/javascript">
9402  */
9403  
9404 /**
9405  * @class Roo.BasicDialog
9406  * @extends Roo.util.Observable
9407  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9408  * <pre><code>
9409 var dlg = new Roo.BasicDialog("my-dlg", {
9410     height: 200,
9411     width: 300,
9412     minHeight: 100,
9413     minWidth: 150,
9414     modal: true,
9415     proxyDrag: true,
9416     shadow: true
9417 });
9418 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9419 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9420 dlg.addButton('Cancel', dlg.hide, dlg);
9421 dlg.show();
9422 </code></pre>
9423   <b>A Dialog should always be a direct child of the body element.</b>
9424  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9425  * @cfg {String} title Default text to display in the title bar (defaults to null)
9426  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9427  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9428  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9429  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9430  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9431  * (defaults to null with no animation)
9432  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9433  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9434  * property for valid values (defaults to 'all')
9435  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9436  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9437  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9438  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9439  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9440  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9441  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9442  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9443  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9444  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9445  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9446  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9447  * draggable = true (defaults to false)
9448  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9449  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9450  * shadow (defaults to false)
9451  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9452  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9453  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9454  * @cfg {Array} buttons Array of buttons
9455  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9456  * @constructor
9457  * Create a new BasicDialog.
9458  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9459  * @param {Object} config Configuration options
9460  */
9461 Roo.BasicDialog = function(el, config){
9462     this.el = Roo.get(el);
9463     var dh = Roo.DomHelper;
9464     if(!this.el && config && config.autoCreate){
9465         if(typeof config.autoCreate == "object"){
9466             if(!config.autoCreate.id){
9467                 config.autoCreate.id = el;
9468             }
9469             this.el = dh.append(document.body,
9470                         config.autoCreate, true);
9471         }else{
9472             this.el = dh.append(document.body,
9473                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9474         }
9475     }
9476     el = this.el;
9477     el.setDisplayed(true);
9478     el.hide = this.hideAction;
9479     this.id = el.id;
9480     el.addClass("x-dlg");
9481
9482     Roo.apply(this, config);
9483
9484     this.proxy = el.createProxy("x-dlg-proxy");
9485     this.proxy.hide = this.hideAction;
9486     this.proxy.setOpacity(.5);
9487     this.proxy.hide();
9488
9489     if(config.width){
9490         el.setWidth(config.width);
9491     }
9492     if(config.height){
9493         el.setHeight(config.height);
9494     }
9495     this.size = el.getSize();
9496     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9497         this.xy = [config.x,config.y];
9498     }else{
9499         this.xy = el.getCenterXY(true);
9500     }
9501     /** The header element @type Roo.Element */
9502     this.header = el.child("> .x-dlg-hd");
9503     /** The body element @type Roo.Element */
9504     this.body = el.child("> .x-dlg-bd");
9505     /** The footer element @type Roo.Element */
9506     this.footer = el.child("> .x-dlg-ft");
9507
9508     if(!this.header){
9509         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9510     }
9511     if(!this.body){
9512         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9513     }
9514
9515     this.header.unselectable();
9516     if(this.title){
9517         this.header.update(this.title);
9518     }
9519     // this element allows the dialog to be focused for keyboard event
9520     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9521     this.focusEl.swallowEvent("click", true);
9522
9523     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9524
9525     // wrap the body and footer for special rendering
9526     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9527     if(this.footer){
9528         this.bwrap.dom.appendChild(this.footer.dom);
9529     }
9530
9531     this.bg = this.el.createChild({
9532         tag: "div", cls:"x-dlg-bg",
9533         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9534     });
9535     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9536
9537
9538     if(this.autoScroll !== false && !this.autoTabs){
9539         this.body.setStyle("overflow", "auto");
9540     }
9541
9542     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9543
9544     if(this.closable !== false){
9545         this.el.addClass("x-dlg-closable");
9546         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9547         this.close.on("click", this.closeClick, this);
9548         this.close.addClassOnOver("x-dlg-close-over");
9549     }
9550     if(this.collapsible !== false){
9551         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9552         this.collapseBtn.on("click", this.collapseClick, this);
9553         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9554         this.header.on("dblclick", this.collapseClick, this);
9555     }
9556     if(this.resizable !== false){
9557         this.el.addClass("x-dlg-resizable");
9558         this.resizer = new Roo.Resizable(el, {
9559             minWidth: this.minWidth || 80,
9560             minHeight:this.minHeight || 80,
9561             handles: this.resizeHandles || "all",
9562             pinned: true
9563         });
9564         this.resizer.on("beforeresize", this.beforeResize, this);
9565         this.resizer.on("resize", this.onResize, this);
9566     }
9567     if(this.draggable !== false){
9568         el.addClass("x-dlg-draggable");
9569         if (!this.proxyDrag) {
9570             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9571         }
9572         else {
9573             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9574         }
9575         dd.setHandleElId(this.header.id);
9576         dd.endDrag = this.endMove.createDelegate(this);
9577         dd.startDrag = this.startMove.createDelegate(this);
9578         dd.onDrag = this.onDrag.createDelegate(this);
9579         dd.scroll = false;
9580         this.dd = dd;
9581     }
9582     if(this.modal){
9583         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9584         this.mask.enableDisplayMode("block");
9585         this.mask.hide();
9586         this.el.addClass("x-dlg-modal");
9587     }
9588     if(this.shadow){
9589         this.shadow = new Roo.Shadow({
9590             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9591             offset : this.shadowOffset
9592         });
9593     }else{
9594         this.shadowOffset = 0;
9595     }
9596     if(Roo.useShims && this.shim !== false){
9597         this.shim = this.el.createShim();
9598         this.shim.hide = this.hideAction;
9599         this.shim.hide();
9600     }else{
9601         this.shim = false;
9602     }
9603     if(this.autoTabs){
9604         this.initTabs();
9605     }
9606     if (this.buttons) { 
9607         var bts= this.buttons;
9608         this.buttons = [];
9609         Roo.each(bts, function(b) {
9610             this.addButton(b);
9611         }, this);
9612     }
9613     
9614     
9615     this.addEvents({
9616         /**
9617          * @event keydown
9618          * Fires when a key is pressed
9619          * @param {Roo.BasicDialog} this
9620          * @param {Roo.EventObject} e
9621          */
9622         "keydown" : true,
9623         /**
9624          * @event move
9625          * Fires when this dialog is moved by the user.
9626          * @param {Roo.BasicDialog} this
9627          * @param {Number} x The new page X
9628          * @param {Number} y The new page Y
9629          */
9630         "move" : true,
9631         /**
9632          * @event resize
9633          * Fires when this dialog is resized by the user.
9634          * @param {Roo.BasicDialog} this
9635          * @param {Number} width The new width
9636          * @param {Number} height The new height
9637          */
9638         "resize" : true,
9639         /**
9640          * @event beforehide
9641          * Fires before this dialog is hidden.
9642          * @param {Roo.BasicDialog} this
9643          */
9644         "beforehide" : true,
9645         /**
9646          * @event hide
9647          * Fires when this dialog is hidden.
9648          * @param {Roo.BasicDialog} this
9649          */
9650         "hide" : true,
9651         /**
9652          * @event beforeshow
9653          * Fires before this dialog is shown.
9654          * @param {Roo.BasicDialog} this
9655          */
9656         "beforeshow" : true,
9657         /**
9658          * @event show
9659          * Fires when this dialog is shown.
9660          * @param {Roo.BasicDialog} this
9661          */
9662         "show" : true
9663     });
9664     el.on("keydown", this.onKeyDown, this);
9665     el.on("mousedown", this.toFront, this);
9666     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9667     this.el.hide();
9668     Roo.DialogManager.register(this);
9669     Roo.BasicDialog.superclass.constructor.call(this);
9670 };
9671
9672 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9673     shadowOffset: Roo.isIE ? 6 : 5,
9674     minHeight: 80,
9675     minWidth: 200,
9676     minButtonWidth: 75,
9677     defaultButton: null,
9678     buttonAlign: "right",
9679     tabTag: 'div',
9680     firstShow: true,
9681
9682     /**
9683      * Sets the dialog title text
9684      * @param {String} text The title text to display
9685      * @return {Roo.BasicDialog} this
9686      */
9687     setTitle : function(text){
9688         this.header.update(text);
9689         return this;
9690     },
9691
9692     // private
9693     closeClick : function(){
9694         this.hide();
9695     },
9696
9697     // private
9698     collapseClick : function(){
9699         this[this.collapsed ? "expand" : "collapse"]();
9700     },
9701
9702     /**
9703      * Collapses the dialog to its minimized state (only the title bar is visible).
9704      * Equivalent to the user clicking the collapse dialog button.
9705      */
9706     collapse : function(){
9707         if(!this.collapsed){
9708             this.collapsed = true;
9709             this.el.addClass("x-dlg-collapsed");
9710             this.restoreHeight = this.el.getHeight();
9711             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9712         }
9713     },
9714
9715     /**
9716      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9717      * clicking the expand dialog button.
9718      */
9719     expand : function(){
9720         if(this.collapsed){
9721             this.collapsed = false;
9722             this.el.removeClass("x-dlg-collapsed");
9723             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9724         }
9725     },
9726
9727     /**
9728      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9729      * @return {Roo.TabPanel} The tabs component
9730      */
9731     initTabs : function(){
9732         var tabs = this.getTabs();
9733         while(tabs.getTab(0)){
9734             tabs.removeTab(0);
9735         }
9736         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9737             var dom = el.dom;
9738             tabs.addTab(Roo.id(dom), dom.title);
9739             dom.title = "";
9740         });
9741         tabs.activate(0);
9742         return tabs;
9743     },
9744
9745     // private
9746     beforeResize : function(){
9747         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9748     },
9749
9750     // private
9751     onResize : function(){
9752         this.refreshSize();
9753         this.syncBodyHeight();
9754         this.adjustAssets();
9755         this.focus();
9756         this.fireEvent("resize", this, this.size.width, this.size.height);
9757     },
9758
9759     // private
9760     onKeyDown : function(e){
9761         if(this.isVisible()){
9762             this.fireEvent("keydown", this, e);
9763         }
9764     },
9765
9766     /**
9767      * Resizes the dialog.
9768      * @param {Number} width
9769      * @param {Number} height
9770      * @return {Roo.BasicDialog} this
9771      */
9772     resizeTo : function(width, height){
9773         this.el.setSize(width, height);
9774         this.size = {width: width, height: height};
9775         this.syncBodyHeight();
9776         if(this.fixedcenter){
9777             this.center();
9778         }
9779         if(this.isVisible()){
9780             this.constrainXY();
9781             this.adjustAssets();
9782         }
9783         this.fireEvent("resize", this, width, height);
9784         return this;
9785     },
9786
9787
9788     /**
9789      * Resizes the dialog to fit the specified content size.
9790      * @param {Number} width
9791      * @param {Number} height
9792      * @return {Roo.BasicDialog} this
9793      */
9794     setContentSize : function(w, h){
9795         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9796         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9797         //if(!this.el.isBorderBox()){
9798             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9799             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9800         //}
9801         if(this.tabs){
9802             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9803             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9804         }
9805         this.resizeTo(w, h);
9806         return this;
9807     },
9808
9809     /**
9810      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9811      * executed in response to a particular key being pressed while the dialog is active.
9812      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9813      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9814      * @param {Function} fn The function to call
9815      * @param {Object} scope (optional) The scope of the function
9816      * @return {Roo.BasicDialog} this
9817      */
9818     addKeyListener : function(key, fn, scope){
9819         var keyCode, shift, ctrl, alt;
9820         if(typeof key == "object" && !(key instanceof Array)){
9821             keyCode = key["key"];
9822             shift = key["shift"];
9823             ctrl = key["ctrl"];
9824             alt = key["alt"];
9825         }else{
9826             keyCode = key;
9827         }
9828         var handler = function(dlg, e){
9829             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9830                 var k = e.getKey();
9831                 if(keyCode instanceof Array){
9832                     for(var i = 0, len = keyCode.length; i < len; i++){
9833                         if(keyCode[i] == k){
9834                           fn.call(scope || window, dlg, k, e);
9835                           return;
9836                         }
9837                     }
9838                 }else{
9839                     if(k == keyCode){
9840                         fn.call(scope || window, dlg, k, e);
9841                     }
9842                 }
9843             }
9844         };
9845         this.on("keydown", handler);
9846         return this;
9847     },
9848
9849     /**
9850      * Returns the TabPanel component (creates it if it doesn't exist).
9851      * Note: If you wish to simply check for the existence of tabs without creating them,
9852      * check for a null 'tabs' property.
9853      * @return {Roo.TabPanel} The tabs component
9854      */
9855     getTabs : function(){
9856         if(!this.tabs){
9857             this.el.addClass("x-dlg-auto-tabs");
9858             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9859             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9860         }
9861         return this.tabs;
9862     },
9863
9864     /**
9865      * Adds a button to the footer section of the dialog.
9866      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9867      * object or a valid Roo.DomHelper element config
9868      * @param {Function} handler The function called when the button is clicked
9869      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9870      * @return {Roo.Button} The new button
9871      */
9872     addButton : function(config, handler, scope){
9873         var dh = Roo.DomHelper;
9874         if(!this.footer){
9875             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9876         }
9877         if(!this.btnContainer){
9878             var tb = this.footer.createChild({
9879
9880                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9881                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9882             }, null, true);
9883             this.btnContainer = tb.firstChild.firstChild.firstChild;
9884         }
9885         var bconfig = {
9886             handler: handler,
9887             scope: scope,
9888             minWidth: this.minButtonWidth,
9889             hideParent:true
9890         };
9891         if(typeof config == "string"){
9892             bconfig.text = config;
9893         }else{
9894             if(config.tag){
9895                 bconfig.dhconfig = config;
9896             }else{
9897                 Roo.apply(bconfig, config);
9898             }
9899         }
9900         var fc = false;
9901         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9902             bconfig.position = Math.max(0, bconfig.position);
9903             fc = this.btnContainer.childNodes[bconfig.position];
9904         }
9905          
9906         var btn = new Roo.Button(
9907             fc ? 
9908                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9909                 : this.btnContainer.appendChild(document.createElement("td")),
9910             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9911             bconfig
9912         );
9913         this.syncBodyHeight();
9914         if(!this.buttons){
9915             /**
9916              * Array of all the buttons that have been added to this dialog via addButton
9917              * @type Array
9918              */
9919             this.buttons = [];
9920         }
9921         this.buttons.push(btn);
9922         return btn;
9923     },
9924
9925     /**
9926      * Sets the default button to be focused when the dialog is displayed.
9927      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9928      * @return {Roo.BasicDialog} this
9929      */
9930     setDefaultButton : function(btn){
9931         this.defaultButton = btn;
9932         return this;
9933     },
9934
9935     // private
9936     getHeaderFooterHeight : function(safe){
9937         var height = 0;
9938         if(this.header){
9939            height += this.header.getHeight();
9940         }
9941         if(this.footer){
9942            var fm = this.footer.getMargins();
9943             height += (this.footer.getHeight()+fm.top+fm.bottom);
9944         }
9945         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9946         height += this.centerBg.getPadding("tb");
9947         return height;
9948     },
9949
9950     // private
9951     syncBodyHeight : function()
9952     {
9953         var bd = this.body, // the text
9954             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9955             bw = this.bwrap;
9956         var height = this.size.height - this.getHeaderFooterHeight(false);
9957         bd.setHeight(height-bd.getMargins("tb"));
9958         var hh = this.header.getHeight();
9959         var h = this.size.height-hh;
9960         cb.setHeight(h);
9961         
9962         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9963         bw.setHeight(h-cb.getPadding("tb"));
9964         
9965         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9966         bd.setWidth(bw.getWidth(true));
9967         if(this.tabs){
9968             this.tabs.syncHeight();
9969             if(Roo.isIE){
9970                 this.tabs.el.repaint();
9971             }
9972         }
9973     },
9974
9975     /**
9976      * Restores the previous state of the dialog if Roo.state is configured.
9977      * @return {Roo.BasicDialog} this
9978      */
9979     restoreState : function(){
9980         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9981         if(box && box.width){
9982             this.xy = [box.x, box.y];
9983             this.resizeTo(box.width, box.height);
9984         }
9985         return this;
9986     },
9987
9988     // private
9989     beforeShow : function(){
9990         this.expand();
9991         if(this.fixedcenter){
9992             this.xy = this.el.getCenterXY(true);
9993         }
9994         if(this.modal){
9995             Roo.get(document.body).addClass("x-body-masked");
9996             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9997             this.mask.show();
9998         }
9999         this.constrainXY();
10000     },
10001
10002     // private
10003     animShow : function(){
10004         var b = Roo.get(this.animateTarget).getBox();
10005         this.proxy.setSize(b.width, b.height);
10006         this.proxy.setLocation(b.x, b.y);
10007         this.proxy.show();
10008         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10009                     true, .35, this.showEl.createDelegate(this));
10010     },
10011
10012     /**
10013      * Shows the dialog.
10014      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10015      * @return {Roo.BasicDialog} this
10016      */
10017     show : function(animateTarget){
10018         if (this.fireEvent("beforeshow", this) === false){
10019             return;
10020         }
10021         if(this.syncHeightBeforeShow){
10022             this.syncBodyHeight();
10023         }else if(this.firstShow){
10024             this.firstShow = false;
10025             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10026         }
10027         this.animateTarget = animateTarget || this.animateTarget;
10028         if(!this.el.isVisible()){
10029             this.beforeShow();
10030             if(this.animateTarget && Roo.get(this.animateTarget)){
10031                 this.animShow();
10032             }else{
10033                 this.showEl();
10034             }
10035         }
10036         return this;
10037     },
10038
10039     // private
10040     showEl : function(){
10041         this.proxy.hide();
10042         this.el.setXY(this.xy);
10043         this.el.show();
10044         this.adjustAssets(true);
10045         this.toFront();
10046         this.focus();
10047         // IE peekaboo bug - fix found by Dave Fenwick
10048         if(Roo.isIE){
10049             this.el.repaint();
10050         }
10051         this.fireEvent("show", this);
10052     },
10053
10054     /**
10055      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10056      * dialog itself will receive focus.
10057      */
10058     focus : function(){
10059         if(this.defaultButton){
10060             this.defaultButton.focus();
10061         }else{
10062             this.focusEl.focus();
10063         }
10064     },
10065
10066     // private
10067     constrainXY : function(){
10068         if(this.constraintoviewport !== false){
10069             if(!this.viewSize){
10070                 if(this.container){
10071                     var s = this.container.getSize();
10072                     this.viewSize = [s.width, s.height];
10073                 }else{
10074                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10075                 }
10076             }
10077             var s = Roo.get(this.container||document).getScroll();
10078
10079             var x = this.xy[0], y = this.xy[1];
10080             var w = this.size.width, h = this.size.height;
10081             var vw = this.viewSize[0], vh = this.viewSize[1];
10082             // only move it if it needs it
10083             var moved = false;
10084             // first validate right/bottom
10085             if(x + w > vw+s.left){
10086                 x = vw - w;
10087                 moved = true;
10088             }
10089             if(y + h > vh+s.top){
10090                 y = vh - h;
10091                 moved = true;
10092             }
10093             // then make sure top/left isn't negative
10094             if(x < s.left){
10095                 x = s.left;
10096                 moved = true;
10097             }
10098             if(y < s.top){
10099                 y = s.top;
10100                 moved = true;
10101             }
10102             if(moved){
10103                 // cache xy
10104                 this.xy = [x, y];
10105                 if(this.isVisible()){
10106                     this.el.setLocation(x, y);
10107                     this.adjustAssets();
10108                 }
10109             }
10110         }
10111     },
10112
10113     // private
10114     onDrag : function(){
10115         if(!this.proxyDrag){
10116             this.xy = this.el.getXY();
10117             this.adjustAssets();
10118         }
10119     },
10120
10121     // private
10122     adjustAssets : function(doShow){
10123         var x = this.xy[0], y = this.xy[1];
10124         var w = this.size.width, h = this.size.height;
10125         if(doShow === true){
10126             if(this.shadow){
10127                 this.shadow.show(this.el);
10128             }
10129             if(this.shim){
10130                 this.shim.show();
10131             }
10132         }
10133         if(this.shadow && this.shadow.isVisible()){
10134             this.shadow.show(this.el);
10135         }
10136         if(this.shim && this.shim.isVisible()){
10137             this.shim.setBounds(x, y, w, h);
10138         }
10139     },
10140
10141     // private
10142     adjustViewport : function(w, h){
10143         if(!w || !h){
10144             w = Roo.lib.Dom.getViewWidth();
10145             h = Roo.lib.Dom.getViewHeight();
10146         }
10147         // cache the size
10148         this.viewSize = [w, h];
10149         if(this.modal && this.mask.isVisible()){
10150             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10151             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10152         }
10153         if(this.isVisible()){
10154             this.constrainXY();
10155         }
10156     },
10157
10158     /**
10159      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10160      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10161      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10162      */
10163     destroy : function(removeEl){
10164         if(this.isVisible()){
10165             this.animateTarget = null;
10166             this.hide();
10167         }
10168         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10169         if(this.tabs){
10170             this.tabs.destroy(removeEl);
10171         }
10172         Roo.destroy(
10173              this.shim,
10174              this.proxy,
10175              this.resizer,
10176              this.close,
10177              this.mask
10178         );
10179         if(this.dd){
10180             this.dd.unreg();
10181         }
10182         if(this.buttons){
10183            for(var i = 0, len = this.buttons.length; i < len; i++){
10184                this.buttons[i].destroy();
10185            }
10186         }
10187         this.el.removeAllListeners();
10188         if(removeEl === true){
10189             this.el.update("");
10190             this.el.remove();
10191         }
10192         Roo.DialogManager.unregister(this);
10193     },
10194
10195     // private
10196     startMove : function(){
10197         if(this.proxyDrag){
10198             this.proxy.show();
10199         }
10200         if(this.constraintoviewport !== false){
10201             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10202         }
10203     },
10204
10205     // private
10206     endMove : function(){
10207         if(!this.proxyDrag){
10208             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10209         }else{
10210             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10211             this.proxy.hide();
10212         }
10213         this.refreshSize();
10214         this.adjustAssets();
10215         this.focus();
10216         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10217     },
10218
10219     /**
10220      * Brings this dialog to the front of any other visible dialogs
10221      * @return {Roo.BasicDialog} this
10222      */
10223     toFront : function(){
10224         Roo.DialogManager.bringToFront(this);
10225         return this;
10226     },
10227
10228     /**
10229      * Sends this dialog to the back (under) of any other visible dialogs
10230      * @return {Roo.BasicDialog} this
10231      */
10232     toBack : function(){
10233         Roo.DialogManager.sendToBack(this);
10234         return this;
10235     },
10236
10237     /**
10238      * Centers this dialog in the viewport
10239      * @return {Roo.BasicDialog} this
10240      */
10241     center : function(){
10242         var xy = this.el.getCenterXY(true);
10243         this.moveTo(xy[0], xy[1]);
10244         return this;
10245     },
10246
10247     /**
10248      * Moves the dialog's top-left corner to the specified point
10249      * @param {Number} x
10250      * @param {Number} y
10251      * @return {Roo.BasicDialog} this
10252      */
10253     moveTo : function(x, y){
10254         this.xy = [x,y];
10255         if(this.isVisible()){
10256             this.el.setXY(this.xy);
10257             this.adjustAssets();
10258         }
10259         return this;
10260     },
10261
10262     /**
10263      * Aligns the dialog to the specified element
10264      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10265      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10266      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10267      * @return {Roo.BasicDialog} this
10268      */
10269     alignTo : function(element, position, offsets){
10270         this.xy = this.el.getAlignToXY(element, position, offsets);
10271         if(this.isVisible()){
10272             this.el.setXY(this.xy);
10273             this.adjustAssets();
10274         }
10275         return this;
10276     },
10277
10278     /**
10279      * Anchors an element to another element and realigns it when the window is resized.
10280      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10281      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10282      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10283      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10284      * is a number, it is used as the buffer delay (defaults to 50ms).
10285      * @return {Roo.BasicDialog} this
10286      */
10287     anchorTo : function(el, alignment, offsets, monitorScroll){
10288         var action = function(){
10289             this.alignTo(el, alignment, offsets);
10290         };
10291         Roo.EventManager.onWindowResize(action, this);
10292         var tm = typeof monitorScroll;
10293         if(tm != 'undefined'){
10294             Roo.EventManager.on(window, 'scroll', action, this,
10295                 {buffer: tm == 'number' ? monitorScroll : 50});
10296         }
10297         action.call(this);
10298         return this;
10299     },
10300
10301     /**
10302      * Returns true if the dialog is visible
10303      * @return {Boolean}
10304      */
10305     isVisible : function(){
10306         return this.el.isVisible();
10307     },
10308
10309     // private
10310     animHide : function(callback){
10311         var b = Roo.get(this.animateTarget).getBox();
10312         this.proxy.show();
10313         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10314         this.el.hide();
10315         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10316                     this.hideEl.createDelegate(this, [callback]));
10317     },
10318
10319     /**
10320      * Hides the dialog.
10321      * @param {Function} callback (optional) Function to call when the dialog is hidden
10322      * @return {Roo.BasicDialog} this
10323      */
10324     hide : function(callback){
10325         if (this.fireEvent("beforehide", this) === false){
10326             return;
10327         }
10328         if(this.shadow){
10329             this.shadow.hide();
10330         }
10331         if(this.shim) {
10332           this.shim.hide();
10333         }
10334         // sometimes animateTarget seems to get set.. causing problems...
10335         // this just double checks..
10336         if(this.animateTarget && Roo.get(this.animateTarget)) {
10337            this.animHide(callback);
10338         }else{
10339             this.el.hide();
10340             this.hideEl(callback);
10341         }
10342         return this;
10343     },
10344
10345     // private
10346     hideEl : function(callback){
10347         this.proxy.hide();
10348         if(this.modal){
10349             this.mask.hide();
10350             Roo.get(document.body).removeClass("x-body-masked");
10351         }
10352         this.fireEvent("hide", this);
10353         if(typeof callback == "function"){
10354             callback();
10355         }
10356     },
10357
10358     // private
10359     hideAction : function(){
10360         this.setLeft("-10000px");
10361         this.setTop("-10000px");
10362         this.setStyle("visibility", "hidden");
10363     },
10364
10365     // private
10366     refreshSize : function(){
10367         this.size = this.el.getSize();
10368         this.xy = this.el.getXY();
10369         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10370     },
10371
10372     // private
10373     // z-index is managed by the DialogManager and may be overwritten at any time
10374     setZIndex : function(index){
10375         if(this.modal){
10376             this.mask.setStyle("z-index", index);
10377         }
10378         if(this.shim){
10379             this.shim.setStyle("z-index", ++index);
10380         }
10381         if(this.shadow){
10382             this.shadow.setZIndex(++index);
10383         }
10384         this.el.setStyle("z-index", ++index);
10385         if(this.proxy){
10386             this.proxy.setStyle("z-index", ++index);
10387         }
10388         if(this.resizer){
10389             this.resizer.proxy.setStyle("z-index", ++index);
10390         }
10391
10392         this.lastZIndex = index;
10393     },
10394
10395     /**
10396      * Returns the element for this dialog
10397      * @return {Roo.Element} The underlying dialog Element
10398      */
10399     getEl : function(){
10400         return this.el;
10401     }
10402 });
10403
10404 /**
10405  * @class Roo.DialogManager
10406  * Provides global access to BasicDialogs that have been created and
10407  * support for z-indexing (layering) multiple open dialogs.
10408  */
10409 Roo.DialogManager = function(){
10410     var list = {};
10411     var accessList = [];
10412     var front = null;
10413
10414     // private
10415     var sortDialogs = function(d1, d2){
10416         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10417     };
10418
10419     // private
10420     var orderDialogs = function(){
10421         accessList.sort(sortDialogs);
10422         var seed = Roo.DialogManager.zseed;
10423         for(var i = 0, len = accessList.length; i < len; i++){
10424             var dlg = accessList[i];
10425             if(dlg){
10426                 dlg.setZIndex(seed + (i*10));
10427             }
10428         }
10429     };
10430
10431     return {
10432         /**
10433          * The starting z-index for BasicDialogs (defaults to 9000)
10434          * @type Number The z-index value
10435          */
10436         zseed : 9000,
10437
10438         // private
10439         register : function(dlg){
10440             list[dlg.id] = dlg;
10441             accessList.push(dlg);
10442         },
10443
10444         // private
10445         unregister : function(dlg){
10446             delete list[dlg.id];
10447             var i=0;
10448             var len=0;
10449             if(!accessList.indexOf){
10450                 for(  i = 0, len = accessList.length; i < len; i++){
10451                     if(accessList[i] == dlg){
10452                         accessList.splice(i, 1);
10453                         return;
10454                     }
10455                 }
10456             }else{
10457                  i = accessList.indexOf(dlg);
10458                 if(i != -1){
10459                     accessList.splice(i, 1);
10460                 }
10461             }
10462         },
10463
10464         /**
10465          * Gets a registered dialog by id
10466          * @param {String/Object} id The id of the dialog or a dialog
10467          * @return {Roo.BasicDialog} this
10468          */
10469         get : function(id){
10470             return typeof id == "object" ? id : list[id];
10471         },
10472
10473         /**
10474          * Brings the specified dialog to the front
10475          * @param {String/Object} dlg The id of the dialog or a dialog
10476          * @return {Roo.BasicDialog} this
10477          */
10478         bringToFront : function(dlg){
10479             dlg = this.get(dlg);
10480             if(dlg != front){
10481                 front = dlg;
10482                 dlg._lastAccess = new Date().getTime();
10483                 orderDialogs();
10484             }
10485             return dlg;
10486         },
10487
10488         /**
10489          * Sends the specified dialog to the back
10490          * @param {String/Object} dlg The id of the dialog or a dialog
10491          * @return {Roo.BasicDialog} this
10492          */
10493         sendToBack : function(dlg){
10494             dlg = this.get(dlg);
10495             dlg._lastAccess = -(new Date().getTime());
10496             orderDialogs();
10497             return dlg;
10498         },
10499
10500         /**
10501          * Hides all dialogs
10502          */
10503         hideAll : function(){
10504             for(var id in list){
10505                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10506                     list[id].hide();
10507                 }
10508             }
10509         }
10510     };
10511 }();
10512
10513 /**
10514  * @class Roo.LayoutDialog
10515  * @extends Roo.BasicDialog
10516  * Dialog which provides adjustments for working with a layout in a Dialog.
10517  * Add your necessary layout config options to the dialog's config.<br>
10518  * Example usage (including a nested layout):
10519  * <pre><code>
10520 if(!dialog){
10521     dialog = new Roo.LayoutDialog("download-dlg", {
10522         modal: true,
10523         width:600,
10524         height:450,
10525         shadow:true,
10526         minWidth:500,
10527         minHeight:350,
10528         autoTabs:true,
10529         proxyDrag:true,
10530         // layout config merges with the dialog config
10531         center:{
10532             tabPosition: "top",
10533             alwaysShowTabs: true
10534         }
10535     });
10536     dialog.addKeyListener(27, dialog.hide, dialog);
10537     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10538     dialog.addButton("Build It!", this.getDownload, this);
10539
10540     // we can even add nested layouts
10541     var innerLayout = new Roo.BorderLayout("dl-inner", {
10542         east: {
10543             initialSize: 200,
10544             autoScroll:true,
10545             split:true
10546         },
10547         center: {
10548             autoScroll:true
10549         }
10550     });
10551     innerLayout.beginUpdate();
10552     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10553     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10554     innerLayout.endUpdate(true);
10555
10556     var layout = dialog.getLayout();
10557     layout.beginUpdate();
10558     layout.add("center", new Roo.ContentPanel("standard-panel",
10559                         {title: "Download the Source", fitToFrame:true}));
10560     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10561                {title: "Build your own roo.js"}));
10562     layout.getRegion("center").showPanel(sp);
10563     layout.endUpdate();
10564 }
10565 </code></pre>
10566     * @constructor
10567     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10568     * @param {Object} config configuration options
10569   */
10570 Roo.LayoutDialog = function(el, cfg){
10571     
10572     var config=  cfg;
10573     if (typeof(cfg) == 'undefined') {
10574         config = Roo.apply({}, el);
10575         // not sure why we use documentElement here.. - it should always be body.
10576         // IE7 borks horribly if we use documentElement.
10577         // webkit also does not like documentElement - it creates a body element...
10578         el = Roo.get( document.body || document.documentElement ).createChild();
10579         //config.autoCreate = true;
10580     }
10581     
10582     
10583     config.autoTabs = false;
10584     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10585     this.body.setStyle({overflow:"hidden", position:"relative"});
10586     this.layout = new Roo.BorderLayout(this.body.dom, config);
10587     this.layout.monitorWindowResize = false;
10588     this.el.addClass("x-dlg-auto-layout");
10589     // fix case when center region overwrites center function
10590     this.center = Roo.BasicDialog.prototype.center;
10591     this.on("show", this.layout.layout, this.layout, true);
10592     if (config.items) {
10593         var xitems = config.items;
10594         delete config.items;
10595         Roo.each(xitems, this.addxtype, this);
10596     }
10597     
10598     
10599 };
10600 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10601     /**
10602      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10603      * @deprecated
10604      */
10605     endUpdate : function(){
10606         this.layout.endUpdate();
10607     },
10608
10609     /**
10610      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10611      *  @deprecated
10612      */
10613     beginUpdate : function(){
10614         this.layout.beginUpdate();
10615     },
10616
10617     /**
10618      * Get the BorderLayout for this dialog
10619      * @return {Roo.BorderLayout}
10620      */
10621     getLayout : function(){
10622         return this.layout;
10623     },
10624
10625     showEl : function(){
10626         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10627         if(Roo.isIE7){
10628             this.layout.layout();
10629         }
10630     },
10631
10632     // private
10633     // Use the syncHeightBeforeShow config option to control this automatically
10634     syncBodyHeight : function(){
10635         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10636         if(this.layout){this.layout.layout();}
10637     },
10638     
10639       /**
10640      * Add an xtype element (actually adds to the layout.)
10641      * @return {Object} xdata xtype object data.
10642      */
10643     
10644     addxtype : function(c) {
10645         return this.layout.addxtype(c);
10646     }
10647 });/*
10648  * Based on:
10649  * Ext JS Library 1.1.1
10650  * Copyright(c) 2006-2007, Ext JS, LLC.
10651  *
10652  * Originally Released Under LGPL - original licence link has changed is not relivant.
10653  *
10654  * Fork - LGPL
10655  * <script type="text/javascript">
10656  */
10657  
10658 /**
10659  * @class Roo.MessageBox
10660  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10661  * Example usage:
10662  *<pre><code>
10663 // Basic alert:
10664 Roo.Msg.alert('Status', 'Changes saved successfully.');
10665
10666 // Prompt for user data:
10667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10668     if (btn == 'ok'){
10669         // process text value...
10670     }
10671 });
10672
10673 // Show a dialog using config options:
10674 Roo.Msg.show({
10675    title:'Save Changes?',
10676    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10677    buttons: Roo.Msg.YESNOCANCEL,
10678    fn: processResult,
10679    animEl: 'elId'
10680 });
10681 </code></pre>
10682  * @singleton
10683  */
10684 Roo.MessageBox = function(){
10685     var dlg, opt, mask, waitTimer;
10686     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10687     var buttons, activeTextEl, bwidth;
10688
10689     // private
10690     var handleButton = function(button){
10691         dlg.hide();
10692         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10693     };
10694
10695     // private
10696     var handleHide = function(){
10697         if(opt && opt.cls){
10698             dlg.el.removeClass(opt.cls);
10699         }
10700         if(waitTimer){
10701             Roo.TaskMgr.stop(waitTimer);
10702             waitTimer = null;
10703         }
10704     };
10705
10706     // private
10707     var updateButtons = function(b){
10708         var width = 0;
10709         if(!b){
10710             buttons["ok"].hide();
10711             buttons["cancel"].hide();
10712             buttons["yes"].hide();
10713             buttons["no"].hide();
10714             dlg.footer.dom.style.display = 'none';
10715             return width;
10716         }
10717         dlg.footer.dom.style.display = '';
10718         for(var k in buttons){
10719             if(typeof buttons[k] != "function"){
10720                 if(b[k]){
10721                     buttons[k].show();
10722                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10723                     width += buttons[k].el.getWidth()+15;
10724                 }else{
10725                     buttons[k].hide();
10726                 }
10727             }
10728         }
10729         return width;
10730     };
10731
10732     // private
10733     var handleEsc = function(d, k, e){
10734         if(opt && opt.closable !== false){
10735             dlg.hide();
10736         }
10737         if(e){
10738             e.stopEvent();
10739         }
10740     };
10741
10742     return {
10743         /**
10744          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10745          * @return {Roo.BasicDialog} The BasicDialog element
10746          */
10747         getDialog : function(){
10748            if(!dlg){
10749                 dlg = new Roo.BasicDialog("x-msg-box", {
10750                     autoCreate : true,
10751                     shadow: true,
10752                     draggable: true,
10753                     resizable:false,
10754                     constraintoviewport:false,
10755                     fixedcenter:true,
10756                     collapsible : false,
10757                     shim:true,
10758                     modal: true,
10759                     width:400, height:100,
10760                     buttonAlign:"center",
10761                     closeClick : function(){
10762                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10763                             handleButton("no");
10764                         }else{
10765                             handleButton("cancel");
10766                         }
10767                     }
10768                 });
10769                 dlg.on("hide", handleHide);
10770                 mask = dlg.mask;
10771                 dlg.addKeyListener(27, handleEsc);
10772                 buttons = {};
10773                 var bt = this.buttonText;
10774                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10775                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10776                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10777                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10778                 bodyEl = dlg.body.createChild({
10779
10780                     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>'
10781                 });
10782                 msgEl = bodyEl.dom.firstChild;
10783                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10784                 textboxEl.enableDisplayMode();
10785                 textboxEl.addKeyListener([10,13], function(){
10786                     if(dlg.isVisible() && opt && opt.buttons){
10787                         if(opt.buttons.ok){
10788                             handleButton("ok");
10789                         }else if(opt.buttons.yes){
10790                             handleButton("yes");
10791                         }
10792                     }
10793                 });
10794                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10795                 textareaEl.enableDisplayMode();
10796                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10797                 progressEl.enableDisplayMode();
10798                 var pf = progressEl.dom.firstChild;
10799                 if (pf) {
10800                     pp = Roo.get(pf.firstChild);
10801                     pp.setHeight(pf.offsetHeight);
10802                 }
10803                 
10804             }
10805             return dlg;
10806         },
10807
10808         /**
10809          * Updates the message box body text
10810          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10811          * the XHTML-compliant non-breaking space character '&amp;#160;')
10812          * @return {Roo.MessageBox} This message box
10813          */
10814         updateText : function(text){
10815             if(!dlg.isVisible() && !opt.width){
10816                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10817             }
10818             msgEl.innerHTML = text || '&#160;';
10819       
10820             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10821             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10822             var w = Math.max(
10823                     Math.min(opt.width || cw , this.maxWidth), 
10824                     Math.max(opt.minWidth || this.minWidth, bwidth)
10825             );
10826             if(opt.prompt){
10827                 activeTextEl.setWidth(w);
10828             }
10829             if(dlg.isVisible()){
10830                 dlg.fixedcenter = false;
10831             }
10832             // to big, make it scroll. = But as usual stupid IE does not support
10833             // !important..
10834             
10835             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10836                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10837                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10838             } else {
10839                 bodyEl.dom.style.height = '';
10840                 bodyEl.dom.style.overflowY = '';
10841             }
10842             if (cw > w) {
10843                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10844             } else {
10845                 bodyEl.dom.style.overflowX = '';
10846             }
10847             
10848             dlg.setContentSize(w, bodyEl.getHeight());
10849             if(dlg.isVisible()){
10850                 dlg.fixedcenter = true;
10851             }
10852             return this;
10853         },
10854
10855         /**
10856          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10857          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10858          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10859          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10860          * @return {Roo.MessageBox} This message box
10861          */
10862         updateProgress : function(value, text){
10863             if(text){
10864                 this.updateText(text);
10865             }
10866             if (pp) { // weird bug on my firefox - for some reason this is not defined
10867                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10868             }
10869             return this;
10870         },        
10871
10872         /**
10873          * Returns true if the message box is currently displayed
10874          * @return {Boolean} True if the message box is visible, else false
10875          */
10876         isVisible : function(){
10877             return dlg && dlg.isVisible();  
10878         },
10879
10880         /**
10881          * Hides the message box if it is displayed
10882          */
10883         hide : function(){
10884             if(this.isVisible()){
10885                 dlg.hide();
10886             }  
10887         },
10888
10889         /**
10890          * Displays a new message box, or reinitializes an existing message box, based on the config options
10891          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10892          * The following config object properties are supported:
10893          * <pre>
10894 Property    Type             Description
10895 ----------  ---------------  ------------------------------------------------------------------------------------
10896 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10897                                    closes (defaults to undefined)
10898 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10899                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10900 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10901                                    progress and wait dialogs will ignore this property and always hide the
10902                                    close button as they can only be closed programmatically.
10903 cls               String           A custom CSS class to apply to the message box element
10904 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10905                                    displayed (defaults to 75)
10906 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10907                                    function will be btn (the name of the button that was clicked, if applicable,
10908                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10909                                    Progress and wait dialogs will ignore this option since they do not respond to
10910                                    user actions and can only be closed programmatically, so any required function
10911                                    should be called by the same code after it closes the dialog.
10912 icon              String           A CSS class that provides a background image to be used as an icon for
10913                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10914 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10915 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10916 modal             Boolean          False to allow user interaction with the page while the message box is
10917                                    displayed (defaults to true)
10918 msg               String           A string that will replace the existing message box body text (defaults
10919                                    to the XHTML-compliant non-breaking space character '&#160;')
10920 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10921 progress          Boolean          True to display a progress bar (defaults to false)
10922 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10923 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10924 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10925 title             String           The title text
10926 value             String           The string value to set into the active textbox element if displayed
10927 wait              Boolean          True to display a progress bar (defaults to false)
10928 width             Number           The width of the dialog in pixels
10929 </pre>
10930          *
10931          * Example usage:
10932          * <pre><code>
10933 Roo.Msg.show({
10934    title: 'Address',
10935    msg: 'Please enter your address:',
10936    width: 300,
10937    buttons: Roo.MessageBox.OKCANCEL,
10938    multiline: true,
10939    fn: saveAddress,
10940    animEl: 'addAddressBtn'
10941 });
10942 </code></pre>
10943          * @param {Object} config Configuration options
10944          * @return {Roo.MessageBox} This message box
10945          */
10946         show : function(options)
10947         {
10948             
10949             // this causes nightmares if you show one dialog after another
10950             // especially on callbacks..
10951              
10952             if(this.isVisible()){
10953                 
10954                 this.hide();
10955                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10956                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10957                 Roo.log("New Dialog Message:" +  options.msg )
10958                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10959                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10960                 
10961             }
10962             var d = this.getDialog();
10963             opt = options;
10964             d.setTitle(opt.title || "&#160;");
10965             d.close.setDisplayed(opt.closable !== false);
10966             activeTextEl = textboxEl;
10967             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10968             if(opt.prompt){
10969                 if(opt.multiline){
10970                     textboxEl.hide();
10971                     textareaEl.show();
10972                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10973                         opt.multiline : this.defaultTextHeight);
10974                     activeTextEl = textareaEl;
10975                 }else{
10976                     textboxEl.show();
10977                     textareaEl.hide();
10978                 }
10979             }else{
10980                 textboxEl.hide();
10981                 textareaEl.hide();
10982             }
10983             progressEl.setDisplayed(opt.progress === true);
10984             this.updateProgress(0);
10985             activeTextEl.dom.value = opt.value || "";
10986             if(opt.prompt){
10987                 dlg.setDefaultButton(activeTextEl);
10988             }else{
10989                 var bs = opt.buttons;
10990                 var db = null;
10991                 if(bs && bs.ok){
10992                     db = buttons["ok"];
10993                 }else if(bs && bs.yes){
10994                     db = buttons["yes"];
10995                 }
10996                 dlg.setDefaultButton(db);
10997             }
10998             bwidth = updateButtons(opt.buttons);
10999             this.updateText(opt.msg);
11000             if(opt.cls){
11001                 d.el.addClass(opt.cls);
11002             }
11003             d.proxyDrag = opt.proxyDrag === true;
11004             d.modal = opt.modal !== false;
11005             d.mask = opt.modal !== false ? mask : false;
11006             if(!d.isVisible()){
11007                 // force it to the end of the z-index stack so it gets a cursor in FF
11008                 document.body.appendChild(dlg.el.dom);
11009                 d.animateTarget = null;
11010                 d.show(options.animEl);
11011             }
11012             return this;
11013         },
11014
11015         /**
11016          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11017          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11018          * and closing the message box when the process is complete.
11019          * @param {String} title The title bar text
11020          * @param {String} msg The message box body text
11021          * @return {Roo.MessageBox} This message box
11022          */
11023         progress : function(title, msg){
11024             this.show({
11025                 title : title,
11026                 msg : msg,
11027                 buttons: false,
11028                 progress:true,
11029                 closable:false,
11030                 minWidth: this.minProgressWidth,
11031                 modal : true
11032             });
11033             return this;
11034         },
11035
11036         /**
11037          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11038          * If a callback function is passed it will be called after the user clicks the button, and the
11039          * id of the button that was clicked will be passed as the only parameter to the callback
11040          * (could also be the top-right close button).
11041          * @param {String} title The title bar text
11042          * @param {String} msg The message box body text
11043          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11044          * @param {Object} scope (optional) The scope of the callback function
11045          * @return {Roo.MessageBox} This message box
11046          */
11047         alert : function(title, msg, fn, scope){
11048             this.show({
11049                 title : title,
11050                 msg : msg,
11051                 buttons: this.OK,
11052                 fn: fn,
11053                 scope : scope,
11054                 modal : true
11055             });
11056             return this;
11057         },
11058
11059         /**
11060          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11061          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11062          * You are responsible for closing the message box when the process is complete.
11063          * @param {String} msg The message box body text
11064          * @param {String} title (optional) The title bar text
11065          * @return {Roo.MessageBox} This message box
11066          */
11067         wait : function(msg, title){
11068             this.show({
11069                 title : title,
11070                 msg : msg,
11071                 buttons: false,
11072                 closable:false,
11073                 progress:true,
11074                 modal:true,
11075                 width:300,
11076                 wait:true
11077             });
11078             waitTimer = Roo.TaskMgr.start({
11079                 run: function(i){
11080                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11081                 },
11082                 interval: 1000
11083             });
11084             return this;
11085         },
11086
11087         /**
11088          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11089          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11090          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11091          * @param {String} title The title bar text
11092          * @param {String} msg The message box body text
11093          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11094          * @param {Object} scope (optional) The scope of the callback function
11095          * @return {Roo.MessageBox} This message box
11096          */
11097         confirm : function(title, msg, fn, scope){
11098             this.show({
11099                 title : title,
11100                 msg : msg,
11101                 buttons: this.YESNO,
11102                 fn: fn,
11103                 scope : scope,
11104                 modal : true
11105             });
11106             return this;
11107         },
11108
11109         /**
11110          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11111          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11112          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11113          * (could also be the top-right close button) and the text that was entered will be passed as the two
11114          * parameters to the callback.
11115          * @param {String} title The title bar text
11116          * @param {String} msg The message box body text
11117          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11118          * @param {Object} scope (optional) The scope of the callback function
11119          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11120          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11121          * @return {Roo.MessageBox} This message box
11122          */
11123         prompt : function(title, msg, fn, scope, multiline){
11124             this.show({
11125                 title : title,
11126                 msg : msg,
11127                 buttons: this.OKCANCEL,
11128                 fn: fn,
11129                 minWidth:250,
11130                 scope : scope,
11131                 prompt:true,
11132                 multiline: multiline,
11133                 modal : true
11134             });
11135             return this;
11136         },
11137
11138         /**
11139          * Button config that displays a single OK button
11140          * @type Object
11141          */
11142         OK : {ok:true},
11143         /**
11144          * Button config that displays Yes and No buttons
11145          * @type Object
11146          */
11147         YESNO : {yes:true, no:true},
11148         /**
11149          * Button config that displays OK and Cancel buttons
11150          * @type Object
11151          */
11152         OKCANCEL : {ok:true, cancel:true},
11153         /**
11154          * Button config that displays Yes, No and Cancel buttons
11155          * @type Object
11156          */
11157         YESNOCANCEL : {yes:true, no:true, cancel:true},
11158
11159         /**
11160          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11161          * @type Number
11162          */
11163         defaultTextHeight : 75,
11164         /**
11165          * The maximum width in pixels of the message box (defaults to 600)
11166          * @type Number
11167          */
11168         maxWidth : 600,
11169         /**
11170          * The minimum width in pixels of the message box (defaults to 100)
11171          * @type Number
11172          */
11173         minWidth : 100,
11174         /**
11175          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11176          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11177          * @type Number
11178          */
11179         minProgressWidth : 250,
11180         /**
11181          * An object containing the default button text strings that can be overriden for localized language support.
11182          * Supported properties are: ok, cancel, yes and no.
11183          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11184          * @type Object
11185          */
11186         buttonText : {
11187             ok : "OK",
11188             cancel : "Cancel",
11189             yes : "Yes",
11190             no : "No"
11191         }
11192     };
11193 }();
11194
11195 /**
11196  * Shorthand for {@link Roo.MessageBox}
11197  */
11198 Roo.Msg = Roo.MessageBox;/*
11199  * Based on:
11200  * Ext JS Library 1.1.1
11201  * Copyright(c) 2006-2007, Ext JS, LLC.
11202  *
11203  * Originally Released Under LGPL - original licence link has changed is not relivant.
11204  *
11205  * Fork - LGPL
11206  * <script type="text/javascript">
11207  */
11208 /**
11209  * @class Roo.QuickTips
11210  * Provides attractive and customizable tooltips for any element.
11211  * @singleton
11212  */
11213 Roo.QuickTips = function(){
11214     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11215     var ce, bd, xy, dd;
11216     var visible = false, disabled = true, inited = false;
11217     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11218     
11219     var onOver = function(e){
11220         if(disabled){
11221             return;
11222         }
11223         var t = e.getTarget();
11224         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11225             return;
11226         }
11227         if(ce && t == ce.el){
11228             clearTimeout(hideProc);
11229             return;
11230         }
11231         if(t && tagEls[t.id]){
11232             tagEls[t.id].el = t;
11233             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11234             return;
11235         }
11236         var ttp, et = Roo.fly(t);
11237         var ns = cfg.namespace;
11238         if(tm.interceptTitles && t.title){
11239             ttp = t.title;
11240             t.qtip = ttp;
11241             t.removeAttribute("title");
11242             e.preventDefault();
11243         }else{
11244             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11245         }
11246         if(ttp){
11247             showProc = show.defer(tm.showDelay, tm, [{
11248                 el: t, 
11249                 text: ttp.replace(/\\n/g,'<br/>'),
11250                 width: et.getAttributeNS(ns, cfg.width),
11251                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11252                 title: et.getAttributeNS(ns, cfg.title),
11253                     cls: et.getAttributeNS(ns, cfg.cls)
11254             }]);
11255         }
11256     };
11257     
11258     var onOut = function(e){
11259         clearTimeout(showProc);
11260         var t = e.getTarget();
11261         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11262             hideProc = setTimeout(hide, tm.hideDelay);
11263         }
11264     };
11265     
11266     var onMove = function(e){
11267         if(disabled){
11268             return;
11269         }
11270         xy = e.getXY();
11271         xy[1] += 18;
11272         if(tm.trackMouse && ce){
11273             el.setXY(xy);
11274         }
11275     };
11276     
11277     var onDown = function(e){
11278         clearTimeout(showProc);
11279         clearTimeout(hideProc);
11280         if(!e.within(el)){
11281             if(tm.hideOnClick){
11282                 hide();
11283                 tm.disable();
11284                 tm.enable.defer(100, tm);
11285             }
11286         }
11287     };
11288     
11289     var getPad = function(){
11290         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11291     };
11292
11293     var show = function(o){
11294         if(disabled){
11295             return;
11296         }
11297         clearTimeout(dismissProc);
11298         ce = o;
11299         if(removeCls){ // in case manually hidden
11300             el.removeClass(removeCls);
11301             removeCls = null;
11302         }
11303         if(ce.cls){
11304             el.addClass(ce.cls);
11305             removeCls = ce.cls;
11306         }
11307         if(ce.title){
11308             tipTitle.update(ce.title);
11309             tipTitle.show();
11310         }else{
11311             tipTitle.update('');
11312             tipTitle.hide();
11313         }
11314         el.dom.style.width  = tm.maxWidth+'px';
11315         //tipBody.dom.style.width = '';
11316         tipBodyText.update(o.text);
11317         var p = getPad(), w = ce.width;
11318         if(!w){
11319             var td = tipBodyText.dom;
11320             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11321             if(aw > tm.maxWidth){
11322                 w = tm.maxWidth;
11323             }else if(aw < tm.minWidth){
11324                 w = tm.minWidth;
11325             }else{
11326                 w = aw;
11327             }
11328         }
11329         //tipBody.setWidth(w);
11330         el.setWidth(parseInt(w, 10) + p);
11331         if(ce.autoHide === false){
11332             close.setDisplayed(true);
11333             if(dd){
11334                 dd.unlock();
11335             }
11336         }else{
11337             close.setDisplayed(false);
11338             if(dd){
11339                 dd.lock();
11340             }
11341         }
11342         if(xy){
11343             el.avoidY = xy[1]-18;
11344             el.setXY(xy);
11345         }
11346         if(tm.animate){
11347             el.setOpacity(.1);
11348             el.setStyle("visibility", "visible");
11349             el.fadeIn({callback: afterShow});
11350         }else{
11351             afterShow();
11352         }
11353     };
11354     
11355     var afterShow = function(){
11356         if(ce){
11357             el.show();
11358             esc.enable();
11359             if(tm.autoDismiss && ce.autoHide !== false){
11360                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11361             }
11362         }
11363     };
11364     
11365     var hide = function(noanim){
11366         clearTimeout(dismissProc);
11367         clearTimeout(hideProc);
11368         ce = null;
11369         if(el.isVisible()){
11370             esc.disable();
11371             if(noanim !== true && tm.animate){
11372                 el.fadeOut({callback: afterHide});
11373             }else{
11374                 afterHide();
11375             } 
11376         }
11377     };
11378     
11379     var afterHide = function(){
11380         el.hide();
11381         if(removeCls){
11382             el.removeClass(removeCls);
11383             removeCls = null;
11384         }
11385     };
11386     
11387     return {
11388         /**
11389         * @cfg {Number} minWidth
11390         * The minimum width of the quick tip (defaults to 40)
11391         */
11392        minWidth : 40,
11393         /**
11394         * @cfg {Number} maxWidth
11395         * The maximum width of the quick tip (defaults to 300)
11396         */
11397        maxWidth : 300,
11398         /**
11399         * @cfg {Boolean} interceptTitles
11400         * True to automatically use the element's DOM title value if available (defaults to false)
11401         */
11402        interceptTitles : false,
11403         /**
11404         * @cfg {Boolean} trackMouse
11405         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11406         */
11407        trackMouse : false,
11408         /**
11409         * @cfg {Boolean} hideOnClick
11410         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11411         */
11412        hideOnClick : true,
11413         /**
11414         * @cfg {Number} showDelay
11415         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11416         */
11417        showDelay : 500,
11418         /**
11419         * @cfg {Number} hideDelay
11420         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11421         */
11422        hideDelay : 200,
11423         /**
11424         * @cfg {Boolean} autoHide
11425         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11426         * Used in conjunction with hideDelay.
11427         */
11428        autoHide : true,
11429         /**
11430         * @cfg {Boolean}
11431         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11432         * (defaults to true).  Used in conjunction with autoDismissDelay.
11433         */
11434        autoDismiss : true,
11435         /**
11436         * @cfg {Number}
11437         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11438         */
11439        autoDismissDelay : 5000,
11440        /**
11441         * @cfg {Boolean} animate
11442         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11443         */
11444        animate : false,
11445
11446        /**
11447         * @cfg {String} title
11448         * Title text to display (defaults to '').  This can be any valid HTML markup.
11449         */
11450         title: '',
11451        /**
11452         * @cfg {String} text
11453         * Body text to display (defaults to '').  This can be any valid HTML markup.
11454         */
11455         text : '',
11456        /**
11457         * @cfg {String} cls
11458         * A CSS class to apply to the base quick tip element (defaults to '').
11459         */
11460         cls : '',
11461        /**
11462         * @cfg {Number} width
11463         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11464         * minWidth or maxWidth.
11465         */
11466         width : null,
11467
11468     /**
11469      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11470      * or display QuickTips in a page.
11471      */
11472        init : function(){
11473           tm = Roo.QuickTips;
11474           cfg = tm.tagConfig;
11475           if(!inited){
11476               if(!Roo.isReady){ // allow calling of init() before onReady
11477                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11478                   return;
11479               }
11480               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11481               el.fxDefaults = {stopFx: true};
11482               // maximum custom styling
11483               //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>');
11484               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>');              
11485               tipTitle = el.child('h3');
11486               tipTitle.enableDisplayMode("block");
11487               tipBody = el.child('div.x-tip-bd');
11488               tipBodyText = el.child('div.x-tip-bd-inner');
11489               //bdLeft = el.child('div.x-tip-bd-left');
11490               //bdRight = el.child('div.x-tip-bd-right');
11491               close = el.child('div.x-tip-close');
11492               close.enableDisplayMode("block");
11493               close.on("click", hide);
11494               var d = Roo.get(document);
11495               d.on("mousedown", onDown);
11496               d.on("mouseover", onOver);
11497               d.on("mouseout", onOut);
11498               d.on("mousemove", onMove);
11499               esc = d.addKeyListener(27, hide);
11500               esc.disable();
11501               if(Roo.dd.DD){
11502                   dd = el.initDD("default", null, {
11503                       onDrag : function(){
11504                           el.sync();  
11505                       }
11506                   });
11507                   dd.setHandleElId(tipTitle.id);
11508                   dd.lock();
11509               }
11510               inited = true;
11511           }
11512           this.enable(); 
11513        },
11514
11515     /**
11516      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11517      * are supported:
11518      * <pre>
11519 Property    Type                   Description
11520 ----------  ---------------------  ------------------------------------------------------------------------
11521 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11522      * </ul>
11523      * @param {Object} config The config object
11524      */
11525        register : function(config){
11526            var cs = config instanceof Array ? config : arguments;
11527            for(var i = 0, len = cs.length; i < len; i++) {
11528                var c = cs[i];
11529                var target = c.target;
11530                if(target){
11531                    if(target instanceof Array){
11532                        for(var j = 0, jlen = target.length; j < jlen; j++){
11533                            tagEls[target[j]] = c;
11534                        }
11535                    }else{
11536                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11537                    }
11538                }
11539            }
11540        },
11541
11542     /**
11543      * Removes this quick tip from its element and destroys it.
11544      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11545      */
11546        unregister : function(el){
11547            delete tagEls[Roo.id(el)];
11548        },
11549
11550     /**
11551      * Enable this quick tip.
11552      */
11553        enable : function(){
11554            if(inited && disabled){
11555                locks.pop();
11556                if(locks.length < 1){
11557                    disabled = false;
11558                }
11559            }
11560        },
11561
11562     /**
11563      * Disable this quick tip.
11564      */
11565        disable : function(){
11566           disabled = true;
11567           clearTimeout(showProc);
11568           clearTimeout(hideProc);
11569           clearTimeout(dismissProc);
11570           if(ce){
11571               hide(true);
11572           }
11573           locks.push(1);
11574        },
11575
11576     /**
11577      * Returns true if the quick tip is enabled, else false.
11578      */
11579        isEnabled : function(){
11580             return !disabled;
11581        },
11582
11583         // private
11584        tagConfig : {
11585            namespace : "roo", // was ext?? this may break..
11586            alt_namespace : "ext",
11587            attribute : "qtip",
11588            width : "width",
11589            target : "target",
11590            title : "qtitle",
11591            hide : "hide",
11592            cls : "qclass"
11593        }
11594    };
11595 }();
11596
11597 // backwards compat
11598 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11599  * Based on:
11600  * Ext JS Library 1.1.1
11601  * Copyright(c) 2006-2007, Ext JS, LLC.
11602  *
11603  * Originally Released Under LGPL - original licence link has changed is not relivant.
11604  *
11605  * Fork - LGPL
11606  * <script type="text/javascript">
11607  */
11608  
11609
11610 /**
11611  * @class Roo.tree.TreePanel
11612  * @extends Roo.data.Tree
11613
11614  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11615  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11616  * @cfg {Boolean} enableDD true to enable drag and drop
11617  * @cfg {Boolean} enableDrag true to enable just drag
11618  * @cfg {Boolean} enableDrop true to enable just drop
11619  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11620  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11621  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11622  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11623  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11624  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11625  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11626  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11627  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11628  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11629  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11630  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11631  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11632  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11633  * @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>
11634  * @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>
11635  * 
11636  * @constructor
11637  * @param {String/HTMLElement/Element} el The container element
11638  * @param {Object} config
11639  */
11640 Roo.tree.TreePanel = function(el, config){
11641     var root = false;
11642     var loader = false;
11643     if (config.root) {
11644         root = config.root;
11645         delete config.root;
11646     }
11647     if (config.loader) {
11648         loader = config.loader;
11649         delete config.loader;
11650     }
11651     
11652     Roo.apply(this, config);
11653     Roo.tree.TreePanel.superclass.constructor.call(this);
11654     this.el = Roo.get(el);
11655     this.el.addClass('x-tree');
11656     //console.log(root);
11657     if (root) {
11658         this.setRootNode( Roo.factory(root, Roo.tree));
11659     }
11660     if (loader) {
11661         this.loader = Roo.factory(loader, Roo.tree);
11662     }
11663    /**
11664     * Read-only. The id of the container element becomes this TreePanel's id.
11665     */
11666     this.id = this.el.id;
11667     this.addEvents({
11668         /**
11669         * @event beforeload
11670         * Fires before a node is loaded, return false to cancel
11671         * @param {Node} node The node being loaded
11672         */
11673         "beforeload" : true,
11674         /**
11675         * @event load
11676         * Fires when a node is loaded
11677         * @param {Node} node The node that was loaded
11678         */
11679         "load" : true,
11680         /**
11681         * @event textchange
11682         * Fires when the text for a node is changed
11683         * @param {Node} node The node
11684         * @param {String} text The new text
11685         * @param {String} oldText The old text
11686         */
11687         "textchange" : true,
11688         /**
11689         * @event beforeexpand
11690         * Fires before a node is expanded, return false to cancel.
11691         * @param {Node} node The node
11692         * @param {Boolean} deep
11693         * @param {Boolean} anim
11694         */
11695         "beforeexpand" : true,
11696         /**
11697         * @event beforecollapse
11698         * Fires before a node is collapsed, return false to cancel.
11699         * @param {Node} node The node
11700         * @param {Boolean} deep
11701         * @param {Boolean} anim
11702         */
11703         "beforecollapse" : true,
11704         /**
11705         * @event expand
11706         * Fires when a node is expanded
11707         * @param {Node} node The node
11708         */
11709         "expand" : true,
11710         /**
11711         * @event disabledchange
11712         * Fires when the disabled status of a node changes
11713         * @param {Node} node The node
11714         * @param {Boolean} disabled
11715         */
11716         "disabledchange" : true,
11717         /**
11718         * @event collapse
11719         * Fires when a node is collapsed
11720         * @param {Node} node The node
11721         */
11722         "collapse" : true,
11723         /**
11724         * @event beforeclick
11725         * Fires before click processing on a node. Return false to cancel the default action.
11726         * @param {Node} node The node
11727         * @param {Roo.EventObject} e The event object
11728         */
11729         "beforeclick":true,
11730         /**
11731         * @event checkchange
11732         * Fires when a node with a checkbox's checked property changes
11733         * @param {Node} this This node
11734         * @param {Boolean} checked
11735         */
11736         "checkchange":true,
11737         /**
11738         * @event click
11739         * Fires when a node is clicked
11740         * @param {Node} node The node
11741         * @param {Roo.EventObject} e The event object
11742         */
11743         "click":true,
11744         /**
11745         * @event dblclick
11746         * Fires when a node is double clicked
11747         * @param {Node} node The node
11748         * @param {Roo.EventObject} e The event object
11749         */
11750         "dblclick":true,
11751         /**
11752         * @event contextmenu
11753         * Fires when a node is right clicked
11754         * @param {Node} node The node
11755         * @param {Roo.EventObject} e The event object
11756         */
11757         "contextmenu":true,
11758         /**
11759         * @event beforechildrenrendered
11760         * Fires right before the child nodes for a node are rendered
11761         * @param {Node} node The node
11762         */
11763         "beforechildrenrendered":true,
11764         /**
11765         * @event startdrag
11766         * Fires when a node starts being dragged
11767         * @param {Roo.tree.TreePanel} this
11768         * @param {Roo.tree.TreeNode} node
11769         * @param {event} e The raw browser event
11770         */ 
11771        "startdrag" : true,
11772        /**
11773         * @event enddrag
11774         * Fires when a drag operation is complete
11775         * @param {Roo.tree.TreePanel} this
11776         * @param {Roo.tree.TreeNode} node
11777         * @param {event} e The raw browser event
11778         */
11779        "enddrag" : true,
11780        /**
11781         * @event dragdrop
11782         * Fires when a dragged node is dropped on a valid DD target
11783         * @param {Roo.tree.TreePanel} this
11784         * @param {Roo.tree.TreeNode} node
11785         * @param {DD} dd The dd it was dropped on
11786         * @param {event} e The raw browser event
11787         */
11788        "dragdrop" : true,
11789        /**
11790         * @event beforenodedrop
11791         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11792         * passed to handlers has the following properties:<br />
11793         * <ul style="padding:5px;padding-left:16px;">
11794         * <li>tree - The TreePanel</li>
11795         * <li>target - The node being targeted for the drop</li>
11796         * <li>data - The drag data from the drag source</li>
11797         * <li>point - The point of the drop - append, above or below</li>
11798         * <li>source - The drag source</li>
11799         * <li>rawEvent - Raw mouse event</li>
11800         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11801         * to be inserted by setting them on this object.</li>
11802         * <li>cancel - Set this to true to cancel the drop.</li>
11803         * </ul>
11804         * @param {Object} dropEvent
11805         */
11806        "beforenodedrop" : true,
11807        /**
11808         * @event nodedrop
11809         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11810         * passed to handlers has the following properties:<br />
11811         * <ul style="padding:5px;padding-left:16px;">
11812         * <li>tree - The TreePanel</li>
11813         * <li>target - The node being targeted for the drop</li>
11814         * <li>data - The drag data from the drag source</li>
11815         * <li>point - The point of the drop - append, above or below</li>
11816         * <li>source - The drag source</li>
11817         * <li>rawEvent - Raw mouse event</li>
11818         * <li>dropNode - Dropped node(s).</li>
11819         * </ul>
11820         * @param {Object} dropEvent
11821         */
11822        "nodedrop" : true,
11823         /**
11824         * @event nodedragover
11825         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11826         * passed to handlers has the following properties:<br />
11827         * <ul style="padding:5px;padding-left:16px;">
11828         * <li>tree - The TreePanel</li>
11829         * <li>target - The node being targeted for the drop</li>
11830         * <li>data - The drag data from the drag source</li>
11831         * <li>point - The point of the drop - append, above or below</li>
11832         * <li>source - The drag source</li>
11833         * <li>rawEvent - Raw mouse event</li>
11834         * <li>dropNode - Drop node(s) provided by the source.</li>
11835         * <li>cancel - Set this to true to signal drop not allowed.</li>
11836         * </ul>
11837         * @param {Object} dragOverEvent
11838         */
11839        "nodedragover" : true,
11840        /**
11841         * @event appendnode
11842         * Fires when append node to the tree
11843         * @param {Roo.tree.TreePanel} this
11844         * @param {Roo.tree.TreeNode} node
11845         * @param {Number} index The index of the newly appended node
11846         */
11847        "appendnode" : true
11848         
11849     });
11850     if(this.singleExpand){
11851        this.on("beforeexpand", this.restrictExpand, this);
11852     }
11853     if (this.editor) {
11854         this.editor.tree = this;
11855         this.editor = Roo.factory(this.editor, Roo.tree);
11856     }
11857     
11858     if (this.selModel) {
11859         this.selModel = Roo.factory(this.selModel, Roo.tree);
11860     }
11861    
11862 };
11863 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11864     rootVisible : true,
11865     animate: Roo.enableFx,
11866     lines : true,
11867     enableDD : false,
11868     hlDrop : Roo.enableFx,
11869   
11870     renderer: false,
11871     
11872     rendererTip: false,
11873     // private
11874     restrictExpand : function(node){
11875         var p = node.parentNode;
11876         if(p){
11877             if(p.expandedChild && p.expandedChild.parentNode == p){
11878                 p.expandedChild.collapse();
11879             }
11880             p.expandedChild = node;
11881         }
11882     },
11883
11884     // private override
11885     setRootNode : function(node){
11886         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11887         if(!this.rootVisible){
11888             node.ui = new Roo.tree.RootTreeNodeUI(node);
11889         }
11890         return node;
11891     },
11892
11893     /**
11894      * Returns the container element for this TreePanel
11895      */
11896     getEl : function(){
11897         return this.el;
11898     },
11899
11900     /**
11901      * Returns the default TreeLoader for this TreePanel
11902      */
11903     getLoader : function(){
11904         return this.loader;
11905     },
11906
11907     /**
11908      * Expand all nodes
11909      */
11910     expandAll : function(){
11911         this.root.expand(true);
11912     },
11913
11914     /**
11915      * Collapse all nodes
11916      */
11917     collapseAll : function(){
11918         this.root.collapse(true);
11919     },
11920
11921     /**
11922      * Returns the selection model used by this TreePanel
11923      */
11924     getSelectionModel : function(){
11925         if(!this.selModel){
11926             this.selModel = new Roo.tree.DefaultSelectionModel();
11927         }
11928         return this.selModel;
11929     },
11930
11931     /**
11932      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11933      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11934      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11935      * @return {Array}
11936      */
11937     getChecked : function(a, startNode){
11938         startNode = startNode || this.root;
11939         var r = [];
11940         var f = function(){
11941             if(this.attributes.checked){
11942                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11943             }
11944         }
11945         startNode.cascade(f);
11946         return r;
11947     },
11948
11949     /**
11950      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11951      * @param {String} path
11952      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11953      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11954      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11955      */
11956     expandPath : function(path, attr, callback){
11957         attr = attr || "id";
11958         var keys = path.split(this.pathSeparator);
11959         var curNode = this.root;
11960         if(curNode.attributes[attr] != keys[1]){ // invalid root
11961             if(callback){
11962                 callback(false, null);
11963             }
11964             return;
11965         }
11966         var index = 1;
11967         var f = function(){
11968             if(++index == keys.length){
11969                 if(callback){
11970                     callback(true, curNode);
11971                 }
11972                 return;
11973             }
11974             var c = curNode.findChild(attr, keys[index]);
11975             if(!c){
11976                 if(callback){
11977                     callback(false, curNode);
11978                 }
11979                 return;
11980             }
11981             curNode = c;
11982             c.expand(false, false, f);
11983         };
11984         curNode.expand(false, false, f);
11985     },
11986
11987     /**
11988      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11989      * @param {String} path
11990      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11991      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11992      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11993      */
11994     selectPath : function(path, attr, callback){
11995         attr = attr || "id";
11996         var keys = path.split(this.pathSeparator);
11997         var v = keys.pop();
11998         if(keys.length > 0){
11999             var f = function(success, node){
12000                 if(success && node){
12001                     var n = node.findChild(attr, v);
12002                     if(n){
12003                         n.select();
12004                         if(callback){
12005                             callback(true, n);
12006                         }
12007                     }else if(callback){
12008                         callback(false, n);
12009                     }
12010                 }else{
12011                     if(callback){
12012                         callback(false, n);
12013                     }
12014                 }
12015             };
12016             this.expandPath(keys.join(this.pathSeparator), attr, f);
12017         }else{
12018             this.root.select();
12019             if(callback){
12020                 callback(true, this.root);
12021             }
12022         }
12023     },
12024
12025     getTreeEl : function(){
12026         return this.el;
12027     },
12028
12029     /**
12030      * Trigger rendering of this TreePanel
12031      */
12032     render : function(){
12033         if (this.innerCt) {
12034             return this; // stop it rendering more than once!!
12035         }
12036         
12037         this.innerCt = this.el.createChild({tag:"ul",
12038                cls:"x-tree-root-ct " +
12039                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12040
12041         if(this.containerScroll){
12042             Roo.dd.ScrollManager.register(this.el);
12043         }
12044         if((this.enableDD || this.enableDrop) && !this.dropZone){
12045            /**
12046             * The dropZone used by this tree if drop is enabled
12047             * @type Roo.tree.TreeDropZone
12048             */
12049              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12050                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12051            });
12052         }
12053         if((this.enableDD || this.enableDrag) && !this.dragZone){
12054            /**
12055             * The dragZone used by this tree if drag is enabled
12056             * @type Roo.tree.TreeDragZone
12057             */
12058             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12059                ddGroup: this.ddGroup || "TreeDD",
12060                scroll: this.ddScroll
12061            });
12062         }
12063         this.getSelectionModel().init(this);
12064         if (!this.root) {
12065             Roo.log("ROOT not set in tree");
12066             return this;
12067         }
12068         this.root.render();
12069         if(!this.rootVisible){
12070             this.root.renderChildren();
12071         }
12072         return this;
12073     }
12074 });/*
12075  * Based on:
12076  * Ext JS Library 1.1.1
12077  * Copyright(c) 2006-2007, Ext JS, LLC.
12078  *
12079  * Originally Released Under LGPL - original licence link has changed is not relivant.
12080  *
12081  * Fork - LGPL
12082  * <script type="text/javascript">
12083  */
12084  
12085
12086 /**
12087  * @class Roo.tree.DefaultSelectionModel
12088  * @extends Roo.util.Observable
12089  * The default single selection for a TreePanel.
12090  * @param {Object} cfg Configuration
12091  */
12092 Roo.tree.DefaultSelectionModel = function(cfg){
12093    this.selNode = null;
12094    
12095    
12096    
12097    this.addEvents({
12098        /**
12099         * @event selectionchange
12100         * Fires when the selected node changes
12101         * @param {DefaultSelectionModel} this
12102         * @param {TreeNode} node the new selection
12103         */
12104        "selectionchange" : true,
12105
12106        /**
12107         * @event beforeselect
12108         * Fires before the selected node changes, return false to cancel the change
12109         * @param {DefaultSelectionModel} this
12110         * @param {TreeNode} node the new selection
12111         * @param {TreeNode} node the old selection
12112         */
12113        "beforeselect" : true
12114    });
12115    
12116     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12117 };
12118
12119 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12120     init : function(tree){
12121         this.tree = tree;
12122         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12123         tree.on("click", this.onNodeClick, this);
12124     },
12125     
12126     onNodeClick : function(node, e){
12127         if (e.ctrlKey && this.selNode == node)  {
12128             this.unselect(node);
12129             return;
12130         }
12131         this.select(node);
12132     },
12133     
12134     /**
12135      * Select a node.
12136      * @param {TreeNode} node The node to select
12137      * @return {TreeNode} The selected node
12138      */
12139     select : function(node){
12140         var last = this.selNode;
12141         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12142             if(last){
12143                 last.ui.onSelectedChange(false);
12144             }
12145             this.selNode = node;
12146             node.ui.onSelectedChange(true);
12147             this.fireEvent("selectionchange", this, node, last);
12148         }
12149         return node;
12150     },
12151     
12152     /**
12153      * Deselect a node.
12154      * @param {TreeNode} node The node to unselect
12155      */
12156     unselect : function(node){
12157         if(this.selNode == node){
12158             this.clearSelections();
12159         }    
12160     },
12161     
12162     /**
12163      * Clear all selections
12164      */
12165     clearSelections : function(){
12166         var n = this.selNode;
12167         if(n){
12168             n.ui.onSelectedChange(false);
12169             this.selNode = null;
12170             this.fireEvent("selectionchange", this, null);
12171         }
12172         return n;
12173     },
12174     
12175     /**
12176      * Get the selected node
12177      * @return {TreeNode} The selected node
12178      */
12179     getSelectedNode : function(){
12180         return this.selNode;    
12181     },
12182     
12183     /**
12184      * Returns true if the node is selected
12185      * @param {TreeNode} node The node to check
12186      * @return {Boolean}
12187      */
12188     isSelected : function(node){
12189         return this.selNode == node;  
12190     },
12191
12192     /**
12193      * Selects the node above the selected node in the tree, intelligently walking the nodes
12194      * @return TreeNode The new selection
12195      */
12196     selectPrevious : function(){
12197         var s = this.selNode || this.lastSelNode;
12198         if(!s){
12199             return null;
12200         }
12201         var ps = s.previousSibling;
12202         if(ps){
12203             if(!ps.isExpanded() || ps.childNodes.length < 1){
12204                 return this.select(ps);
12205             } else{
12206                 var lc = ps.lastChild;
12207                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12208                     lc = lc.lastChild;
12209                 }
12210                 return this.select(lc);
12211             }
12212         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12213             return this.select(s.parentNode);
12214         }
12215         return null;
12216     },
12217
12218     /**
12219      * Selects the node above the selected node in the tree, intelligently walking the nodes
12220      * @return TreeNode The new selection
12221      */
12222     selectNext : function(){
12223         var s = this.selNode || this.lastSelNode;
12224         if(!s){
12225             return null;
12226         }
12227         if(s.firstChild && s.isExpanded()){
12228              return this.select(s.firstChild);
12229          }else if(s.nextSibling){
12230              return this.select(s.nextSibling);
12231          }else if(s.parentNode){
12232             var newS = null;
12233             s.parentNode.bubble(function(){
12234                 if(this.nextSibling){
12235                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12236                     return false;
12237                 }
12238             });
12239             return newS;
12240          }
12241         return null;
12242     },
12243
12244     onKeyDown : function(e){
12245         var s = this.selNode || this.lastSelNode;
12246         // undesirable, but required
12247         var sm = this;
12248         if(!s){
12249             return;
12250         }
12251         var k = e.getKey();
12252         switch(k){
12253              case e.DOWN:
12254                  e.stopEvent();
12255                  this.selectNext();
12256              break;
12257              case e.UP:
12258                  e.stopEvent();
12259                  this.selectPrevious();
12260              break;
12261              case e.RIGHT:
12262                  e.preventDefault();
12263                  if(s.hasChildNodes()){
12264                      if(!s.isExpanded()){
12265                          s.expand();
12266                      }else if(s.firstChild){
12267                          this.select(s.firstChild, e);
12268                      }
12269                  }
12270              break;
12271              case e.LEFT:
12272                  e.preventDefault();
12273                  if(s.hasChildNodes() && s.isExpanded()){
12274                      s.collapse();
12275                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12276                      this.select(s.parentNode, e);
12277                  }
12278              break;
12279         };
12280     }
12281 });
12282
12283 /**
12284  * @class Roo.tree.MultiSelectionModel
12285  * @extends Roo.util.Observable
12286  * Multi selection for a TreePanel.
12287  * @param {Object} cfg Configuration
12288  */
12289 Roo.tree.MultiSelectionModel = function(){
12290    this.selNodes = [];
12291    this.selMap = {};
12292    this.addEvents({
12293        /**
12294         * @event selectionchange
12295         * Fires when the selected nodes change
12296         * @param {MultiSelectionModel} this
12297         * @param {Array} nodes Array of the selected nodes
12298         */
12299        "selectionchange" : true
12300    });
12301    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12302    
12303 };
12304
12305 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12306     init : function(tree){
12307         this.tree = tree;
12308         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12309         tree.on("click", this.onNodeClick, this);
12310     },
12311     
12312     onNodeClick : function(node, e){
12313         this.select(node, e, e.ctrlKey);
12314     },
12315     
12316     /**
12317      * Select a node.
12318      * @param {TreeNode} node The node to select
12319      * @param {EventObject} e (optional) An event associated with the selection
12320      * @param {Boolean} keepExisting True to retain existing selections
12321      * @return {TreeNode} The selected node
12322      */
12323     select : function(node, e, keepExisting){
12324         if(keepExisting !== true){
12325             this.clearSelections(true);
12326         }
12327         if(this.isSelected(node)){
12328             this.lastSelNode = node;
12329             return node;
12330         }
12331         this.selNodes.push(node);
12332         this.selMap[node.id] = node;
12333         this.lastSelNode = node;
12334         node.ui.onSelectedChange(true);
12335         this.fireEvent("selectionchange", this, this.selNodes);
12336         return node;
12337     },
12338     
12339     /**
12340      * Deselect a node.
12341      * @param {TreeNode} node The node to unselect
12342      */
12343     unselect : function(node){
12344         if(this.selMap[node.id]){
12345             node.ui.onSelectedChange(false);
12346             var sn = this.selNodes;
12347             var index = -1;
12348             if(sn.indexOf){
12349                 index = sn.indexOf(node);
12350             }else{
12351                 for(var i = 0, len = sn.length; i < len; i++){
12352                     if(sn[i] == node){
12353                         index = i;
12354                         break;
12355                     }
12356                 }
12357             }
12358             if(index != -1){
12359                 this.selNodes.splice(index, 1);
12360             }
12361             delete this.selMap[node.id];
12362             this.fireEvent("selectionchange", this, this.selNodes);
12363         }
12364     },
12365     
12366     /**
12367      * Clear all selections
12368      */
12369     clearSelections : function(suppressEvent){
12370         var sn = this.selNodes;
12371         if(sn.length > 0){
12372             for(var i = 0, len = sn.length; i < len; i++){
12373                 sn[i].ui.onSelectedChange(false);
12374             }
12375             this.selNodes = [];
12376             this.selMap = {};
12377             if(suppressEvent !== true){
12378                 this.fireEvent("selectionchange", this, this.selNodes);
12379             }
12380         }
12381     },
12382     
12383     /**
12384      * Returns true if the node is selected
12385      * @param {TreeNode} node The node to check
12386      * @return {Boolean}
12387      */
12388     isSelected : function(node){
12389         return this.selMap[node.id] ? true : false;  
12390     },
12391     
12392     /**
12393      * Returns an array of the selected nodes
12394      * @return {Array}
12395      */
12396     getSelectedNodes : function(){
12397         return this.selNodes;    
12398     },
12399
12400     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12401
12402     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12403
12404     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12405 });/*
12406  * Based on:
12407  * Ext JS Library 1.1.1
12408  * Copyright(c) 2006-2007, Ext JS, LLC.
12409  *
12410  * Originally Released Under LGPL - original licence link has changed is not relivant.
12411  *
12412  * Fork - LGPL
12413  * <script type="text/javascript">
12414  */
12415  
12416 /**
12417  * @class Roo.tree.TreeNode
12418  * @extends Roo.data.Node
12419  * @cfg {String} text The text for this node
12420  * @cfg {Boolean} expanded true to start the node expanded
12421  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12422  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12423  * @cfg {Boolean} disabled true to start the node disabled
12424  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12425  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12426  * @cfg {String} cls A css class to be added to the node
12427  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12428  * @cfg {String} href URL of the link used for the node (defaults to #)
12429  * @cfg {String} hrefTarget target frame for the link
12430  * @cfg {String} qtip An Ext QuickTip for the node
12431  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12432  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12433  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12434  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12435  * (defaults to undefined with no checkbox rendered)
12436  * @constructor
12437  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12438  */
12439 Roo.tree.TreeNode = function(attributes){
12440     attributes = attributes || {};
12441     if(typeof attributes == "string"){
12442         attributes = {text: attributes};
12443     }
12444     this.childrenRendered = false;
12445     this.rendered = false;
12446     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12447     this.expanded = attributes.expanded === true;
12448     this.isTarget = attributes.isTarget !== false;
12449     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12450     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12451
12452     /**
12453      * Read-only. The text for this node. To change it use setText().
12454      * @type String
12455      */
12456     this.text = attributes.text;
12457     /**
12458      * True if this node is disabled.
12459      * @type Boolean
12460      */
12461     this.disabled = attributes.disabled === true;
12462
12463     this.addEvents({
12464         /**
12465         * @event textchange
12466         * Fires when the text for this node is changed
12467         * @param {Node} this This node
12468         * @param {String} text The new text
12469         * @param {String} oldText The old text
12470         */
12471         "textchange" : true,
12472         /**
12473         * @event beforeexpand
12474         * Fires before this node is expanded, return false to cancel.
12475         * @param {Node} this This node
12476         * @param {Boolean} deep
12477         * @param {Boolean} anim
12478         */
12479         "beforeexpand" : true,
12480         /**
12481         * @event beforecollapse
12482         * Fires before this node is collapsed, return false to cancel.
12483         * @param {Node} this This node
12484         * @param {Boolean} deep
12485         * @param {Boolean} anim
12486         */
12487         "beforecollapse" : true,
12488         /**
12489         * @event expand
12490         * Fires when this node is expanded
12491         * @param {Node} this This node
12492         */
12493         "expand" : true,
12494         /**
12495         * @event disabledchange
12496         * Fires when the disabled status of this node changes
12497         * @param {Node} this This node
12498         * @param {Boolean} disabled
12499         */
12500         "disabledchange" : true,
12501         /**
12502         * @event collapse
12503         * Fires when this node is collapsed
12504         * @param {Node} this This node
12505         */
12506         "collapse" : true,
12507         /**
12508         * @event beforeclick
12509         * Fires before click processing. Return false to cancel the default action.
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "beforeclick":true,
12514         /**
12515         * @event checkchange
12516         * Fires when a node with a checkbox's checked property changes
12517         * @param {Node} this This node
12518         * @param {Boolean} checked
12519         */
12520         "checkchange":true,
12521         /**
12522         * @event click
12523         * Fires when this node is clicked
12524         * @param {Node} this This node
12525         * @param {Roo.EventObject} e The event object
12526         */
12527         "click":true,
12528         /**
12529         * @event dblclick
12530         * Fires when this node is double clicked
12531         * @param {Node} this This node
12532         * @param {Roo.EventObject} e The event object
12533         */
12534         "dblclick":true,
12535         /**
12536         * @event contextmenu
12537         * Fires when this node is right clicked
12538         * @param {Node} this This node
12539         * @param {Roo.EventObject} e The event object
12540         */
12541         "contextmenu":true,
12542         /**
12543         * @event beforechildrenrendered
12544         * Fires right before the child nodes for this node are rendered
12545         * @param {Node} this This node
12546         */
12547         "beforechildrenrendered":true
12548     });
12549
12550     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12551
12552     /**
12553      * Read-only. The UI for this node
12554      * @type TreeNodeUI
12555      */
12556     this.ui = new uiClass(this);
12557     
12558     // finally support items[]
12559     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12560         return;
12561     }
12562     
12563     
12564     Roo.each(this.attributes.items, function(c) {
12565         this.appendChild(Roo.factory(c,Roo.Tree));
12566     }, this);
12567     delete this.attributes.items;
12568     
12569     
12570     
12571 };
12572 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12573     preventHScroll: true,
12574     /**
12575      * Returns true if this node is expanded
12576      * @return {Boolean}
12577      */
12578     isExpanded : function(){
12579         return this.expanded;
12580     },
12581
12582     /**
12583      * Returns the UI object for this node
12584      * @return {TreeNodeUI}
12585      */
12586     getUI : function(){
12587         return this.ui;
12588     },
12589
12590     // private override
12591     setFirstChild : function(node){
12592         var of = this.firstChild;
12593         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12594         if(this.childrenRendered && of && node != of){
12595             of.renderIndent(true, true);
12596         }
12597         if(this.rendered){
12598             this.renderIndent(true, true);
12599         }
12600     },
12601
12602     // private override
12603     setLastChild : function(node){
12604         var ol = this.lastChild;
12605         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12606         if(this.childrenRendered && ol && node != ol){
12607             ol.renderIndent(true, true);
12608         }
12609         if(this.rendered){
12610             this.renderIndent(true, true);
12611         }
12612     },
12613
12614     // these methods are overridden to provide lazy rendering support
12615     // private override
12616     appendChild : function()
12617     {
12618         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12619         if(node && this.childrenRendered){
12620             node.render();
12621         }
12622         this.ui.updateExpandIcon();
12623         return node;
12624     },
12625
12626     // private override
12627     removeChild : function(node){
12628         this.ownerTree.getSelectionModel().unselect(node);
12629         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12630         // if it's been rendered remove dom node
12631         if(this.childrenRendered){
12632             node.ui.remove();
12633         }
12634         if(this.childNodes.length < 1){
12635             this.collapse(false, false);
12636         }else{
12637             this.ui.updateExpandIcon();
12638         }
12639         if(!this.firstChild) {
12640             this.childrenRendered = false;
12641         }
12642         return node;
12643     },
12644
12645     // private override
12646     insertBefore : function(node, refNode){
12647         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12648         if(newNode && refNode && this.childrenRendered){
12649             node.render();
12650         }
12651         this.ui.updateExpandIcon();
12652         return newNode;
12653     },
12654
12655     /**
12656      * Sets the text for this node
12657      * @param {String} text
12658      */
12659     setText : function(text){
12660         var oldText = this.text;
12661         this.text = text;
12662         this.attributes.text = text;
12663         if(this.rendered){ // event without subscribing
12664             this.ui.onTextChange(this, text, oldText);
12665         }
12666         this.fireEvent("textchange", this, text, oldText);
12667     },
12668
12669     /**
12670      * Triggers selection of this node
12671      */
12672     select : function(){
12673         this.getOwnerTree().getSelectionModel().select(this);
12674     },
12675
12676     /**
12677      * Triggers deselection of this node
12678      */
12679     unselect : function(){
12680         this.getOwnerTree().getSelectionModel().unselect(this);
12681     },
12682
12683     /**
12684      * Returns true if this node is selected
12685      * @return {Boolean}
12686      */
12687     isSelected : function(){
12688         return this.getOwnerTree().getSelectionModel().isSelected(this);
12689     },
12690
12691     /**
12692      * Expand this node.
12693      * @param {Boolean} deep (optional) True to expand all children as well
12694      * @param {Boolean} anim (optional) false to cancel the default animation
12695      * @param {Function} callback (optional) A callback to be called when
12696      * expanding this node completes (does not wait for deep expand to complete).
12697      * Called with 1 parameter, this node.
12698      */
12699     expand : function(deep, anim, callback){
12700         if(!this.expanded){
12701             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12702                 return;
12703             }
12704             if(!this.childrenRendered){
12705                 this.renderChildren();
12706             }
12707             this.expanded = true;
12708             
12709             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12710                 this.ui.animExpand(function(){
12711                     this.fireEvent("expand", this);
12712                     if(typeof callback == "function"){
12713                         callback(this);
12714                     }
12715                     if(deep === true){
12716                         this.expandChildNodes(true);
12717                     }
12718                 }.createDelegate(this));
12719                 return;
12720             }else{
12721                 this.ui.expand();
12722                 this.fireEvent("expand", this);
12723                 if(typeof callback == "function"){
12724                     callback(this);
12725                 }
12726             }
12727         }else{
12728            if(typeof callback == "function"){
12729                callback(this);
12730            }
12731         }
12732         if(deep === true){
12733             this.expandChildNodes(true);
12734         }
12735     },
12736
12737     isHiddenRoot : function(){
12738         return this.isRoot && !this.getOwnerTree().rootVisible;
12739     },
12740
12741     /**
12742      * Collapse this node.
12743      * @param {Boolean} deep (optional) True to collapse all children as well
12744      * @param {Boolean} anim (optional) false to cancel the default animation
12745      */
12746     collapse : function(deep, anim){
12747         if(this.expanded && !this.isHiddenRoot()){
12748             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12749                 return;
12750             }
12751             this.expanded = false;
12752             if((this.getOwnerTree().animate && anim !== false) || anim){
12753                 this.ui.animCollapse(function(){
12754                     this.fireEvent("collapse", this);
12755                     if(deep === true){
12756                         this.collapseChildNodes(true);
12757                     }
12758                 }.createDelegate(this));
12759                 return;
12760             }else{
12761                 this.ui.collapse();
12762                 this.fireEvent("collapse", this);
12763             }
12764         }
12765         if(deep === true){
12766             var cs = this.childNodes;
12767             for(var i = 0, len = cs.length; i < len; i++) {
12768                 cs[i].collapse(true, false);
12769             }
12770         }
12771     },
12772
12773     // private
12774     delayedExpand : function(delay){
12775         if(!this.expandProcId){
12776             this.expandProcId = this.expand.defer(delay, this);
12777         }
12778     },
12779
12780     // private
12781     cancelExpand : function(){
12782         if(this.expandProcId){
12783             clearTimeout(this.expandProcId);
12784         }
12785         this.expandProcId = false;
12786     },
12787
12788     /**
12789      * Toggles expanded/collapsed state of the node
12790      */
12791     toggle : function(){
12792         if(this.expanded){
12793             this.collapse();
12794         }else{
12795             this.expand();
12796         }
12797     },
12798
12799     /**
12800      * Ensures all parent nodes are expanded
12801      */
12802     ensureVisible : function(callback){
12803         var tree = this.getOwnerTree();
12804         tree.expandPath(this.parentNode.getPath(), false, function(){
12805             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12806             Roo.callback(callback);
12807         }.createDelegate(this));
12808     },
12809
12810     /**
12811      * Expand all child nodes
12812      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12813      */
12814     expandChildNodes : function(deep){
12815         var cs = this.childNodes;
12816         for(var i = 0, len = cs.length; i < len; i++) {
12817                 cs[i].expand(deep);
12818         }
12819     },
12820
12821     /**
12822      * Collapse all child nodes
12823      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12824      */
12825     collapseChildNodes : function(deep){
12826         var cs = this.childNodes;
12827         for(var i = 0, len = cs.length; i < len; i++) {
12828                 cs[i].collapse(deep);
12829         }
12830     },
12831
12832     /**
12833      * Disables this node
12834      */
12835     disable : function(){
12836         this.disabled = true;
12837         this.unselect();
12838         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12839             this.ui.onDisableChange(this, true);
12840         }
12841         this.fireEvent("disabledchange", this, true);
12842     },
12843
12844     /**
12845      * Enables this node
12846      */
12847     enable : function(){
12848         this.disabled = false;
12849         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12850             this.ui.onDisableChange(this, false);
12851         }
12852         this.fireEvent("disabledchange", this, false);
12853     },
12854
12855     // private
12856     renderChildren : function(suppressEvent){
12857         if(suppressEvent !== false){
12858             this.fireEvent("beforechildrenrendered", this);
12859         }
12860         var cs = this.childNodes;
12861         for(var i = 0, len = cs.length; i < len; i++){
12862             cs[i].render(true);
12863         }
12864         this.childrenRendered = true;
12865     },
12866
12867     // private
12868     sort : function(fn, scope){
12869         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12870         if(this.childrenRendered){
12871             var cs = this.childNodes;
12872             for(var i = 0, len = cs.length; i < len; i++){
12873                 cs[i].render(true);
12874             }
12875         }
12876     },
12877
12878     // private
12879     render : function(bulkRender){
12880         this.ui.render(bulkRender);
12881         if(!this.rendered){
12882             this.rendered = true;
12883             if(this.expanded){
12884                 this.expanded = false;
12885                 this.expand(false, false);
12886             }
12887         }
12888     },
12889
12890     // private
12891     renderIndent : function(deep, refresh){
12892         if(refresh){
12893             this.ui.childIndent = null;
12894         }
12895         this.ui.renderIndent();
12896         if(deep === true && this.childrenRendered){
12897             var cs = this.childNodes;
12898             for(var i = 0, len = cs.length; i < len; i++){
12899                 cs[i].renderIndent(true, refresh);
12900             }
12901         }
12902     }
12903 });/*
12904  * Based on:
12905  * Ext JS Library 1.1.1
12906  * Copyright(c) 2006-2007, Ext JS, LLC.
12907  *
12908  * Originally Released Under LGPL - original licence link has changed is not relivant.
12909  *
12910  * Fork - LGPL
12911  * <script type="text/javascript">
12912  */
12913  
12914 /**
12915  * @class Roo.tree.AsyncTreeNode
12916  * @extends Roo.tree.TreeNode
12917  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12918  * @constructor
12919  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12920  */
12921  Roo.tree.AsyncTreeNode = function(config){
12922     this.loaded = false;
12923     this.loading = false;
12924     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12925     /**
12926     * @event beforeload
12927     * Fires before this node is loaded, return false to cancel
12928     * @param {Node} this This node
12929     */
12930     this.addEvents({'beforeload':true, 'load': true});
12931     /**
12932     * @event load
12933     * Fires when this node is loaded
12934     * @param {Node} this This node
12935     */
12936     /**
12937      * The loader used by this node (defaults to using the tree's defined loader)
12938      * @type TreeLoader
12939      * @property loader
12940      */
12941 };
12942 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12943     expand : function(deep, anim, callback){
12944         if(this.loading){ // if an async load is already running, waiting til it's done
12945             var timer;
12946             var f = function(){
12947                 if(!this.loading){ // done loading
12948                     clearInterval(timer);
12949                     this.expand(deep, anim, callback);
12950                 }
12951             }.createDelegate(this);
12952             timer = setInterval(f, 200);
12953             return;
12954         }
12955         if(!this.loaded){
12956             if(this.fireEvent("beforeload", this) === false){
12957                 return;
12958             }
12959             this.loading = true;
12960             this.ui.beforeLoad(this);
12961             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12962             if(loader){
12963                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12964                 return;
12965             }
12966         }
12967         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12968     },
12969     
12970     /**
12971      * Returns true if this node is currently loading
12972      * @return {Boolean}
12973      */
12974     isLoading : function(){
12975         return this.loading;  
12976     },
12977     
12978     loadComplete : function(deep, anim, callback){
12979         this.loading = false;
12980         this.loaded = true;
12981         this.ui.afterLoad(this);
12982         this.fireEvent("load", this);
12983         this.expand(deep, anim, callback);
12984     },
12985     
12986     /**
12987      * Returns true if this node has been loaded
12988      * @return {Boolean}
12989      */
12990     isLoaded : function(){
12991         return this.loaded;
12992     },
12993     
12994     hasChildNodes : function(){
12995         if(!this.isLeaf() && !this.loaded){
12996             return true;
12997         }else{
12998             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12999         }
13000     },
13001
13002     /**
13003      * Trigger a reload for this node
13004      * @param {Function} callback
13005      */
13006     reload : function(callback){
13007         this.collapse(false, false);
13008         while(this.firstChild){
13009             this.removeChild(this.firstChild);
13010         }
13011         this.childrenRendered = false;
13012         this.loaded = false;
13013         if(this.isHiddenRoot()){
13014             this.expanded = false;
13015         }
13016         this.expand(false, false, callback);
13017     }
13018 });/*
13019  * Based on:
13020  * Ext JS Library 1.1.1
13021  * Copyright(c) 2006-2007, Ext JS, LLC.
13022  *
13023  * Originally Released Under LGPL - original licence link has changed is not relivant.
13024  *
13025  * Fork - LGPL
13026  * <script type="text/javascript">
13027  */
13028  
13029 /**
13030  * @class Roo.tree.TreeNodeUI
13031  * @constructor
13032  * @param {Object} node The node to render
13033  * The TreeNode UI implementation is separate from the
13034  * tree implementation. Unless you are customizing the tree UI,
13035  * you should never have to use this directly.
13036  */
13037 Roo.tree.TreeNodeUI = function(node){
13038     this.node = node;
13039     this.rendered = false;
13040     this.animating = false;
13041     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13042 };
13043
13044 Roo.tree.TreeNodeUI.prototype = {
13045     removeChild : function(node){
13046         if(this.rendered){
13047             this.ctNode.removeChild(node.ui.getEl());
13048         }
13049     },
13050
13051     beforeLoad : function(){
13052          this.addClass("x-tree-node-loading");
13053     },
13054
13055     afterLoad : function(){
13056          this.removeClass("x-tree-node-loading");
13057     },
13058
13059     onTextChange : function(node, text, oldText){
13060         if(this.rendered){
13061             this.textNode.innerHTML = text;
13062         }
13063     },
13064
13065     onDisableChange : function(node, state){
13066         this.disabled = state;
13067         if(state){
13068             this.addClass("x-tree-node-disabled");
13069         }else{
13070             this.removeClass("x-tree-node-disabled");
13071         }
13072     },
13073
13074     onSelectedChange : function(state){
13075         if(state){
13076             this.focus();
13077             this.addClass("x-tree-selected");
13078         }else{
13079             //this.blur();
13080             this.removeClass("x-tree-selected");
13081         }
13082     },
13083
13084     onMove : function(tree, node, oldParent, newParent, index, refNode){
13085         this.childIndent = null;
13086         if(this.rendered){
13087             var targetNode = newParent.ui.getContainer();
13088             if(!targetNode){//target not rendered
13089                 this.holder = document.createElement("div");
13090                 this.holder.appendChild(this.wrap);
13091                 return;
13092             }
13093             var insertBefore = refNode ? refNode.ui.getEl() : null;
13094             if(insertBefore){
13095                 targetNode.insertBefore(this.wrap, insertBefore);
13096             }else{
13097                 targetNode.appendChild(this.wrap);
13098             }
13099             this.node.renderIndent(true);
13100         }
13101     },
13102
13103     addClass : function(cls){
13104         if(this.elNode){
13105             Roo.fly(this.elNode).addClass(cls);
13106         }
13107     },
13108
13109     removeClass : function(cls){
13110         if(this.elNode){
13111             Roo.fly(this.elNode).removeClass(cls);
13112         }
13113     },
13114
13115     remove : function(){
13116         if(this.rendered){
13117             this.holder = document.createElement("div");
13118             this.holder.appendChild(this.wrap);
13119         }
13120     },
13121
13122     fireEvent : function(){
13123         return this.node.fireEvent.apply(this.node, arguments);
13124     },
13125
13126     initEvents : function(){
13127         this.node.on("move", this.onMove, this);
13128         var E = Roo.EventManager;
13129         var a = this.anchor;
13130
13131         var el = Roo.fly(a, '_treeui');
13132
13133         if(Roo.isOpera){ // opera render bug ignores the CSS
13134             el.setStyle("text-decoration", "none");
13135         }
13136
13137         el.on("click", this.onClick, this);
13138         el.on("dblclick", this.onDblClick, this);
13139
13140         if(this.checkbox){
13141             Roo.EventManager.on(this.checkbox,
13142                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13143         }
13144
13145         el.on("contextmenu", this.onContextMenu, this);
13146
13147         var icon = Roo.fly(this.iconNode);
13148         icon.on("click", this.onClick, this);
13149         icon.on("dblclick", this.onDblClick, this);
13150         icon.on("contextmenu", this.onContextMenu, this);
13151         E.on(this.ecNode, "click", this.ecClick, this, true);
13152
13153         if(this.node.disabled){
13154             this.addClass("x-tree-node-disabled");
13155         }
13156         if(this.node.hidden){
13157             this.addClass("x-tree-node-disabled");
13158         }
13159         var ot = this.node.getOwnerTree();
13160         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13161         if(dd && (!this.node.isRoot || ot.rootVisible)){
13162             Roo.dd.Registry.register(this.elNode, {
13163                 node: this.node,
13164                 handles: this.getDDHandles(),
13165                 isHandle: false
13166             });
13167         }
13168     },
13169
13170     getDDHandles : function(){
13171         return [this.iconNode, this.textNode];
13172     },
13173
13174     hide : function(){
13175         if(this.rendered){
13176             this.wrap.style.display = "none";
13177         }
13178     },
13179
13180     show : function(){
13181         if(this.rendered){
13182             this.wrap.style.display = "";
13183         }
13184     },
13185
13186     onContextMenu : function(e){
13187         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13188             e.preventDefault();
13189             this.focus();
13190             this.fireEvent("contextmenu", this.node, e);
13191         }
13192     },
13193
13194     onClick : function(e){
13195         if(this.dropping){
13196             e.stopEvent();
13197             return;
13198         }
13199         if(this.fireEvent("beforeclick", this.node, e) !== false){
13200             if(!this.disabled && this.node.attributes.href){
13201                 this.fireEvent("click", this.node, e);
13202                 return;
13203             }
13204             e.preventDefault();
13205             if(this.disabled){
13206                 return;
13207             }
13208
13209             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13210                 this.node.toggle();
13211             }
13212
13213             this.fireEvent("click", this.node, e);
13214         }else{
13215             e.stopEvent();
13216         }
13217     },
13218
13219     onDblClick : function(e){
13220         e.preventDefault();
13221         if(this.disabled){
13222             return;
13223         }
13224         if(this.checkbox){
13225             this.toggleCheck();
13226         }
13227         if(!this.animating && this.node.hasChildNodes()){
13228             this.node.toggle();
13229         }
13230         this.fireEvent("dblclick", this.node, e);
13231     },
13232
13233     onCheckChange : function(){
13234         var checked = this.checkbox.checked;
13235         this.node.attributes.checked = checked;
13236         this.fireEvent('checkchange', this.node, checked);
13237     },
13238
13239     ecClick : function(e){
13240         if(!this.animating && this.node.hasChildNodes()){
13241             this.node.toggle();
13242         }
13243     },
13244
13245     startDrop : function(){
13246         this.dropping = true;
13247     },
13248
13249     // delayed drop so the click event doesn't get fired on a drop
13250     endDrop : function(){
13251        setTimeout(function(){
13252            this.dropping = false;
13253        }.createDelegate(this), 50);
13254     },
13255
13256     expand : function(){
13257         this.updateExpandIcon();
13258         this.ctNode.style.display = "";
13259     },
13260
13261     focus : function(){
13262         if(!this.node.preventHScroll){
13263             try{this.anchor.focus();
13264             }catch(e){}
13265         }else if(!Roo.isIE){
13266             try{
13267                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13268                 var l = noscroll.scrollLeft;
13269                 this.anchor.focus();
13270                 noscroll.scrollLeft = l;
13271             }catch(e){}
13272         }
13273     },
13274
13275     toggleCheck : function(value){
13276         var cb = this.checkbox;
13277         if(cb){
13278             cb.checked = (value === undefined ? !cb.checked : value);
13279         }
13280     },
13281
13282     blur : function(){
13283         try{
13284             this.anchor.blur();
13285         }catch(e){}
13286     },
13287
13288     animExpand : function(callback){
13289         var ct = Roo.get(this.ctNode);
13290         ct.stopFx();
13291         if(!this.node.hasChildNodes()){
13292             this.updateExpandIcon();
13293             this.ctNode.style.display = "";
13294             Roo.callback(callback);
13295             return;
13296         }
13297         this.animating = true;
13298         this.updateExpandIcon();
13299
13300         ct.slideIn('t', {
13301            callback : function(){
13302                this.animating = false;
13303                Roo.callback(callback);
13304             },
13305             scope: this,
13306             duration: this.node.ownerTree.duration || .25
13307         });
13308     },
13309
13310     highlight : function(){
13311         var tree = this.node.getOwnerTree();
13312         Roo.fly(this.wrap).highlight(
13313             tree.hlColor || "C3DAF9",
13314             {endColor: tree.hlBaseColor}
13315         );
13316     },
13317
13318     collapse : function(){
13319         this.updateExpandIcon();
13320         this.ctNode.style.display = "none";
13321     },
13322
13323     animCollapse : function(callback){
13324         var ct = Roo.get(this.ctNode);
13325         ct.enableDisplayMode('block');
13326         ct.stopFx();
13327
13328         this.animating = true;
13329         this.updateExpandIcon();
13330
13331         ct.slideOut('t', {
13332             callback : function(){
13333                this.animating = false;
13334                Roo.callback(callback);
13335             },
13336             scope: this,
13337             duration: this.node.ownerTree.duration || .25
13338         });
13339     },
13340
13341     getContainer : function(){
13342         return this.ctNode;
13343     },
13344
13345     getEl : function(){
13346         return this.wrap;
13347     },
13348
13349     appendDDGhost : function(ghostNode){
13350         ghostNode.appendChild(this.elNode.cloneNode(true));
13351     },
13352
13353     getDDRepairXY : function(){
13354         return Roo.lib.Dom.getXY(this.iconNode);
13355     },
13356
13357     onRender : function(){
13358         this.render();
13359     },
13360
13361     render : function(bulkRender){
13362         var n = this.node, a = n.attributes;
13363         var targetNode = n.parentNode ?
13364               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13365
13366         if(!this.rendered){
13367             this.rendered = true;
13368
13369             this.renderElements(n, a, targetNode, bulkRender);
13370
13371             if(a.qtip){
13372                if(this.textNode.setAttributeNS){
13373                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13374                    if(a.qtipTitle){
13375                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13376                    }
13377                }else{
13378                    this.textNode.setAttribute("ext:qtip", a.qtip);
13379                    if(a.qtipTitle){
13380                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13381                    }
13382                }
13383             }else if(a.qtipCfg){
13384                 a.qtipCfg.target = Roo.id(this.textNode);
13385                 Roo.QuickTips.register(a.qtipCfg);
13386             }
13387             this.initEvents();
13388             if(!this.node.expanded){
13389                 this.updateExpandIcon();
13390             }
13391         }else{
13392             if(bulkRender === true) {
13393                 targetNode.appendChild(this.wrap);
13394             }
13395         }
13396     },
13397
13398     renderElements : function(n, a, targetNode, bulkRender)
13399     {
13400         // add some indent caching, this helps performance when rendering a large tree
13401         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13402         var t = n.getOwnerTree();
13403         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13404         if (typeof(n.attributes.html) != 'undefined') {
13405             txt = n.attributes.html;
13406         }
13407         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13408         var cb = typeof a.checked == 'boolean';
13409         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13410         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13411             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13412             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13413             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13414             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13415             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13416              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13417                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13418             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13419             "</li>"];
13420
13421         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13422             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13423                                 n.nextSibling.ui.getEl(), buf.join(""));
13424         }else{
13425             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13426         }
13427
13428         this.elNode = this.wrap.childNodes[0];
13429         this.ctNode = this.wrap.childNodes[1];
13430         var cs = this.elNode.childNodes;
13431         this.indentNode = cs[0];
13432         this.ecNode = cs[1];
13433         this.iconNode = cs[2];
13434         var index = 3;
13435         if(cb){
13436             this.checkbox = cs[3];
13437             index++;
13438         }
13439         this.anchor = cs[index];
13440         this.textNode = cs[index].firstChild;
13441     },
13442
13443     getAnchor : function(){
13444         return this.anchor;
13445     },
13446
13447     getTextEl : function(){
13448         return this.textNode;
13449     },
13450
13451     getIconEl : function(){
13452         return this.iconNode;
13453     },
13454
13455     isChecked : function(){
13456         return this.checkbox ? this.checkbox.checked : false;
13457     },
13458
13459     updateExpandIcon : function(){
13460         if(this.rendered){
13461             var n = this.node, c1, c2;
13462             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13463             var hasChild = n.hasChildNodes();
13464             if(hasChild){
13465                 if(n.expanded){
13466                     cls += "-minus";
13467                     c1 = "x-tree-node-collapsed";
13468                     c2 = "x-tree-node-expanded";
13469                 }else{
13470                     cls += "-plus";
13471                     c1 = "x-tree-node-expanded";
13472                     c2 = "x-tree-node-collapsed";
13473                 }
13474                 if(this.wasLeaf){
13475                     this.removeClass("x-tree-node-leaf");
13476                     this.wasLeaf = false;
13477                 }
13478                 if(this.c1 != c1 || this.c2 != c2){
13479                     Roo.fly(this.elNode).replaceClass(c1, c2);
13480                     this.c1 = c1; this.c2 = c2;
13481                 }
13482             }else{
13483                 // this changes non-leafs into leafs if they have no children.
13484                 // it's not very rational behaviour..
13485                 
13486                 if(!this.wasLeaf && this.node.leaf){
13487                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13488                     delete this.c1;
13489                     delete this.c2;
13490                     this.wasLeaf = true;
13491                 }
13492             }
13493             var ecc = "x-tree-ec-icon "+cls;
13494             if(this.ecc != ecc){
13495                 this.ecNode.className = ecc;
13496                 this.ecc = ecc;
13497             }
13498         }
13499     },
13500
13501     getChildIndent : function(){
13502         if(!this.childIndent){
13503             var buf = [];
13504             var p = this.node;
13505             while(p){
13506                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13507                     if(!p.isLast()) {
13508                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13509                     } else {
13510                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13511                     }
13512                 }
13513                 p = p.parentNode;
13514             }
13515             this.childIndent = buf.join("");
13516         }
13517         return this.childIndent;
13518     },
13519
13520     renderIndent : function(){
13521         if(this.rendered){
13522             var indent = "";
13523             var p = this.node.parentNode;
13524             if(p){
13525                 indent = p.ui.getChildIndent();
13526             }
13527             if(this.indentMarkup != indent){ // don't rerender if not required
13528                 this.indentNode.innerHTML = indent;
13529                 this.indentMarkup = indent;
13530             }
13531             this.updateExpandIcon();
13532         }
13533     }
13534 };
13535
13536 Roo.tree.RootTreeNodeUI = function(){
13537     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13538 };
13539 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13540     render : function(){
13541         if(!this.rendered){
13542             var targetNode = this.node.ownerTree.innerCt.dom;
13543             this.node.expanded = true;
13544             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13545             this.wrap = this.ctNode = targetNode.firstChild;
13546         }
13547     },
13548     collapse : function(){
13549     },
13550     expand : function(){
13551     }
13552 });/*
13553  * Based on:
13554  * Ext JS Library 1.1.1
13555  * Copyright(c) 2006-2007, Ext JS, LLC.
13556  *
13557  * Originally Released Under LGPL - original licence link has changed is not relivant.
13558  *
13559  * Fork - LGPL
13560  * <script type="text/javascript">
13561  */
13562 /**
13563  * @class Roo.tree.TreeLoader
13564  * @extends Roo.util.Observable
13565  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13566  * nodes from a specified URL. The response must be a javascript Array definition
13567  * who's elements are node definition objects. eg:
13568  * <pre><code>
13569 {  success : true,
13570    data :      [
13571    
13572     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13573     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13574     ]
13575 }
13576
13577
13578 </code></pre>
13579  * <br><br>
13580  * The old style respose with just an array is still supported, but not recommended.
13581  * <br><br>
13582  *
13583  * A server request is sent, and child nodes are loaded only when a node is expanded.
13584  * The loading node's id is passed to the server under the parameter name "node" to
13585  * enable the server to produce the correct child nodes.
13586  * <br><br>
13587  * To pass extra parameters, an event handler may be attached to the "beforeload"
13588  * event, and the parameters specified in the TreeLoader's baseParams property:
13589  * <pre><code>
13590     myTreeLoader.on("beforeload", function(treeLoader, node) {
13591         this.baseParams.category = node.attributes.category;
13592     }, this);
13593     
13594 </code></pre>
13595  *
13596  * This would pass an HTTP parameter called "category" to the server containing
13597  * the value of the Node's "category" attribute.
13598  * @constructor
13599  * Creates a new Treeloader.
13600  * @param {Object} config A config object containing config properties.
13601  */
13602 Roo.tree.TreeLoader = function(config){
13603     this.baseParams = {};
13604     this.requestMethod = "POST";
13605     Roo.apply(this, config);
13606
13607     this.addEvents({
13608     
13609         /**
13610          * @event beforeload
13611          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13612          * @param {Object} This TreeLoader object.
13613          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13614          * @param {Object} callback The callback function specified in the {@link #load} call.
13615          */
13616         beforeload : true,
13617         /**
13618          * @event load
13619          * Fires when the node has been successfuly loaded.
13620          * @param {Object} This TreeLoader object.
13621          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13622          * @param {Object} response The response object containing the data from the server.
13623          */
13624         load : true,
13625         /**
13626          * @event loadexception
13627          * Fires if the network request failed.
13628          * @param {Object} This TreeLoader object.
13629          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13630          * @param {Object} response The response object containing the data from the server.
13631          */
13632         loadexception : true,
13633         /**
13634          * @event create
13635          * Fires before a node is created, enabling you to return custom Node types 
13636          * @param {Object} This TreeLoader object.
13637          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13638          */
13639         create : true
13640     });
13641
13642     Roo.tree.TreeLoader.superclass.constructor.call(this);
13643 };
13644
13645 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13646     /**
13647     * @cfg {String} dataUrl The URL from which to request a Json string which
13648     * specifies an array of node definition object representing the child nodes
13649     * to be loaded.
13650     */
13651     /**
13652     * @cfg {String} requestMethod either GET or POST
13653     * defaults to POST (due to BC)
13654     * to be loaded.
13655     */
13656     /**
13657     * @cfg {Object} baseParams (optional) An object containing properties which
13658     * specify HTTP parameters to be passed to each request for child nodes.
13659     */
13660     /**
13661     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13662     * created by this loader. If the attributes sent by the server have an attribute in this object,
13663     * they take priority.
13664     */
13665     /**
13666     * @cfg {Object} uiProviders (optional) An object containing properties which
13667     * 
13668     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13669     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13670     * <i>uiProvider</i> attribute of a returned child node is a string rather
13671     * than a reference to a TreeNodeUI implementation, this that string value
13672     * is used as a property name in the uiProviders object. You can define the provider named
13673     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13674     */
13675     uiProviders : {},
13676
13677     /**
13678     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13679     * child nodes before loading.
13680     */
13681     clearOnLoad : true,
13682
13683     /**
13684     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13685     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13686     * Grid query { data : [ .....] }
13687     */
13688     
13689     root : false,
13690      /**
13691     * @cfg {String} queryParam (optional) 
13692     * Name of the query as it will be passed on the querystring (defaults to 'node')
13693     * eg. the request will be ?node=[id]
13694     */
13695     
13696     
13697     queryParam: false,
13698     
13699     /**
13700      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13701      * This is called automatically when a node is expanded, but may be used to reload
13702      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13703      * @param {Roo.tree.TreeNode} node
13704      * @param {Function} callback
13705      */
13706     load : function(node, callback){
13707         if(this.clearOnLoad){
13708             while(node.firstChild){
13709                 node.removeChild(node.firstChild);
13710             }
13711         }
13712         if(node.attributes.children){ // preloaded json children
13713             var cs = node.attributes.children;
13714             for(var i = 0, len = cs.length; i < len; i++){
13715                 node.appendChild(this.createNode(cs[i]));
13716             }
13717             if(typeof callback == "function"){
13718                 callback();
13719             }
13720         }else if(this.dataUrl){
13721             this.requestData(node, callback);
13722         }
13723     },
13724
13725     getParams: function(node){
13726         var buf = [], bp = this.baseParams;
13727         for(var key in bp){
13728             if(typeof bp[key] != "function"){
13729                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13730             }
13731         }
13732         var n = this.queryParam === false ? 'node' : this.queryParam;
13733         buf.push(n + "=", encodeURIComponent(node.id));
13734         return buf.join("");
13735     },
13736
13737     requestData : function(node, callback){
13738         if(this.fireEvent("beforeload", this, node, callback) !== false){
13739             this.transId = Roo.Ajax.request({
13740                 method:this.requestMethod,
13741                 url: this.dataUrl||this.url,
13742                 success: this.handleResponse,
13743                 failure: this.handleFailure,
13744                 scope: this,
13745                 argument: {callback: callback, node: node},
13746                 params: this.getParams(node)
13747             });
13748         }else{
13749             // if the load is cancelled, make sure we notify
13750             // the node that we are done
13751             if(typeof callback == "function"){
13752                 callback();
13753             }
13754         }
13755     },
13756
13757     isLoading : function(){
13758         return this.transId ? true : false;
13759     },
13760
13761     abort : function(){
13762         if(this.isLoading()){
13763             Roo.Ajax.abort(this.transId);
13764         }
13765     },
13766
13767     // private
13768     createNode : function(attr)
13769     {
13770         // apply baseAttrs, nice idea Corey!
13771         if(this.baseAttrs){
13772             Roo.applyIf(attr, this.baseAttrs);
13773         }
13774         if(this.applyLoader !== false){
13775             attr.loader = this;
13776         }
13777         // uiProvider = depreciated..
13778         
13779         if(typeof(attr.uiProvider) == 'string'){
13780            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13781                 /**  eval:var:attr */ eval(attr.uiProvider);
13782         }
13783         if(typeof(this.uiProviders['default']) != 'undefined') {
13784             attr.uiProvider = this.uiProviders['default'];
13785         }
13786         
13787         this.fireEvent('create', this, attr);
13788         
13789         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13790         return(attr.leaf ?
13791                         new Roo.tree.TreeNode(attr) :
13792                         new Roo.tree.AsyncTreeNode(attr));
13793     },
13794
13795     processResponse : function(response, node, callback)
13796     {
13797         var json = response.responseText;
13798         try {
13799             
13800             var o = Roo.decode(json);
13801             
13802             if (this.root === false && typeof(o.success) != undefined) {
13803                 this.root = 'data'; // the default behaviour for list like data..
13804                 }
13805                 
13806             if (this.root !== false &&  !o.success) {
13807                 // it's a failure condition.
13808                 var a = response.argument;
13809                 this.fireEvent("loadexception", this, a.node, response);
13810                 Roo.log("Load failed - should have a handler really");
13811                 return;
13812             }
13813             
13814             
13815             
13816             if (this.root !== false) {
13817                  o = o[this.root];
13818             }
13819             
13820             for(var i = 0, len = o.length; i < len; i++){
13821                 var n = this.createNode(o[i]);
13822                 if(n){
13823                     node.appendChild(n);
13824                 }
13825             }
13826             if(typeof callback == "function"){
13827                 callback(this, node);
13828             }
13829         }catch(e){
13830             this.handleFailure(response);
13831         }
13832     },
13833
13834     handleResponse : function(response){
13835         this.transId = false;
13836         var a = response.argument;
13837         this.processResponse(response, a.node, a.callback);
13838         this.fireEvent("load", this, a.node, response);
13839     },
13840
13841     handleFailure : function(response)
13842     {
13843         // should handle failure better..
13844         this.transId = false;
13845         var a = response.argument;
13846         this.fireEvent("loadexception", this, a.node, response);
13847         if(typeof a.callback == "function"){
13848             a.callback(this, a.node);
13849         }
13850     }
13851 });/*
13852  * Based on:
13853  * Ext JS Library 1.1.1
13854  * Copyright(c) 2006-2007, Ext JS, LLC.
13855  *
13856  * Originally Released Under LGPL - original licence link has changed is not relivant.
13857  *
13858  * Fork - LGPL
13859  * <script type="text/javascript">
13860  */
13861
13862 /**
13863 * @class Roo.tree.TreeFilter
13864 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13865 * @param {TreePanel} tree
13866 * @param {Object} config (optional)
13867  */
13868 Roo.tree.TreeFilter = function(tree, config){
13869     this.tree = tree;
13870     this.filtered = {};
13871     Roo.apply(this, config);
13872 };
13873
13874 Roo.tree.TreeFilter.prototype = {
13875     clearBlank:false,
13876     reverse:false,
13877     autoClear:false,
13878     remove:false,
13879
13880      /**
13881      * Filter the data by a specific attribute.
13882      * @param {String/RegExp} value Either string that the attribute value
13883      * should start with or a RegExp to test against the attribute
13884      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13885      * @param {TreeNode} startNode (optional) The node to start the filter at.
13886      */
13887     filter : function(value, attr, startNode){
13888         attr = attr || "text";
13889         var f;
13890         if(typeof value == "string"){
13891             var vlen = value.length;
13892             // auto clear empty filter
13893             if(vlen == 0 && this.clearBlank){
13894                 this.clear();
13895                 return;
13896             }
13897             value = value.toLowerCase();
13898             f = function(n){
13899                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13900             };
13901         }else if(value.exec){ // regex?
13902             f = function(n){
13903                 return value.test(n.attributes[attr]);
13904             };
13905         }else{
13906             throw 'Illegal filter type, must be string or regex';
13907         }
13908         this.filterBy(f, null, startNode);
13909         },
13910
13911     /**
13912      * Filter by a function. The passed function will be called with each
13913      * node in the tree (or from the startNode). If the function returns true, the node is kept
13914      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13915      * @param {Function} fn The filter function
13916      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13917      */
13918     filterBy : function(fn, scope, startNode){
13919         startNode = startNode || this.tree.root;
13920         if(this.autoClear){
13921             this.clear();
13922         }
13923         var af = this.filtered, rv = this.reverse;
13924         var f = function(n){
13925             if(n == startNode){
13926                 return true;
13927             }
13928             if(af[n.id]){
13929                 return false;
13930             }
13931             var m = fn.call(scope || n, n);
13932             if(!m || rv){
13933                 af[n.id] = n;
13934                 n.ui.hide();
13935                 return false;
13936             }
13937             return true;
13938         };
13939         startNode.cascade(f);
13940         if(this.remove){
13941            for(var id in af){
13942                if(typeof id != "function"){
13943                    var n = af[id];
13944                    if(n && n.parentNode){
13945                        n.parentNode.removeChild(n);
13946                    }
13947                }
13948            }
13949         }
13950     },
13951
13952     /**
13953      * Clears the current filter. Note: with the "remove" option
13954      * set a filter cannot be cleared.
13955      */
13956     clear : function(){
13957         var t = this.tree;
13958         var af = this.filtered;
13959         for(var id in af){
13960             if(typeof id != "function"){
13961                 var n = af[id];
13962                 if(n){
13963                     n.ui.show();
13964                 }
13965             }
13966         }
13967         this.filtered = {};
13968     }
13969 };
13970 /*
13971  * Based on:
13972  * Ext JS Library 1.1.1
13973  * Copyright(c) 2006-2007, Ext JS, LLC.
13974  *
13975  * Originally Released Under LGPL - original licence link has changed is not relivant.
13976  *
13977  * Fork - LGPL
13978  * <script type="text/javascript">
13979  */
13980  
13981
13982 /**
13983  * @class Roo.tree.TreeSorter
13984  * Provides sorting of nodes in a TreePanel
13985  * 
13986  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13987  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13988  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13989  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13990  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13991  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13992  * @constructor
13993  * @param {TreePanel} tree
13994  * @param {Object} config
13995  */
13996 Roo.tree.TreeSorter = function(tree, config){
13997     Roo.apply(this, config);
13998     tree.on("beforechildrenrendered", this.doSort, this);
13999     tree.on("append", this.updateSort, this);
14000     tree.on("insert", this.updateSort, this);
14001     
14002     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14003     var p = this.property || "text";
14004     var sortType = this.sortType;
14005     var fs = this.folderSort;
14006     var cs = this.caseSensitive === true;
14007     var leafAttr = this.leafAttr || 'leaf';
14008
14009     this.sortFn = function(n1, n2){
14010         if(fs){
14011             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14012                 return 1;
14013             }
14014             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14015                 return -1;
14016             }
14017         }
14018         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14019         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14020         if(v1 < v2){
14021                         return dsc ? +1 : -1;
14022                 }else if(v1 > v2){
14023                         return dsc ? -1 : +1;
14024         }else{
14025                 return 0;
14026         }
14027     };
14028 };
14029
14030 Roo.tree.TreeSorter.prototype = {
14031     doSort : function(node){
14032         node.sort(this.sortFn);
14033     },
14034     
14035     compareNodes : function(n1, n2){
14036         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14037     },
14038     
14039     updateSort : function(tree, node){
14040         if(node.childrenRendered){
14041             this.doSort.defer(1, this, [node]);
14042         }
14043     }
14044 };/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055 if(Roo.dd.DropZone){
14056     
14057 Roo.tree.TreeDropZone = function(tree, config){
14058     this.allowParentInsert = false;
14059     this.allowContainerDrop = false;
14060     this.appendOnly = false;
14061     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14062     this.tree = tree;
14063     this.lastInsertClass = "x-tree-no-status";
14064     this.dragOverData = {};
14065 };
14066
14067 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14068     ddGroup : "TreeDD",
14069     scroll:  true,
14070     
14071     expandDelay : 1000,
14072     
14073     expandNode : function(node){
14074         if(node.hasChildNodes() && !node.isExpanded()){
14075             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14076         }
14077     },
14078     
14079     queueExpand : function(node){
14080         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14081     },
14082     
14083     cancelExpand : function(){
14084         if(this.expandProcId){
14085             clearTimeout(this.expandProcId);
14086             this.expandProcId = false;
14087         }
14088     },
14089     
14090     isValidDropPoint : function(n, pt, dd, e, data){
14091         if(!n || !data){ return false; }
14092         var targetNode = n.node;
14093         var dropNode = data.node;
14094         // default drop rules
14095         if(!(targetNode && targetNode.isTarget && pt)){
14096             return false;
14097         }
14098         if(pt == "append" && targetNode.allowChildren === false){
14099             return false;
14100         }
14101         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14102             return false;
14103         }
14104         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14105             return false;
14106         }
14107         // reuse the object
14108         var overEvent = this.dragOverData;
14109         overEvent.tree = this.tree;
14110         overEvent.target = targetNode;
14111         overEvent.data = data;
14112         overEvent.point = pt;
14113         overEvent.source = dd;
14114         overEvent.rawEvent = e;
14115         overEvent.dropNode = dropNode;
14116         overEvent.cancel = false;  
14117         var result = this.tree.fireEvent("nodedragover", overEvent);
14118         return overEvent.cancel === false && result !== false;
14119     },
14120     
14121     getDropPoint : function(e, n, dd)
14122     {
14123         var tn = n.node;
14124         if(tn.isRoot){
14125             return tn.allowChildren !== false ? "append" : false; // always append for root
14126         }
14127         var dragEl = n.ddel;
14128         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14129         var y = Roo.lib.Event.getPageY(e);
14130         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14131         
14132         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14133         var noAppend = tn.allowChildren === false;
14134         if(this.appendOnly || tn.parentNode.allowChildren === false){
14135             return noAppend ? false : "append";
14136         }
14137         var noBelow = false;
14138         if(!this.allowParentInsert){
14139             noBelow = tn.hasChildNodes() && tn.isExpanded();
14140         }
14141         var q = (b - t) / (noAppend ? 2 : 3);
14142         if(y >= t && y < (t + q)){
14143             return "above";
14144         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14145             return "below";
14146         }else{
14147             return "append";
14148         }
14149     },
14150     
14151     onNodeEnter : function(n, dd, e, data)
14152     {
14153         this.cancelExpand();
14154     },
14155     
14156     onNodeOver : function(n, dd, e, data)
14157     {
14158        
14159         var pt = this.getDropPoint(e, n, dd);
14160         var node = n.node;
14161         
14162         // auto node expand check
14163         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14164             this.queueExpand(node);
14165         }else if(pt != "append"){
14166             this.cancelExpand();
14167         }
14168         
14169         // set the insert point style on the target node
14170         var returnCls = this.dropNotAllowed;
14171         if(this.isValidDropPoint(n, pt, dd, e, data)){
14172            if(pt){
14173                var el = n.ddel;
14174                var cls;
14175                if(pt == "above"){
14176                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14177                    cls = "x-tree-drag-insert-above";
14178                }else if(pt == "below"){
14179                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14180                    cls = "x-tree-drag-insert-below";
14181                }else{
14182                    returnCls = "x-tree-drop-ok-append";
14183                    cls = "x-tree-drag-append";
14184                }
14185                if(this.lastInsertClass != cls){
14186                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14187                    this.lastInsertClass = cls;
14188                }
14189            }
14190        }
14191        return returnCls;
14192     },
14193     
14194     onNodeOut : function(n, dd, e, data){
14195         
14196         this.cancelExpand();
14197         this.removeDropIndicators(n);
14198     },
14199     
14200     onNodeDrop : function(n, dd, e, data){
14201         var point = this.getDropPoint(e, n, dd);
14202         var targetNode = n.node;
14203         targetNode.ui.startDrop();
14204         if(!this.isValidDropPoint(n, point, dd, e, data)){
14205             targetNode.ui.endDrop();
14206             return false;
14207         }
14208         // first try to find the drop node
14209         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14210         var dropEvent = {
14211             tree : this.tree,
14212             target: targetNode,
14213             data: data,
14214             point: point,
14215             source: dd,
14216             rawEvent: e,
14217             dropNode: dropNode,
14218             cancel: !dropNode   
14219         };
14220         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14221         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14222             targetNode.ui.endDrop();
14223             return false;
14224         }
14225         // allow target changing
14226         targetNode = dropEvent.target;
14227         if(point == "append" && !targetNode.isExpanded()){
14228             targetNode.expand(false, null, function(){
14229                 this.completeDrop(dropEvent);
14230             }.createDelegate(this));
14231         }else{
14232             this.completeDrop(dropEvent);
14233         }
14234         return true;
14235     },
14236     
14237     completeDrop : function(de){
14238         var ns = de.dropNode, p = de.point, t = de.target;
14239         if(!(ns instanceof Array)){
14240             ns = [ns];
14241         }
14242         var n;
14243         for(var i = 0, len = ns.length; i < len; i++){
14244             n = ns[i];
14245             if(p == "above"){
14246                 t.parentNode.insertBefore(n, t);
14247             }else if(p == "below"){
14248                 t.parentNode.insertBefore(n, t.nextSibling);
14249             }else{
14250                 t.appendChild(n);
14251             }
14252         }
14253         n.ui.focus();
14254         if(this.tree.hlDrop){
14255             n.ui.highlight();
14256         }
14257         t.ui.endDrop();
14258         this.tree.fireEvent("nodedrop", de);
14259     },
14260     
14261     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14262         if(this.tree.hlDrop){
14263             dropNode.ui.focus();
14264             dropNode.ui.highlight();
14265         }
14266         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14267     },
14268     
14269     getTree : function(){
14270         return this.tree;
14271     },
14272     
14273     removeDropIndicators : function(n){
14274         if(n && n.ddel){
14275             var el = n.ddel;
14276             Roo.fly(el).removeClass([
14277                     "x-tree-drag-insert-above",
14278                     "x-tree-drag-insert-below",
14279                     "x-tree-drag-append"]);
14280             this.lastInsertClass = "_noclass";
14281         }
14282     },
14283     
14284     beforeDragDrop : function(target, e, id){
14285         this.cancelExpand();
14286         return true;
14287     },
14288     
14289     afterRepair : function(data){
14290         if(data && Roo.enableFx){
14291             data.node.ui.highlight();
14292         }
14293         this.hideProxy();
14294     } 
14295     
14296 });
14297
14298 }
14299 /*
14300  * Based on:
14301  * Ext JS Library 1.1.1
14302  * Copyright(c) 2006-2007, Ext JS, LLC.
14303  *
14304  * Originally Released Under LGPL - original licence link has changed is not relivant.
14305  *
14306  * Fork - LGPL
14307  * <script type="text/javascript">
14308  */
14309  
14310
14311 if(Roo.dd.DragZone){
14312 Roo.tree.TreeDragZone = function(tree, config){
14313     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14314     this.tree = tree;
14315 };
14316
14317 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14318     ddGroup : "TreeDD",
14319    
14320     onBeforeDrag : function(data, e){
14321         var n = data.node;
14322         return n && n.draggable && !n.disabled;
14323     },
14324      
14325     
14326     onInitDrag : function(e){
14327         var data = this.dragData;
14328         this.tree.getSelectionModel().select(data.node);
14329         this.proxy.update("");
14330         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14331         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14332     },
14333     
14334     getRepairXY : function(e, data){
14335         return data.node.ui.getDDRepairXY();
14336     },
14337     
14338     onEndDrag : function(data, e){
14339         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14340         
14341         
14342     },
14343     
14344     onValidDrop : function(dd, e, id){
14345         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14346         this.hideProxy();
14347     },
14348     
14349     beforeInvalidDrop : function(e, id){
14350         // this scrolls the original position back into view
14351         var sm = this.tree.getSelectionModel();
14352         sm.clearSelections();
14353         sm.select(this.dragData.node);
14354     }
14355 });
14356 }/*
14357  * Based on:
14358  * Ext JS Library 1.1.1
14359  * Copyright(c) 2006-2007, Ext JS, LLC.
14360  *
14361  * Originally Released Under LGPL - original licence link has changed is not relivant.
14362  *
14363  * Fork - LGPL
14364  * <script type="text/javascript">
14365  */
14366 /**
14367  * @class Roo.tree.TreeEditor
14368  * @extends Roo.Editor
14369  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14370  * as the editor field.
14371  * @constructor
14372  * @param {Object} config (used to be the tree panel.)
14373  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14374  * 
14375  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14376  * @cfg {Roo.form.TextField|Object} field The field configuration
14377  *
14378  * 
14379  */
14380 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14381     var tree = config;
14382     var field;
14383     if (oldconfig) { // old style..
14384         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14385     } else {
14386         // new style..
14387         tree = config.tree;
14388         config.field = config.field  || {};
14389         config.field.xtype = 'TextField';
14390         field = Roo.factory(config.field, Roo.form);
14391     }
14392     config = config || {};
14393     
14394     
14395     this.addEvents({
14396         /**
14397          * @event beforenodeedit
14398          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14399          * false from the handler of this event.
14400          * @param {Editor} this
14401          * @param {Roo.tree.Node} node 
14402          */
14403         "beforenodeedit" : true
14404     });
14405     
14406     //Roo.log(config);
14407     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14408
14409     this.tree = tree;
14410
14411     tree.on('beforeclick', this.beforeNodeClick, this);
14412     tree.getTreeEl().on('mousedown', this.hide, this);
14413     this.on('complete', this.updateNode, this);
14414     this.on('beforestartedit', this.fitToTree, this);
14415     this.on('startedit', this.bindScroll, this, {delay:10});
14416     this.on('specialkey', this.onSpecialKey, this);
14417 };
14418
14419 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14420     /**
14421      * @cfg {String} alignment
14422      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14423      */
14424     alignment: "l-l",
14425     // inherit
14426     autoSize: false,
14427     /**
14428      * @cfg {Boolean} hideEl
14429      * True to hide the bound element while the editor is displayed (defaults to false)
14430      */
14431     hideEl : false,
14432     /**
14433      * @cfg {String} cls
14434      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14435      */
14436     cls: "x-small-editor x-tree-editor",
14437     /**
14438      * @cfg {Boolean} shim
14439      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14440      */
14441     shim:false,
14442     // inherit
14443     shadow:"frame",
14444     /**
14445      * @cfg {Number} maxWidth
14446      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14447      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14448      * scroll and client offsets into account prior to each edit.
14449      */
14450     maxWidth: 250,
14451
14452     editDelay : 350,
14453
14454     // private
14455     fitToTree : function(ed, el){
14456         var td = this.tree.getTreeEl().dom, nd = el.dom;
14457         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14458             td.scrollLeft = nd.offsetLeft;
14459         }
14460         var w = Math.min(
14461                 this.maxWidth,
14462                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14463         this.setSize(w, '');
14464         
14465         return this.fireEvent('beforenodeedit', this, this.editNode);
14466         
14467     },
14468
14469     // private
14470     triggerEdit : function(node){
14471         this.completeEdit();
14472         this.editNode = node;
14473         this.startEdit(node.ui.textNode, node.text);
14474     },
14475
14476     // private
14477     bindScroll : function(){
14478         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14479     },
14480
14481     // private
14482     beforeNodeClick : function(node, e){
14483         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14484         this.lastClick = new Date();
14485         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14486             e.stopEvent();
14487             this.triggerEdit(node);
14488             return false;
14489         }
14490         return true;
14491     },
14492
14493     // private
14494     updateNode : function(ed, value){
14495         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14496         this.editNode.setText(value);
14497     },
14498
14499     // private
14500     onHide : function(){
14501         Roo.tree.TreeEditor.superclass.onHide.call(this);
14502         if(this.editNode){
14503             this.editNode.ui.focus();
14504         }
14505     },
14506
14507     // private
14508     onSpecialKey : function(field, e){
14509         var k = e.getKey();
14510         if(k == e.ESC){
14511             e.stopEvent();
14512             this.cancelEdit();
14513         }else if(k == e.ENTER && !e.hasModifier()){
14514             e.stopEvent();
14515             this.completeEdit();
14516         }
14517     }
14518 });//<Script type="text/javascript">
14519 /*
14520  * Based on:
14521  * Ext JS Library 1.1.1
14522  * Copyright(c) 2006-2007, Ext JS, LLC.
14523  *
14524  * Originally Released Under LGPL - original licence link has changed is not relivant.
14525  *
14526  * Fork - LGPL
14527  * <script type="text/javascript">
14528  */
14529  
14530 /**
14531  * Not documented??? - probably should be...
14532  */
14533
14534 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14535     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14536     
14537     renderElements : function(n, a, targetNode, bulkRender){
14538         //consel.log("renderElements?");
14539         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14540
14541         var t = n.getOwnerTree();
14542         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14543         
14544         var cols = t.columns;
14545         var bw = t.borderWidth;
14546         var c = cols[0];
14547         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14548          var cb = typeof a.checked == "boolean";
14549         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14550         var colcls = 'x-t-' + tid + '-c0';
14551         var buf = [
14552             '<li class="x-tree-node">',
14553             
14554                 
14555                 '<div class="x-tree-node-el ', a.cls,'">',
14556                     // extran...
14557                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14558                 
14559                 
14560                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14561                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14562                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14563                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14564                            (a.iconCls ? ' '+a.iconCls : ''),
14565                            '" unselectable="on" />',
14566                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14567                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14568                              
14569                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14570                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14571                             '<span unselectable="on" qtip="' + tx + '">',
14572                              tx,
14573                              '</span></a>' ,
14574                     '</div>',
14575                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14576                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14577                  ];
14578         for(var i = 1, len = cols.length; i < len; i++){
14579             c = cols[i];
14580             colcls = 'x-t-' + tid + '-c' +i;
14581             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14582             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14583                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14584                       "</div>");
14585          }
14586          
14587          buf.push(
14588             '</a>',
14589             '<div class="x-clear"></div></div>',
14590             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14591             "</li>");
14592         
14593         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14594             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14595                                 n.nextSibling.ui.getEl(), buf.join(""));
14596         }else{
14597             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14598         }
14599         var el = this.wrap.firstChild;
14600         this.elRow = el;
14601         this.elNode = el.firstChild;
14602         this.ranchor = el.childNodes[1];
14603         this.ctNode = this.wrap.childNodes[1];
14604         var cs = el.firstChild.childNodes;
14605         this.indentNode = cs[0];
14606         this.ecNode = cs[1];
14607         this.iconNode = cs[2];
14608         var index = 3;
14609         if(cb){
14610             this.checkbox = cs[3];
14611             index++;
14612         }
14613         this.anchor = cs[index];
14614         
14615         this.textNode = cs[index].firstChild;
14616         
14617         //el.on("click", this.onClick, this);
14618         //el.on("dblclick", this.onDblClick, this);
14619         
14620         
14621        // console.log(this);
14622     },
14623     initEvents : function(){
14624         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14625         
14626             
14627         var a = this.ranchor;
14628
14629         var el = Roo.get(a);
14630
14631         if(Roo.isOpera){ // opera render bug ignores the CSS
14632             el.setStyle("text-decoration", "none");
14633         }
14634
14635         el.on("click", this.onClick, this);
14636         el.on("dblclick", this.onDblClick, this);
14637         el.on("contextmenu", this.onContextMenu, this);
14638         
14639     },
14640     
14641     /*onSelectedChange : function(state){
14642         if(state){
14643             this.focus();
14644             this.addClass("x-tree-selected");
14645         }else{
14646             //this.blur();
14647             this.removeClass("x-tree-selected");
14648         }
14649     },*/
14650     addClass : function(cls){
14651         if(this.elRow){
14652             Roo.fly(this.elRow).addClass(cls);
14653         }
14654         
14655     },
14656     
14657     
14658     removeClass : function(cls){
14659         if(this.elRow){
14660             Roo.fly(this.elRow).removeClass(cls);
14661         }
14662     }
14663
14664     
14665     
14666 });//<Script type="text/javascript">
14667
14668 /*
14669  * Based on:
14670  * Ext JS Library 1.1.1
14671  * Copyright(c) 2006-2007, Ext JS, LLC.
14672  *
14673  * Originally Released Under LGPL - original licence link has changed is not relivant.
14674  *
14675  * Fork - LGPL
14676  * <script type="text/javascript">
14677  */
14678  
14679
14680 /**
14681  * @class Roo.tree.ColumnTree
14682  * @extends Roo.data.TreePanel
14683  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14684  * @cfg {int} borderWidth  compined right/left border allowance
14685  * @constructor
14686  * @param {String/HTMLElement/Element} el The container element
14687  * @param {Object} config
14688  */
14689 Roo.tree.ColumnTree =  function(el, config)
14690 {
14691    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14692    this.addEvents({
14693         /**
14694         * @event resize
14695         * Fire this event on a container when it resizes
14696         * @param {int} w Width
14697         * @param {int} h Height
14698         */
14699        "resize" : true
14700     });
14701     this.on('resize', this.onResize, this);
14702 };
14703
14704 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14705     //lines:false,
14706     
14707     
14708     borderWidth: Roo.isBorderBox ? 0 : 2, 
14709     headEls : false,
14710     
14711     render : function(){
14712         // add the header.....
14713        
14714         Roo.tree.ColumnTree.superclass.render.apply(this);
14715         
14716         this.el.addClass('x-column-tree');
14717         
14718         this.headers = this.el.createChild(
14719             {cls:'x-tree-headers'},this.innerCt.dom);
14720    
14721         var cols = this.columns, c;
14722         var totalWidth = 0;
14723         this.headEls = [];
14724         var  len = cols.length;
14725         for(var i = 0; i < len; i++){
14726              c = cols[i];
14727              totalWidth += c.width;
14728             this.headEls.push(this.headers.createChild({
14729                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14730                  cn: {
14731                      cls:'x-tree-hd-text',
14732                      html: c.header
14733                  },
14734                  style:'width:'+(c.width-this.borderWidth)+'px;'
14735              }));
14736         }
14737         this.headers.createChild({cls:'x-clear'});
14738         // prevent floats from wrapping when clipped
14739         this.headers.setWidth(totalWidth);
14740         //this.innerCt.setWidth(totalWidth);
14741         this.innerCt.setStyle({ overflow: 'auto' });
14742         this.onResize(this.width, this.height);
14743              
14744         
14745     },
14746     onResize : function(w,h)
14747     {
14748         this.height = h;
14749         this.width = w;
14750         // resize cols..
14751         this.innerCt.setWidth(this.width);
14752         this.innerCt.setHeight(this.height-20);
14753         
14754         // headers...
14755         var cols = this.columns, c;
14756         var totalWidth = 0;
14757         var expEl = false;
14758         var len = cols.length;
14759         for(var i = 0; i < len; i++){
14760             c = cols[i];
14761             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14762                 // it's the expander..
14763                 expEl  = this.headEls[i];
14764                 continue;
14765             }
14766             totalWidth += c.width;
14767             
14768         }
14769         if (expEl) {
14770             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14771         }
14772         this.headers.setWidth(w-20);
14773
14774         
14775         
14776         
14777     }
14778 });
14779 /*
14780  * Based on:
14781  * Ext JS Library 1.1.1
14782  * Copyright(c) 2006-2007, Ext JS, LLC.
14783  *
14784  * Originally Released Under LGPL - original licence link has changed is not relivant.
14785  *
14786  * Fork - LGPL
14787  * <script type="text/javascript">
14788  */
14789  
14790 /**
14791  * @class Roo.menu.Menu
14792  * @extends Roo.util.Observable
14793  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14794  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14795  * @constructor
14796  * Creates a new Menu
14797  * @param {Object} config Configuration options
14798  */
14799 Roo.menu.Menu = function(config){
14800     
14801     Roo.menu.Menu.superclass.constructor.call(this, config);
14802     
14803     this.id = this.id || Roo.id();
14804     this.addEvents({
14805         /**
14806          * @event beforeshow
14807          * Fires before this menu is displayed
14808          * @param {Roo.menu.Menu} this
14809          */
14810         beforeshow : true,
14811         /**
14812          * @event beforehide
14813          * Fires before this menu is hidden
14814          * @param {Roo.menu.Menu} this
14815          */
14816         beforehide : true,
14817         /**
14818          * @event show
14819          * Fires after this menu is displayed
14820          * @param {Roo.menu.Menu} this
14821          */
14822         show : true,
14823         /**
14824          * @event hide
14825          * Fires after this menu is hidden
14826          * @param {Roo.menu.Menu} this
14827          */
14828         hide : true,
14829         /**
14830          * @event click
14831          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14832          * @param {Roo.menu.Menu} this
14833          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14834          * @param {Roo.EventObject} e
14835          */
14836         click : true,
14837         /**
14838          * @event mouseover
14839          * Fires when the mouse is hovering over this menu
14840          * @param {Roo.menu.Menu} this
14841          * @param {Roo.EventObject} e
14842          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14843          */
14844         mouseover : true,
14845         /**
14846          * @event mouseout
14847          * Fires when the mouse exits this menu
14848          * @param {Roo.menu.Menu} this
14849          * @param {Roo.EventObject} e
14850          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14851          */
14852         mouseout : true,
14853         /**
14854          * @event itemclick
14855          * Fires when a menu item contained in this menu is clicked
14856          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14857          * @param {Roo.EventObject} e
14858          */
14859         itemclick: true
14860     });
14861     if (this.registerMenu) {
14862         Roo.menu.MenuMgr.register(this);
14863     }
14864     
14865     var mis = this.items;
14866     this.items = new Roo.util.MixedCollection();
14867     if(mis){
14868         this.add.apply(this, mis);
14869     }
14870 };
14871
14872 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14873     /**
14874      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14875      */
14876     minWidth : 120,
14877     /**
14878      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14879      * for bottom-right shadow (defaults to "sides")
14880      */
14881     shadow : "sides",
14882     /**
14883      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14884      * this menu (defaults to "tl-tr?")
14885      */
14886     subMenuAlign : "tl-tr?",
14887     /**
14888      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14889      * relative to its element of origin (defaults to "tl-bl?")
14890      */
14891     defaultAlign : "tl-bl?",
14892     /**
14893      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14894      */
14895     allowOtherMenus : false,
14896     /**
14897      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14898      */
14899     registerMenu : true,
14900
14901     hidden:true,
14902
14903     // private
14904     render : function(){
14905         if(this.el){
14906             return;
14907         }
14908         var el = this.el = new Roo.Layer({
14909             cls: "x-menu",
14910             shadow:this.shadow,
14911             constrain: false,
14912             parentEl: this.parentEl || document.body,
14913             zindex:15000
14914         });
14915
14916         this.keyNav = new Roo.menu.MenuNav(this);
14917
14918         if(this.plain){
14919             el.addClass("x-menu-plain");
14920         }
14921         if(this.cls){
14922             el.addClass(this.cls);
14923         }
14924         // generic focus element
14925         this.focusEl = el.createChild({
14926             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14927         });
14928         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14929         //disabling touch- as it's causing issues ..
14930         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14931         ul.on('click'   , this.onClick, this);
14932         
14933         
14934         ul.on("mouseover", this.onMouseOver, this);
14935         ul.on("mouseout", this.onMouseOut, this);
14936         this.items.each(function(item){
14937             if (item.hidden) {
14938                 return;
14939             }
14940             
14941             var li = document.createElement("li");
14942             li.className = "x-menu-list-item";
14943             ul.dom.appendChild(li);
14944             item.render(li, this);
14945         }, this);
14946         this.ul = ul;
14947         this.autoWidth();
14948     },
14949
14950     // private
14951     autoWidth : function(){
14952         var el = this.el, ul = this.ul;
14953         if(!el){
14954             return;
14955         }
14956         var w = this.width;
14957         if(w){
14958             el.setWidth(w);
14959         }else if(Roo.isIE){
14960             el.setWidth(this.minWidth);
14961             var t = el.dom.offsetWidth; // force recalc
14962             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14963         }
14964     },
14965
14966     // private
14967     delayAutoWidth : function(){
14968         if(this.rendered){
14969             if(!this.awTask){
14970                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14971             }
14972             this.awTask.delay(20);
14973         }
14974     },
14975
14976     // private
14977     findTargetItem : function(e){
14978         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14979         if(t && t.menuItemId){
14980             return this.items.get(t.menuItemId);
14981         }
14982     },
14983
14984     // private
14985     onClick : function(e){
14986         Roo.log("menu.onClick");
14987         var t = this.findTargetItem(e);
14988         if(!t){
14989             return;
14990         }
14991         Roo.log(e);
14992         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14993             if(t == this.activeItem && t.shouldDeactivate(e)){
14994                 this.activeItem.deactivate();
14995                 delete this.activeItem;
14996                 return;
14997             }
14998             if(t.canActivate){
14999                 this.setActiveItem(t, true);
15000             }
15001             return;
15002             
15003             
15004         }
15005         
15006         t.onClick(e);
15007         this.fireEvent("click", this, t, e);
15008     },
15009
15010     // private
15011     setActiveItem : function(item, autoExpand){
15012         if(item != this.activeItem){
15013             if(this.activeItem){
15014                 this.activeItem.deactivate();
15015             }
15016             this.activeItem = item;
15017             item.activate(autoExpand);
15018         }else if(autoExpand){
15019             item.expandMenu();
15020         }
15021     },
15022
15023     // private
15024     tryActivate : function(start, step){
15025         var items = this.items;
15026         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15027             var item = items.get(i);
15028             if(!item.disabled && item.canActivate){
15029                 this.setActiveItem(item, false);
15030                 return item;
15031             }
15032         }
15033         return false;
15034     },
15035
15036     // private
15037     onMouseOver : function(e){
15038         var t;
15039         if(t = this.findTargetItem(e)){
15040             if(t.canActivate && !t.disabled){
15041                 this.setActiveItem(t, true);
15042             }
15043         }
15044         this.fireEvent("mouseover", this, e, t);
15045     },
15046
15047     // private
15048     onMouseOut : function(e){
15049         var t;
15050         if(t = this.findTargetItem(e)){
15051             if(t == this.activeItem && t.shouldDeactivate(e)){
15052                 this.activeItem.deactivate();
15053                 delete this.activeItem;
15054             }
15055         }
15056         this.fireEvent("mouseout", this, e, t);
15057     },
15058
15059     /**
15060      * Read-only.  Returns true if the menu is currently displayed, else false.
15061      * @type Boolean
15062      */
15063     isVisible : function(){
15064         return this.el && !this.hidden;
15065     },
15066
15067     /**
15068      * Displays this menu relative to another element
15069      * @param {String/HTMLElement/Roo.Element} element The element to align to
15070      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15071      * the element (defaults to this.defaultAlign)
15072      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15073      */
15074     show : function(el, pos, parentMenu){
15075         this.parentMenu = parentMenu;
15076         if(!this.el){
15077             this.render();
15078         }
15079         this.fireEvent("beforeshow", this);
15080         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15081     },
15082
15083     /**
15084      * Displays this menu at a specific xy position
15085      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15086      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15087      */
15088     showAt : function(xy, parentMenu, /* private: */_e){
15089         this.parentMenu = parentMenu;
15090         if(!this.el){
15091             this.render();
15092         }
15093         if(_e !== false){
15094             this.fireEvent("beforeshow", this);
15095             xy = this.el.adjustForConstraints(xy);
15096         }
15097         this.el.setXY(xy);
15098         this.el.show();
15099         this.hidden = false;
15100         this.focus();
15101         this.fireEvent("show", this);
15102     },
15103
15104     focus : function(){
15105         if(!this.hidden){
15106             this.doFocus.defer(50, this);
15107         }
15108     },
15109
15110     doFocus : function(){
15111         if(!this.hidden){
15112             this.focusEl.focus();
15113         }
15114     },
15115
15116     /**
15117      * Hides this menu and optionally all parent menus
15118      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15119      */
15120     hide : function(deep){
15121         if(this.el && this.isVisible()){
15122             this.fireEvent("beforehide", this);
15123             if(this.activeItem){
15124                 this.activeItem.deactivate();
15125                 this.activeItem = null;
15126             }
15127             this.el.hide();
15128             this.hidden = true;
15129             this.fireEvent("hide", this);
15130         }
15131         if(deep === true && this.parentMenu){
15132             this.parentMenu.hide(true);
15133         }
15134     },
15135
15136     /**
15137      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15138      * Any of the following are valid:
15139      * <ul>
15140      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15141      * <li>An HTMLElement object which will be converted to a menu item</li>
15142      * <li>A menu item config object that will be created as a new menu item</li>
15143      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15144      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15145      * </ul>
15146      * Usage:
15147      * <pre><code>
15148 // Create the menu
15149 var menu = new Roo.menu.Menu();
15150
15151 // Create a menu item to add by reference
15152 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15153
15154 // Add a bunch of items at once using different methods.
15155 // Only the last item added will be returned.
15156 var item = menu.add(
15157     menuItem,                // add existing item by ref
15158     'Dynamic Item',          // new TextItem
15159     '-',                     // new separator
15160     { text: 'Config Item' }  // new item by config
15161 );
15162 </code></pre>
15163      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15164      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15165      */
15166     add : function(){
15167         var a = arguments, l = a.length, item;
15168         for(var i = 0; i < l; i++){
15169             var el = a[i];
15170             if ((typeof(el) == "object") && el.xtype && el.xns) {
15171                 el = Roo.factory(el, Roo.menu);
15172             }
15173             
15174             if(el.render){ // some kind of Item
15175                 item = this.addItem(el);
15176             }else if(typeof el == "string"){ // string
15177                 if(el == "separator" || el == "-"){
15178                     item = this.addSeparator();
15179                 }else{
15180                     item = this.addText(el);
15181                 }
15182             }else if(el.tagName || el.el){ // element
15183                 item = this.addElement(el);
15184             }else if(typeof el == "object"){ // must be menu item config?
15185                 item = this.addMenuItem(el);
15186             }
15187         }
15188         return item;
15189     },
15190
15191     /**
15192      * Returns this menu's underlying {@link Roo.Element} object
15193      * @return {Roo.Element} The element
15194      */
15195     getEl : function(){
15196         if(!this.el){
15197             this.render();
15198         }
15199         return this.el;
15200     },
15201
15202     /**
15203      * Adds a separator bar to the menu
15204      * @return {Roo.menu.Item} The menu item that was added
15205      */
15206     addSeparator : function(){
15207         return this.addItem(new Roo.menu.Separator());
15208     },
15209
15210     /**
15211      * Adds an {@link Roo.Element} object to the menu
15212      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15213      * @return {Roo.menu.Item} The menu item that was added
15214      */
15215     addElement : function(el){
15216         return this.addItem(new Roo.menu.BaseItem(el));
15217     },
15218
15219     /**
15220      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15221      * @param {Roo.menu.Item} item The menu item to add
15222      * @return {Roo.menu.Item} The menu item that was added
15223      */
15224     addItem : function(item){
15225         this.items.add(item);
15226         if(this.ul){
15227             var li = document.createElement("li");
15228             li.className = "x-menu-list-item";
15229             this.ul.dom.appendChild(li);
15230             item.render(li, this);
15231             this.delayAutoWidth();
15232         }
15233         return item;
15234     },
15235
15236     /**
15237      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15238      * @param {Object} config A MenuItem config object
15239      * @return {Roo.menu.Item} The menu item that was added
15240      */
15241     addMenuItem : function(config){
15242         if(!(config instanceof Roo.menu.Item)){
15243             if(typeof config.checked == "boolean"){ // must be check menu item config?
15244                 config = new Roo.menu.CheckItem(config);
15245             }else{
15246                 config = new Roo.menu.Item(config);
15247             }
15248         }
15249         return this.addItem(config);
15250     },
15251
15252     /**
15253      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15254      * @param {String} text The text to display in the menu item
15255      * @return {Roo.menu.Item} The menu item that was added
15256      */
15257     addText : function(text){
15258         return this.addItem(new Roo.menu.TextItem({ text : text }));
15259     },
15260
15261     /**
15262      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15263      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15264      * @param {Roo.menu.Item} item The menu item to add
15265      * @return {Roo.menu.Item} The menu item that was added
15266      */
15267     insert : function(index, item){
15268         this.items.insert(index, item);
15269         if(this.ul){
15270             var li = document.createElement("li");
15271             li.className = "x-menu-list-item";
15272             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15273             item.render(li, this);
15274             this.delayAutoWidth();
15275         }
15276         return item;
15277     },
15278
15279     /**
15280      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15281      * @param {Roo.menu.Item} item The menu item to remove
15282      */
15283     remove : function(item){
15284         this.items.removeKey(item.id);
15285         item.destroy();
15286     },
15287
15288     /**
15289      * Removes and destroys all items in the menu
15290      */
15291     removeAll : function(){
15292         var f;
15293         while(f = this.items.first()){
15294             this.remove(f);
15295         }
15296     }
15297 });
15298
15299 // MenuNav is a private utility class used internally by the Menu
15300 Roo.menu.MenuNav = function(menu){
15301     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15302     this.scope = this.menu = menu;
15303 };
15304
15305 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15306     doRelay : function(e, h){
15307         var k = e.getKey();
15308         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15309             this.menu.tryActivate(0, 1);
15310             return false;
15311         }
15312         return h.call(this.scope || this, e, this.menu);
15313     },
15314
15315     up : function(e, m){
15316         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15317             m.tryActivate(m.items.length-1, -1);
15318         }
15319     },
15320
15321     down : function(e, m){
15322         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15323             m.tryActivate(0, 1);
15324         }
15325     },
15326
15327     right : function(e, m){
15328         if(m.activeItem){
15329             m.activeItem.expandMenu(true);
15330         }
15331     },
15332
15333     left : function(e, m){
15334         m.hide();
15335         if(m.parentMenu && m.parentMenu.activeItem){
15336             m.parentMenu.activeItem.activate();
15337         }
15338     },
15339
15340     enter : function(e, m){
15341         if(m.activeItem){
15342             e.stopPropagation();
15343             m.activeItem.onClick(e);
15344             m.fireEvent("click", this, m.activeItem);
15345             return true;
15346         }
15347     }
15348 });/*
15349  * Based on:
15350  * Ext JS Library 1.1.1
15351  * Copyright(c) 2006-2007, Ext JS, LLC.
15352  *
15353  * Originally Released Under LGPL - original licence link has changed is not relivant.
15354  *
15355  * Fork - LGPL
15356  * <script type="text/javascript">
15357  */
15358  
15359 /**
15360  * @class Roo.menu.MenuMgr
15361  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15362  * @singleton
15363  */
15364 Roo.menu.MenuMgr = function(){
15365    var menus, active, groups = {}, attached = false, lastShow = new Date();
15366
15367    // private - called when first menu is created
15368    function init(){
15369        menus = {};
15370        active = new Roo.util.MixedCollection();
15371        Roo.get(document).addKeyListener(27, function(){
15372            if(active.length > 0){
15373                hideAll();
15374            }
15375        });
15376    }
15377
15378    // private
15379    function hideAll(){
15380        if(active && active.length > 0){
15381            var c = active.clone();
15382            c.each(function(m){
15383                m.hide();
15384            });
15385        }
15386    }
15387
15388    // private
15389    function onHide(m){
15390        active.remove(m);
15391        if(active.length < 1){
15392            Roo.get(document).un("mousedown", onMouseDown);
15393            attached = false;
15394        }
15395    }
15396
15397    // private
15398    function onShow(m){
15399        var last = active.last();
15400        lastShow = new Date();
15401        active.add(m);
15402        if(!attached){
15403            Roo.get(document).on("mousedown", onMouseDown);
15404            attached = true;
15405        }
15406        if(m.parentMenu){
15407           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15408           m.parentMenu.activeChild = m;
15409        }else if(last && last.isVisible()){
15410           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15411        }
15412    }
15413
15414    // private
15415    function onBeforeHide(m){
15416        if(m.activeChild){
15417            m.activeChild.hide();
15418        }
15419        if(m.autoHideTimer){
15420            clearTimeout(m.autoHideTimer);
15421            delete m.autoHideTimer;
15422        }
15423    }
15424
15425    // private
15426    function onBeforeShow(m){
15427        var pm = m.parentMenu;
15428        if(!pm && !m.allowOtherMenus){
15429            hideAll();
15430        }else if(pm && pm.activeChild && active != m){
15431            pm.activeChild.hide();
15432        }
15433    }
15434
15435    // private
15436    function onMouseDown(e){
15437        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15438            hideAll();
15439        }
15440    }
15441
15442    // private
15443    function onBeforeCheck(mi, state){
15444        if(state){
15445            var g = groups[mi.group];
15446            for(var i = 0, l = g.length; i < l; i++){
15447                if(g[i] != mi){
15448                    g[i].setChecked(false);
15449                }
15450            }
15451        }
15452    }
15453
15454    return {
15455
15456        /**
15457         * Hides all menus that are currently visible
15458         */
15459        hideAll : function(){
15460             hideAll();  
15461        },
15462
15463        // private
15464        register : function(menu){
15465            if(!menus){
15466                init();
15467            }
15468            menus[menu.id] = menu;
15469            menu.on("beforehide", onBeforeHide);
15470            menu.on("hide", onHide);
15471            menu.on("beforeshow", onBeforeShow);
15472            menu.on("show", onShow);
15473            var g = menu.group;
15474            if(g && menu.events["checkchange"]){
15475                if(!groups[g]){
15476                    groups[g] = [];
15477                }
15478                groups[g].push(menu);
15479                menu.on("checkchange", onCheck);
15480            }
15481        },
15482
15483         /**
15484          * Returns a {@link Roo.menu.Menu} object
15485          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15486          * be used to generate and return a new Menu instance.
15487          */
15488        get : function(menu){
15489            if(typeof menu == "string"){ // menu id
15490                return menus[menu];
15491            }else if(menu.events){  // menu instance
15492                return menu;
15493            }else if(typeof menu.length == 'number'){ // array of menu items?
15494                return new Roo.menu.Menu({items:menu});
15495            }else{ // otherwise, must be a config
15496                return new Roo.menu.Menu(menu);
15497            }
15498        },
15499
15500        // private
15501        unregister : function(menu){
15502            delete menus[menu.id];
15503            menu.un("beforehide", onBeforeHide);
15504            menu.un("hide", onHide);
15505            menu.un("beforeshow", onBeforeShow);
15506            menu.un("show", onShow);
15507            var g = menu.group;
15508            if(g && menu.events["checkchange"]){
15509                groups[g].remove(menu);
15510                menu.un("checkchange", onCheck);
15511            }
15512        },
15513
15514        // private
15515        registerCheckable : function(menuItem){
15516            var g = menuItem.group;
15517            if(g){
15518                if(!groups[g]){
15519                    groups[g] = [];
15520                }
15521                groups[g].push(menuItem);
15522                menuItem.on("beforecheckchange", onBeforeCheck);
15523            }
15524        },
15525
15526        // private
15527        unregisterCheckable : function(menuItem){
15528            var g = menuItem.group;
15529            if(g){
15530                groups[g].remove(menuItem);
15531                menuItem.un("beforecheckchange", onBeforeCheck);
15532            }
15533        }
15534    };
15535 }();/*
15536  * Based on:
15537  * Ext JS Library 1.1.1
15538  * Copyright(c) 2006-2007, Ext JS, LLC.
15539  *
15540  * Originally Released Under LGPL - original licence link has changed is not relivant.
15541  *
15542  * Fork - LGPL
15543  * <script type="text/javascript">
15544  */
15545  
15546
15547 /**
15548  * @class Roo.menu.BaseItem
15549  * @extends Roo.Component
15550  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15551  * management and base configuration options shared by all menu components.
15552  * @constructor
15553  * Creates a new BaseItem
15554  * @param {Object} config Configuration options
15555  */
15556 Roo.menu.BaseItem = function(config){
15557     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15558
15559     this.addEvents({
15560         /**
15561          * @event click
15562          * Fires when this item is clicked
15563          * @param {Roo.menu.BaseItem} this
15564          * @param {Roo.EventObject} e
15565          */
15566         click: true,
15567         /**
15568          * @event activate
15569          * Fires when this item is activated
15570          * @param {Roo.menu.BaseItem} this
15571          */
15572         activate : true,
15573         /**
15574          * @event deactivate
15575          * Fires when this item is deactivated
15576          * @param {Roo.menu.BaseItem} this
15577          */
15578         deactivate : true
15579     });
15580
15581     if(this.handler){
15582         this.on("click", this.handler, this.scope, true);
15583     }
15584 };
15585
15586 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15587     /**
15588      * @cfg {Function} handler
15589      * A function that will handle the click event of this menu item (defaults to undefined)
15590      */
15591     /**
15592      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15593      */
15594     canActivate : false,
15595     
15596      /**
15597      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15598      */
15599     hidden: false,
15600     
15601     /**
15602      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15603      */
15604     activeClass : "x-menu-item-active",
15605     /**
15606      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15607      */
15608     hideOnClick : true,
15609     /**
15610      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15611      */
15612     hideDelay : 100,
15613
15614     // private
15615     ctype: "Roo.menu.BaseItem",
15616
15617     // private
15618     actionMode : "container",
15619
15620     // private
15621     render : function(container, parentMenu){
15622         this.parentMenu = parentMenu;
15623         Roo.menu.BaseItem.superclass.render.call(this, container);
15624         this.container.menuItemId = this.id;
15625     },
15626
15627     // private
15628     onRender : function(container, position){
15629         this.el = Roo.get(this.el);
15630         container.dom.appendChild(this.el.dom);
15631     },
15632
15633     // private
15634     onClick : function(e){
15635         if(!this.disabled && this.fireEvent("click", this, e) !== false
15636                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15637             this.handleClick(e);
15638         }else{
15639             e.stopEvent();
15640         }
15641     },
15642
15643     // private
15644     activate : function(){
15645         if(this.disabled){
15646             return false;
15647         }
15648         var li = this.container;
15649         li.addClass(this.activeClass);
15650         this.region = li.getRegion().adjust(2, 2, -2, -2);
15651         this.fireEvent("activate", this);
15652         return true;
15653     },
15654
15655     // private
15656     deactivate : function(){
15657         this.container.removeClass(this.activeClass);
15658         this.fireEvent("deactivate", this);
15659     },
15660
15661     // private
15662     shouldDeactivate : function(e){
15663         return !this.region || !this.region.contains(e.getPoint());
15664     },
15665
15666     // private
15667     handleClick : function(e){
15668         if(this.hideOnClick){
15669             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15670         }
15671     },
15672
15673     // private
15674     expandMenu : function(autoActivate){
15675         // do nothing
15676     },
15677
15678     // private
15679     hideMenu : function(){
15680         // do nothing
15681     }
15682 });/*
15683  * Based on:
15684  * Ext JS Library 1.1.1
15685  * Copyright(c) 2006-2007, Ext JS, LLC.
15686  *
15687  * Originally Released Under LGPL - original licence link has changed is not relivant.
15688  *
15689  * Fork - LGPL
15690  * <script type="text/javascript">
15691  */
15692  
15693 /**
15694  * @class Roo.menu.Adapter
15695  * @extends Roo.menu.BaseItem
15696  * 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.
15697  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15698  * @constructor
15699  * Creates a new Adapter
15700  * @param {Object} config Configuration options
15701  */
15702 Roo.menu.Adapter = function(component, config){
15703     Roo.menu.Adapter.superclass.constructor.call(this, config);
15704     this.component = component;
15705 };
15706 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15707     // private
15708     canActivate : true,
15709
15710     // private
15711     onRender : function(container, position){
15712         this.component.render(container);
15713         this.el = this.component.getEl();
15714     },
15715
15716     // private
15717     activate : function(){
15718         if(this.disabled){
15719             return false;
15720         }
15721         this.component.focus();
15722         this.fireEvent("activate", this);
15723         return true;
15724     },
15725
15726     // private
15727     deactivate : function(){
15728         this.fireEvent("deactivate", this);
15729     },
15730
15731     // private
15732     disable : function(){
15733         this.component.disable();
15734         Roo.menu.Adapter.superclass.disable.call(this);
15735     },
15736
15737     // private
15738     enable : function(){
15739         this.component.enable();
15740         Roo.menu.Adapter.superclass.enable.call(this);
15741     }
15742 });/*
15743  * Based on:
15744  * Ext JS Library 1.1.1
15745  * Copyright(c) 2006-2007, Ext JS, LLC.
15746  *
15747  * Originally Released Under LGPL - original licence link has changed is not relivant.
15748  *
15749  * Fork - LGPL
15750  * <script type="text/javascript">
15751  */
15752
15753 /**
15754  * @class Roo.menu.TextItem
15755  * @extends Roo.menu.BaseItem
15756  * Adds a static text string to a menu, usually used as either a heading or group separator.
15757  * Note: old style constructor with text is still supported.
15758  * 
15759  * @constructor
15760  * Creates a new TextItem
15761  * @param {Object} cfg Configuration
15762  */
15763 Roo.menu.TextItem = function(cfg){
15764     if (typeof(cfg) == 'string') {
15765         this.text = cfg;
15766     } else {
15767         Roo.apply(this,cfg);
15768     }
15769     
15770     Roo.menu.TextItem.superclass.constructor.call(this);
15771 };
15772
15773 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15774     /**
15775      * @cfg {Boolean} text Text to show on item.
15776      */
15777     text : '',
15778     
15779     /**
15780      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15781      */
15782     hideOnClick : false,
15783     /**
15784      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15785      */
15786     itemCls : "x-menu-text",
15787
15788     // private
15789     onRender : function(){
15790         var s = document.createElement("span");
15791         s.className = this.itemCls;
15792         s.innerHTML = this.text;
15793         this.el = s;
15794         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15795     }
15796 });/*
15797  * Based on:
15798  * Ext JS Library 1.1.1
15799  * Copyright(c) 2006-2007, Ext JS, LLC.
15800  *
15801  * Originally Released Under LGPL - original licence link has changed is not relivant.
15802  *
15803  * Fork - LGPL
15804  * <script type="text/javascript">
15805  */
15806
15807 /**
15808  * @class Roo.menu.Separator
15809  * @extends Roo.menu.BaseItem
15810  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15811  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15812  * @constructor
15813  * @param {Object} config Configuration options
15814  */
15815 Roo.menu.Separator = function(config){
15816     Roo.menu.Separator.superclass.constructor.call(this, config);
15817 };
15818
15819 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15820     /**
15821      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15822      */
15823     itemCls : "x-menu-sep",
15824     /**
15825      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15826      */
15827     hideOnClick : false,
15828
15829     // private
15830     onRender : function(li){
15831         var s = document.createElement("span");
15832         s.className = this.itemCls;
15833         s.innerHTML = "&#160;";
15834         this.el = s;
15835         li.addClass("x-menu-sep-li");
15836         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15837     }
15838 });/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848 /**
15849  * @class Roo.menu.Item
15850  * @extends Roo.menu.BaseItem
15851  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15852  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15853  * activation and click handling.
15854  * @constructor
15855  * Creates a new Item
15856  * @param {Object} config Configuration options
15857  */
15858 Roo.menu.Item = function(config){
15859     Roo.menu.Item.superclass.constructor.call(this, config);
15860     if(this.menu){
15861         this.menu = Roo.menu.MenuMgr.get(this.menu);
15862     }
15863 };
15864 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15865     
15866     /**
15867      * @cfg {String} text
15868      * The text to show on the menu item.
15869      */
15870     text: '',
15871      /**
15872      * @cfg {String} HTML to render in menu
15873      * The text to show on the menu item (HTML version).
15874      */
15875     html: '',
15876     /**
15877      * @cfg {String} icon
15878      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15879      */
15880     icon: undefined,
15881     /**
15882      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15883      */
15884     itemCls : "x-menu-item",
15885     /**
15886      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15887      */
15888     canActivate : true,
15889     /**
15890      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15891      */
15892     showDelay: 200,
15893     // doc'd in BaseItem
15894     hideDelay: 200,
15895
15896     // private
15897     ctype: "Roo.menu.Item",
15898     
15899     // private
15900     onRender : function(container, position){
15901         var el = document.createElement("a");
15902         el.hideFocus = true;
15903         el.unselectable = "on";
15904         el.href = this.href || "#";
15905         if(this.hrefTarget){
15906             el.target = this.hrefTarget;
15907         }
15908         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15909         
15910         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15911         
15912         el.innerHTML = String.format(
15913                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15914                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15915         this.el = el;
15916         Roo.menu.Item.superclass.onRender.call(this, container, position);
15917     },
15918
15919     /**
15920      * Sets the text to display in this menu item
15921      * @param {String} text The text to display
15922      * @param {Boolean} isHTML true to indicate text is pure html.
15923      */
15924     setText : function(text, isHTML){
15925         if (isHTML) {
15926             this.html = text;
15927         } else {
15928             this.text = text;
15929             this.html = '';
15930         }
15931         if(this.rendered){
15932             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15933      
15934             this.el.update(String.format(
15935                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15936                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15937             this.parentMenu.autoWidth();
15938         }
15939     },
15940
15941     // private
15942     handleClick : function(e){
15943         if(!this.href){ // if no link defined, stop the event automatically
15944             e.stopEvent();
15945         }
15946         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15947     },
15948
15949     // private
15950     activate : function(autoExpand){
15951         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15952             this.focus();
15953             if(autoExpand){
15954                 this.expandMenu();
15955             }
15956         }
15957         return true;
15958     },
15959
15960     // private
15961     shouldDeactivate : function(e){
15962         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15963             if(this.menu && this.menu.isVisible()){
15964                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15965             }
15966             return true;
15967         }
15968         return false;
15969     },
15970
15971     // private
15972     deactivate : function(){
15973         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15974         this.hideMenu();
15975     },
15976
15977     // private
15978     expandMenu : function(autoActivate){
15979         if(!this.disabled && this.menu){
15980             clearTimeout(this.hideTimer);
15981             delete this.hideTimer;
15982             if(!this.menu.isVisible() && !this.showTimer){
15983                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15984             }else if (this.menu.isVisible() && autoActivate){
15985                 this.menu.tryActivate(0, 1);
15986             }
15987         }
15988     },
15989
15990     // private
15991     deferExpand : function(autoActivate){
15992         delete this.showTimer;
15993         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15994         if(autoActivate){
15995             this.menu.tryActivate(0, 1);
15996         }
15997     },
15998
15999     // private
16000     hideMenu : function(){
16001         clearTimeout(this.showTimer);
16002         delete this.showTimer;
16003         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16004             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16005         }
16006     },
16007
16008     // private
16009     deferHide : function(){
16010         delete this.hideTimer;
16011         this.menu.hide();
16012     }
16013 });/*
16014  * Based on:
16015  * Ext JS Library 1.1.1
16016  * Copyright(c) 2006-2007, Ext JS, LLC.
16017  *
16018  * Originally Released Under LGPL - original licence link has changed is not relivant.
16019  *
16020  * Fork - LGPL
16021  * <script type="text/javascript">
16022  */
16023  
16024 /**
16025  * @class Roo.menu.CheckItem
16026  * @extends Roo.menu.Item
16027  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16028  * @constructor
16029  * Creates a new CheckItem
16030  * @param {Object} config Configuration options
16031  */
16032 Roo.menu.CheckItem = function(config){
16033     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16034     this.addEvents({
16035         /**
16036          * @event beforecheckchange
16037          * Fires before the checked value is set, providing an opportunity to cancel if needed
16038          * @param {Roo.menu.CheckItem} this
16039          * @param {Boolean} checked The new checked value that will be set
16040          */
16041         "beforecheckchange" : true,
16042         /**
16043          * @event checkchange
16044          * Fires after the checked value has been set
16045          * @param {Roo.menu.CheckItem} this
16046          * @param {Boolean} checked The checked value that was set
16047          */
16048         "checkchange" : true
16049     });
16050     if(this.checkHandler){
16051         this.on('checkchange', this.checkHandler, this.scope);
16052     }
16053 };
16054 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16055     /**
16056      * @cfg {String} group
16057      * All check items with the same group name will automatically be grouped into a single-select
16058      * radio button group (defaults to '')
16059      */
16060     /**
16061      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16062      */
16063     itemCls : "x-menu-item x-menu-check-item",
16064     /**
16065      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16066      */
16067     groupClass : "x-menu-group-item",
16068
16069     /**
16070      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16071      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16072      * initialized with checked = true will be rendered as checked.
16073      */
16074     checked: false,
16075
16076     // private
16077     ctype: "Roo.menu.CheckItem",
16078
16079     // private
16080     onRender : function(c){
16081         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16082         if(this.group){
16083             this.el.addClass(this.groupClass);
16084         }
16085         Roo.menu.MenuMgr.registerCheckable(this);
16086         if(this.checked){
16087             this.checked = false;
16088             this.setChecked(true, true);
16089         }
16090     },
16091
16092     // private
16093     destroy : function(){
16094         if(this.rendered){
16095             Roo.menu.MenuMgr.unregisterCheckable(this);
16096         }
16097         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16098     },
16099
16100     /**
16101      * Set the checked state of this item
16102      * @param {Boolean} checked The new checked value
16103      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16104      */
16105     setChecked : function(state, suppressEvent){
16106         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16107             if(this.container){
16108                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16109             }
16110             this.checked = state;
16111             if(suppressEvent !== true){
16112                 this.fireEvent("checkchange", this, state);
16113             }
16114         }
16115     },
16116
16117     // private
16118     handleClick : function(e){
16119        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16120            this.setChecked(!this.checked);
16121        }
16122        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16123     }
16124 });/*
16125  * Based on:
16126  * Ext JS Library 1.1.1
16127  * Copyright(c) 2006-2007, Ext JS, LLC.
16128  *
16129  * Originally Released Under LGPL - original licence link has changed is not relivant.
16130  *
16131  * Fork - LGPL
16132  * <script type="text/javascript">
16133  */
16134  
16135 /**
16136  * @class Roo.menu.DateItem
16137  * @extends Roo.menu.Adapter
16138  * A menu item that wraps the {@link Roo.DatPicker} component.
16139  * @constructor
16140  * Creates a new DateItem
16141  * @param {Object} config Configuration options
16142  */
16143 Roo.menu.DateItem = function(config){
16144     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16145     /** The Roo.DatePicker object @type Roo.DatePicker */
16146     this.picker = this.component;
16147     this.addEvents({select: true});
16148     
16149     this.picker.on("render", function(picker){
16150         picker.getEl().swallowEvent("click");
16151         picker.container.addClass("x-menu-date-item");
16152     });
16153
16154     this.picker.on("select", this.onSelect, this);
16155 };
16156
16157 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16158     // private
16159     onSelect : function(picker, date){
16160         this.fireEvent("select", this, date, picker);
16161         Roo.menu.DateItem.superclass.handleClick.call(this);
16162     }
16163 });/*
16164  * Based on:
16165  * Ext JS Library 1.1.1
16166  * Copyright(c) 2006-2007, Ext JS, LLC.
16167  *
16168  * Originally Released Under LGPL - original licence link has changed is not relivant.
16169  *
16170  * Fork - LGPL
16171  * <script type="text/javascript">
16172  */
16173  
16174 /**
16175  * @class Roo.menu.ColorItem
16176  * @extends Roo.menu.Adapter
16177  * A menu item that wraps the {@link Roo.ColorPalette} component.
16178  * @constructor
16179  * Creates a new ColorItem
16180  * @param {Object} config Configuration options
16181  */
16182 Roo.menu.ColorItem = function(config){
16183     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16184     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16185     this.palette = this.component;
16186     this.relayEvents(this.palette, ["select"]);
16187     if(this.selectHandler){
16188         this.on('select', this.selectHandler, this.scope);
16189     }
16190 };
16191 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16192  * Based on:
16193  * Ext JS Library 1.1.1
16194  * Copyright(c) 2006-2007, Ext JS, LLC.
16195  *
16196  * Originally Released Under LGPL - original licence link has changed is not relivant.
16197  *
16198  * Fork - LGPL
16199  * <script type="text/javascript">
16200  */
16201  
16202
16203 /**
16204  * @class Roo.menu.DateMenu
16205  * @extends Roo.menu.Menu
16206  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16207  * @constructor
16208  * Creates a new DateMenu
16209  * @param {Object} config Configuration options
16210  */
16211 Roo.menu.DateMenu = function(config){
16212     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16213     this.plain = true;
16214     var di = new Roo.menu.DateItem(config);
16215     this.add(di);
16216     /**
16217      * The {@link Roo.DatePicker} instance for this DateMenu
16218      * @type DatePicker
16219      */
16220     this.picker = di.picker;
16221     /**
16222      * @event select
16223      * @param {DatePicker} picker
16224      * @param {Date} date
16225      */
16226     this.relayEvents(di, ["select"]);
16227     this.on('beforeshow', function(){
16228         if(this.picker){
16229             this.picker.hideMonthPicker(false);
16230         }
16231     }, this);
16232 };
16233 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16234     cls:'x-date-menu'
16235 });/*
16236  * Based on:
16237  * Ext JS Library 1.1.1
16238  * Copyright(c) 2006-2007, Ext JS, LLC.
16239  *
16240  * Originally Released Under LGPL - original licence link has changed is not relivant.
16241  *
16242  * Fork - LGPL
16243  * <script type="text/javascript">
16244  */
16245  
16246
16247 /**
16248  * @class Roo.menu.ColorMenu
16249  * @extends Roo.menu.Menu
16250  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16251  * @constructor
16252  * Creates a new ColorMenu
16253  * @param {Object} config Configuration options
16254  */
16255 Roo.menu.ColorMenu = function(config){
16256     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16257     this.plain = true;
16258     var ci = new Roo.menu.ColorItem(config);
16259     this.add(ci);
16260     /**
16261      * The {@link Roo.ColorPalette} instance for this ColorMenu
16262      * @type ColorPalette
16263      */
16264     this.palette = ci.palette;
16265     /**
16266      * @event select
16267      * @param {ColorPalette} palette
16268      * @param {String} color
16269      */
16270     this.relayEvents(ci, ["select"]);
16271 };
16272 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.TextItem
16285  * @extends Roo.BoxComponent
16286  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16287  * @constructor
16288  * Creates a new TextItem
16289  * @param {Object} config Configuration options
16290  */
16291 Roo.form.TextItem = function(config){
16292     Roo.form.TextItem.superclass.constructor.call(this, config);
16293 };
16294
16295 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16296     
16297     /**
16298      * @cfg {String} tag the tag for this item (default div)
16299      */
16300     tag : 'div',
16301     /**
16302      * @cfg {String} html the content for this item
16303      */
16304     html : '',
16305     
16306     getAutoCreate : function()
16307     {
16308         var cfg = {
16309             id: this.id,
16310             tag: this.tag,
16311             html: this.html,
16312             cls: 'x-form-item'
16313         };
16314         
16315         return cfg;
16316         
16317     },
16318     
16319     onRender : function(ct, position)
16320     {
16321         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16322         
16323         if(!this.el){
16324             var cfg = this.getAutoCreate();
16325             if(!cfg.name){
16326                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16327             }
16328             if (!cfg.name.length) {
16329                 delete cfg.name;
16330             }
16331             this.el = ct.createChild(cfg, position);
16332         }
16333     }
16334     
16335 });/*
16336  * Based on:
16337  * Ext JS Library 1.1.1
16338  * Copyright(c) 2006-2007, Ext JS, LLC.
16339  *
16340  * Originally Released Under LGPL - original licence link has changed is not relivant.
16341  *
16342  * Fork - LGPL
16343  * <script type="text/javascript">
16344  */
16345  
16346 /**
16347  * @class Roo.form.Field
16348  * @extends Roo.BoxComponent
16349  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16350  * @constructor
16351  * Creates a new Field
16352  * @param {Object} config Configuration options
16353  */
16354 Roo.form.Field = function(config){
16355     Roo.form.Field.superclass.constructor.call(this, config);
16356 };
16357
16358 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16359     /**
16360      * @cfg {String} fieldLabel Label to use when rendering a form.
16361      */
16362        /**
16363      * @cfg {String} qtip Mouse over tip
16364      */
16365      
16366     /**
16367      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16368      */
16369     invalidClass : "x-form-invalid",
16370     /**
16371      * @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")
16372      */
16373     invalidText : "The value in this field is invalid",
16374     /**
16375      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16376      */
16377     focusClass : "x-form-focus",
16378     /**
16379      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16380       automatic validation (defaults to "keyup").
16381      */
16382     validationEvent : "keyup",
16383     /**
16384      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16385      */
16386     validateOnBlur : true,
16387     /**
16388      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16389      */
16390     validationDelay : 250,
16391     /**
16392      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16393      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16394      */
16395     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16396     /**
16397      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16398      */
16399     fieldClass : "x-form-field",
16400     /**
16401      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16402      *<pre>
16403 Value         Description
16404 -----------   ----------------------------------------------------------------------
16405 qtip          Display a quick tip when the user hovers over the field
16406 title         Display a default browser title attribute popup
16407 under         Add a block div beneath the field containing the error text
16408 side          Add an error icon to the right of the field with a popup on hover
16409 [element id]  Add the error text directly to the innerHTML of the specified element
16410 </pre>
16411      */
16412     msgTarget : 'qtip',
16413     /**
16414      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16415      */
16416     msgFx : 'normal',
16417
16418     /**
16419      * @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.
16420      */
16421     readOnly : false,
16422
16423     /**
16424      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16425      */
16426     disabled : false,
16427
16428     /**
16429      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16430      */
16431     inputType : undefined,
16432     
16433     /**
16434      * @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).
16435          */
16436         tabIndex : undefined,
16437         
16438     // private
16439     isFormField : true,
16440
16441     // private
16442     hasFocus : false,
16443     /**
16444      * @property {Roo.Element} fieldEl
16445      * Element Containing the rendered Field (with label etc.)
16446      */
16447     /**
16448      * @cfg {Mixed} value A value to initialize this field with.
16449      */
16450     value : undefined,
16451
16452     /**
16453      * @cfg {String} name The field's HTML name attribute.
16454      */
16455     /**
16456      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16457      */
16458     // private
16459     loadedValue : false,
16460      
16461      
16462         // private ??
16463         initComponent : function(){
16464         Roo.form.Field.superclass.initComponent.call(this);
16465         this.addEvents({
16466             /**
16467              * @event focus
16468              * Fires when this field receives input focus.
16469              * @param {Roo.form.Field} this
16470              */
16471             focus : true,
16472             /**
16473              * @event blur
16474              * Fires when this field loses input focus.
16475              * @param {Roo.form.Field} this
16476              */
16477             blur : true,
16478             /**
16479              * @event specialkey
16480              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16481              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16482              * @param {Roo.form.Field} this
16483              * @param {Roo.EventObject} e The event object
16484              */
16485             specialkey : true,
16486             /**
16487              * @event change
16488              * Fires just before the field blurs if the field value has changed.
16489              * @param {Roo.form.Field} this
16490              * @param {Mixed} newValue The new value
16491              * @param {Mixed} oldValue The original value
16492              */
16493             change : true,
16494             /**
16495              * @event invalid
16496              * Fires after the field has been marked as invalid.
16497              * @param {Roo.form.Field} this
16498              * @param {String} msg The validation message
16499              */
16500             invalid : true,
16501             /**
16502              * @event valid
16503              * Fires after the field has been validated with no errors.
16504              * @param {Roo.form.Field} this
16505              */
16506             valid : true,
16507              /**
16508              * @event keyup
16509              * Fires after the key up
16510              * @param {Roo.form.Field} this
16511              * @param {Roo.EventObject}  e The event Object
16512              */
16513             keyup : true
16514         });
16515     },
16516
16517     /**
16518      * Returns the name attribute of the field if available
16519      * @return {String} name The field name
16520      */
16521     getName: function(){
16522          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16523     },
16524
16525     // private
16526     onRender : function(ct, position){
16527         Roo.form.Field.superclass.onRender.call(this, ct, position);
16528         if(!this.el){
16529             var cfg = this.getAutoCreate();
16530             if(!cfg.name){
16531                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16532             }
16533             if (!cfg.name.length) {
16534                 delete cfg.name;
16535             }
16536             if(this.inputType){
16537                 cfg.type = this.inputType;
16538             }
16539             this.el = ct.createChild(cfg, position);
16540         }
16541         var type = this.el.dom.type;
16542         if(type){
16543             if(type == 'password'){
16544                 type = 'text';
16545             }
16546             this.el.addClass('x-form-'+type);
16547         }
16548         if(this.readOnly){
16549             this.el.dom.readOnly = true;
16550         }
16551         if(this.tabIndex !== undefined){
16552             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16553         }
16554
16555         this.el.addClass([this.fieldClass, this.cls]);
16556         this.initValue();
16557     },
16558
16559     /**
16560      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16561      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16562      * @return {Roo.form.Field} this
16563      */
16564     applyTo : function(target){
16565         this.allowDomMove = false;
16566         this.el = Roo.get(target);
16567         this.render(this.el.dom.parentNode);
16568         return this;
16569     },
16570
16571     // private
16572     initValue : function(){
16573         if(this.value !== undefined){
16574             this.setValue(this.value);
16575         }else if(this.el.dom.value.length > 0){
16576             this.setValue(this.el.dom.value);
16577         }
16578     },
16579
16580     /**
16581      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16582      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16583      */
16584     isDirty : function() {
16585         if(this.disabled) {
16586             return false;
16587         }
16588         return String(this.getValue()) !== String(this.originalValue);
16589     },
16590
16591     /**
16592      * stores the current value in loadedValue
16593      */
16594     resetHasChanged : function()
16595     {
16596         this.loadedValue = String(this.getValue());
16597     },
16598     /**
16599      * checks the current value against the 'loaded' value.
16600      * Note - will return false if 'resetHasChanged' has not been called first.
16601      */
16602     hasChanged : function()
16603     {
16604         if(this.disabled || this.readOnly) {
16605             return false;
16606         }
16607         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16608     },
16609     
16610     
16611     
16612     // private
16613     afterRender : function(){
16614         Roo.form.Field.superclass.afterRender.call(this);
16615         this.initEvents();
16616     },
16617
16618     // private
16619     fireKey : function(e){
16620         //Roo.log('field ' + e.getKey());
16621         if(e.isNavKeyPress()){
16622             this.fireEvent("specialkey", this, e);
16623         }
16624     },
16625
16626     /**
16627      * Resets the current field value to the originally loaded value and clears any validation messages
16628      */
16629     reset : function(){
16630         this.setValue(this.resetValue);
16631         this.originalValue = this.getValue();
16632         this.clearInvalid();
16633     },
16634
16635     // private
16636     initEvents : function(){
16637         // safari killled keypress - so keydown is now used..
16638         this.el.on("keydown" , this.fireKey,  this);
16639         this.el.on("focus", this.onFocus,  this);
16640         this.el.on("blur", this.onBlur,  this);
16641         this.el.relayEvent('keyup', this);
16642
16643         // reference to original value for reset
16644         this.originalValue = this.getValue();
16645         this.resetValue =  this.getValue();
16646     },
16647
16648     // private
16649     onFocus : function(){
16650         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16651             this.el.addClass(this.focusClass);
16652         }
16653         if(!this.hasFocus){
16654             this.hasFocus = true;
16655             this.startValue = this.getValue();
16656             this.fireEvent("focus", this);
16657         }
16658     },
16659
16660     beforeBlur : Roo.emptyFn,
16661
16662     // private
16663     onBlur : function(){
16664         this.beforeBlur();
16665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16666             this.el.removeClass(this.focusClass);
16667         }
16668         this.hasFocus = false;
16669         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16670             this.validate();
16671         }
16672         var v = this.getValue();
16673         if(String(v) !== String(this.startValue)){
16674             this.fireEvent('change', this, v, this.startValue);
16675         }
16676         this.fireEvent("blur", this);
16677     },
16678
16679     /**
16680      * Returns whether or not the field value is currently valid
16681      * @param {Boolean} preventMark True to disable marking the field invalid
16682      * @return {Boolean} True if the value is valid, else false
16683      */
16684     isValid : function(preventMark){
16685         if(this.disabled){
16686             return true;
16687         }
16688         var restore = this.preventMark;
16689         this.preventMark = preventMark === true;
16690         var v = this.validateValue(this.processValue(this.getRawValue()));
16691         this.preventMark = restore;
16692         return v;
16693     },
16694
16695     /**
16696      * Validates the field value
16697      * @return {Boolean} True if the value is valid, else false
16698      */
16699     validate : function(){
16700         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16701             this.clearInvalid();
16702             return true;
16703         }
16704         return false;
16705     },
16706
16707     processValue : function(value){
16708         return value;
16709     },
16710
16711     // private
16712     // Subclasses should provide the validation implementation by overriding this
16713     validateValue : function(value){
16714         return true;
16715     },
16716
16717     /**
16718      * Mark this field as invalid
16719      * @param {String} msg The validation message
16720      */
16721     markInvalid : function(msg){
16722         if(!this.rendered || this.preventMark){ // not rendered
16723             return;
16724         }
16725         
16726         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16727         
16728         obj.el.addClass(this.invalidClass);
16729         msg = msg || this.invalidText;
16730         switch(this.msgTarget){
16731             case 'qtip':
16732                 obj.el.dom.qtip = msg;
16733                 obj.el.dom.qclass = 'x-form-invalid-tip';
16734                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16735                     Roo.QuickTips.enable();
16736                 }
16737                 break;
16738             case 'title':
16739                 this.el.dom.title = msg;
16740                 break;
16741             case 'under':
16742                 if(!this.errorEl){
16743                     var elp = this.el.findParent('.x-form-element', 5, true);
16744                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16745                     this.errorEl.setWidth(elp.getWidth(true)-20);
16746                 }
16747                 this.errorEl.update(msg);
16748                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16749                 break;
16750             case 'side':
16751                 if(!this.errorIcon){
16752                     var elp = this.el.findParent('.x-form-element', 5, true);
16753                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16754                 }
16755                 this.alignErrorIcon();
16756                 this.errorIcon.dom.qtip = msg;
16757                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16758                 this.errorIcon.show();
16759                 this.on('resize', this.alignErrorIcon, this);
16760                 break;
16761             default:
16762                 var t = Roo.getDom(this.msgTarget);
16763                 t.innerHTML = msg;
16764                 t.style.display = this.msgDisplay;
16765                 break;
16766         }
16767         this.fireEvent('invalid', this, msg);
16768     },
16769
16770     // private
16771     alignErrorIcon : function(){
16772         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16773     },
16774
16775     /**
16776      * Clear any invalid styles/messages for this field
16777      */
16778     clearInvalid : function(){
16779         if(!this.rendered || this.preventMark){ // not rendered
16780             return;
16781         }
16782         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16783         
16784         obj.el.removeClass(this.invalidClass);
16785         switch(this.msgTarget){
16786             case 'qtip':
16787                 obj.el.dom.qtip = '';
16788                 break;
16789             case 'title':
16790                 this.el.dom.title = '';
16791                 break;
16792             case 'under':
16793                 if(this.errorEl){
16794                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16795                 }
16796                 break;
16797             case 'side':
16798                 if(this.errorIcon){
16799                     this.errorIcon.dom.qtip = '';
16800                     this.errorIcon.hide();
16801                     this.un('resize', this.alignErrorIcon, this);
16802                 }
16803                 break;
16804             default:
16805                 var t = Roo.getDom(this.msgTarget);
16806                 t.innerHTML = '';
16807                 t.style.display = 'none';
16808                 break;
16809         }
16810         this.fireEvent('valid', this);
16811     },
16812
16813     /**
16814      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16815      * @return {Mixed} value The field value
16816      */
16817     getRawValue : function(){
16818         var v = this.el.getValue();
16819         
16820         return v;
16821     },
16822
16823     /**
16824      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16825      * @return {Mixed} value The field value
16826      */
16827     getValue : function(){
16828         var v = this.el.getValue();
16829          
16830         return v;
16831     },
16832
16833     /**
16834      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16835      * @param {Mixed} value The value to set
16836      */
16837     setRawValue : function(v){
16838         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16839     },
16840
16841     /**
16842      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16843      * @param {Mixed} value The value to set
16844      */
16845     setValue : function(v){
16846         this.value = v;
16847         if(this.rendered){
16848             this.el.dom.value = (v === null || v === undefined ? '' : v);
16849              this.validate();
16850         }
16851     },
16852
16853     adjustSize : function(w, h){
16854         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16855         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16856         return s;
16857     },
16858
16859     adjustWidth : function(tag, w){
16860         tag = tag.toLowerCase();
16861         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16862             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16863                 if(tag == 'input'){
16864                     return w + 2;
16865                 }
16866                 if(tag == 'textarea'){
16867                     return w-2;
16868                 }
16869             }else if(Roo.isOpera){
16870                 if(tag == 'input'){
16871                     return w + 2;
16872                 }
16873                 if(tag == 'textarea'){
16874                     return w-2;
16875                 }
16876             }
16877         }
16878         return w;
16879     }
16880 });
16881
16882
16883 // anything other than normal should be considered experimental
16884 Roo.form.Field.msgFx = {
16885     normal : {
16886         show: function(msgEl, f){
16887             msgEl.setDisplayed('block');
16888         },
16889
16890         hide : function(msgEl, f){
16891             msgEl.setDisplayed(false).update('');
16892         }
16893     },
16894
16895     slide : {
16896         show: function(msgEl, f){
16897             msgEl.slideIn('t', {stopFx:true});
16898         },
16899
16900         hide : function(msgEl, f){
16901             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16902         }
16903     },
16904
16905     slideRight : {
16906         show: function(msgEl, f){
16907             msgEl.fixDisplay();
16908             msgEl.alignTo(f.el, 'tl-tr');
16909             msgEl.slideIn('l', {stopFx:true});
16910         },
16911
16912         hide : function(msgEl, f){
16913             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16914         }
16915     }
16916 };/*
16917  * Based on:
16918  * Ext JS Library 1.1.1
16919  * Copyright(c) 2006-2007, Ext JS, LLC.
16920  *
16921  * Originally Released Under LGPL - original licence link has changed is not relivant.
16922  *
16923  * Fork - LGPL
16924  * <script type="text/javascript">
16925  */
16926  
16927
16928 /**
16929  * @class Roo.form.TextField
16930  * @extends Roo.form.Field
16931  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16932  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16933  * @constructor
16934  * Creates a new TextField
16935  * @param {Object} config Configuration options
16936  */
16937 Roo.form.TextField = function(config){
16938     Roo.form.TextField.superclass.constructor.call(this, config);
16939     this.addEvents({
16940         /**
16941          * @event autosize
16942          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16943          * according to the default logic, but this event provides a hook for the developer to apply additional
16944          * logic at runtime to resize the field if needed.
16945              * @param {Roo.form.Field} this This text field
16946              * @param {Number} width The new field width
16947              */
16948         autosize : true
16949     });
16950 };
16951
16952 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16953     /**
16954      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16955      */
16956     grow : false,
16957     /**
16958      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16959      */
16960     growMin : 30,
16961     /**
16962      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16963      */
16964     growMax : 800,
16965     /**
16966      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16967      */
16968     vtype : null,
16969     /**
16970      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16971      */
16972     maskRe : null,
16973     /**
16974      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16975      */
16976     disableKeyFilter : false,
16977     /**
16978      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16979      */
16980     allowBlank : true,
16981     /**
16982      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16983      */
16984     minLength : 0,
16985     /**
16986      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16987      */
16988     maxLength : Number.MAX_VALUE,
16989     /**
16990      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16991      */
16992     minLengthText : "The minimum length for this field is {0}",
16993     /**
16994      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16995      */
16996     maxLengthText : "The maximum length for this field is {0}",
16997     /**
16998      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16999      */
17000     selectOnFocus : false,
17001     /**
17002      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17003      */    
17004     allowLeadingSpace : false,
17005     /**
17006      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17007      */
17008     blankText : "This field is required",
17009     /**
17010      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17011      * If available, this function will be called only after the basic validators all return true, and will be passed the
17012      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17013      */
17014     validator : null,
17015     /**
17016      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17017      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17018      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17019      */
17020     regex : null,
17021     /**
17022      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17023      */
17024     regexText : "",
17025     /**
17026      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17027      */
17028     emptyText : null,
17029    
17030
17031     // private
17032     initEvents : function()
17033     {
17034         if (this.emptyText) {
17035             this.el.attr('placeholder', this.emptyText);
17036         }
17037         
17038         Roo.form.TextField.superclass.initEvents.call(this);
17039         if(this.validationEvent == 'keyup'){
17040             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17041             this.el.on('keyup', this.filterValidation, this);
17042         }
17043         else if(this.validationEvent !== false){
17044             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17045         }
17046         
17047         if(this.selectOnFocus){
17048             this.on("focus", this.preFocus, this);
17049         }
17050         if (!this.allowLeadingSpace) {
17051             this.on('blur', this.cleanLeadingSpace, this);
17052         }
17053         
17054         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17055             this.el.on("keypress", this.filterKeys, this);
17056         }
17057         if(this.grow){
17058             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17059             this.el.on("click", this.autoSize,  this);
17060         }
17061         if(this.el.is('input[type=password]') && Roo.isSafari){
17062             this.el.on('keydown', this.SafariOnKeyDown, this);
17063         }
17064     },
17065
17066     processValue : function(value){
17067         if(this.stripCharsRe){
17068             var newValue = value.replace(this.stripCharsRe, '');
17069             if(newValue !== value){
17070                 this.setRawValue(newValue);
17071                 return newValue;
17072             }
17073         }
17074         return value;
17075     },
17076
17077     filterValidation : function(e){
17078         if(!e.isNavKeyPress()){
17079             this.validationTask.delay(this.validationDelay);
17080         }
17081     },
17082
17083     // private
17084     onKeyUp : function(e){
17085         if(!e.isNavKeyPress()){
17086             this.autoSize();
17087         }
17088     },
17089     // private - clean the leading white space
17090     cleanLeadingSpace : function(e)
17091     {
17092         if ( this.inputType == 'file') {
17093             return;
17094         }
17095         
17096         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17097     },
17098     /**
17099      * Resets the current field value to the originally-loaded value and clears any validation messages.
17100      *  
17101      */
17102     reset : function(){
17103         Roo.form.TextField.superclass.reset.call(this);
17104        
17105     }, 
17106     // private
17107     preFocus : function(){
17108         
17109         if(this.selectOnFocus){
17110             this.el.dom.select();
17111         }
17112     },
17113
17114     
17115     // private
17116     filterKeys : function(e){
17117         var k = e.getKey();
17118         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17119             return;
17120         }
17121         var c = e.getCharCode(), cc = String.fromCharCode(c);
17122         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17123             return;
17124         }
17125         if(!this.maskRe.test(cc)){
17126             e.stopEvent();
17127         }
17128     },
17129
17130     setValue : function(v){
17131         
17132         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17133         
17134         this.autoSize();
17135     },
17136
17137     /**
17138      * Validates a value according to the field's validation rules and marks the field as invalid
17139      * if the validation fails
17140      * @param {Mixed} value The value to validate
17141      * @return {Boolean} True if the value is valid, else false
17142      */
17143     validateValue : function(value){
17144         if(value.length < 1)  { // if it's blank
17145              if(this.allowBlank){
17146                 this.clearInvalid();
17147                 return true;
17148              }else{
17149                 this.markInvalid(this.blankText);
17150                 return false;
17151              }
17152         }
17153         if(value.length < this.minLength){
17154             this.markInvalid(String.format(this.minLengthText, this.minLength));
17155             return false;
17156         }
17157         if(value.length > this.maxLength){
17158             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17159             return false;
17160         }
17161         if(this.vtype){
17162             var vt = Roo.form.VTypes;
17163             if(!vt[this.vtype](value, this)){
17164                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17165                 return false;
17166             }
17167         }
17168         if(typeof this.validator == "function"){
17169             var msg = this.validator(value);
17170             if(msg !== true){
17171                 this.markInvalid(msg);
17172                 return false;
17173             }
17174         }
17175         if(this.regex && !this.regex.test(value)){
17176             this.markInvalid(this.regexText);
17177             return false;
17178         }
17179         return true;
17180     },
17181
17182     /**
17183      * Selects text in this field
17184      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17185      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17186      */
17187     selectText : function(start, end){
17188         var v = this.getRawValue();
17189         if(v.length > 0){
17190             start = start === undefined ? 0 : start;
17191             end = end === undefined ? v.length : end;
17192             var d = this.el.dom;
17193             if(d.setSelectionRange){
17194                 d.setSelectionRange(start, end);
17195             }else if(d.createTextRange){
17196                 var range = d.createTextRange();
17197                 range.moveStart("character", start);
17198                 range.moveEnd("character", v.length-end);
17199                 range.select();
17200             }
17201         }
17202     },
17203
17204     /**
17205      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17206      * This only takes effect if grow = true, and fires the autosize event.
17207      */
17208     autoSize : function(){
17209         if(!this.grow || !this.rendered){
17210             return;
17211         }
17212         if(!this.metrics){
17213             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17214         }
17215         var el = this.el;
17216         var v = el.dom.value;
17217         var d = document.createElement('div');
17218         d.appendChild(document.createTextNode(v));
17219         v = d.innerHTML;
17220         d = null;
17221         v += "&#160;";
17222         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17223         this.el.setWidth(w);
17224         this.fireEvent("autosize", this, w);
17225     },
17226     
17227     // private
17228     SafariOnKeyDown : function(event)
17229     {
17230         // this is a workaround for a password hang bug on chrome/ webkit.
17231         
17232         var isSelectAll = false;
17233         
17234         if(this.el.dom.selectionEnd > 0){
17235             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17236         }
17237         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17238             event.preventDefault();
17239             this.setValue('');
17240             return;
17241         }
17242         
17243         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17244             
17245             event.preventDefault();
17246             // this is very hacky as keydown always get's upper case.
17247             
17248             var cc = String.fromCharCode(event.getCharCode());
17249             
17250             
17251             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17252             
17253         }
17254         
17255         
17256     }
17257 });/*
17258  * Based on:
17259  * Ext JS Library 1.1.1
17260  * Copyright(c) 2006-2007, Ext JS, LLC.
17261  *
17262  * Originally Released Under LGPL - original licence link has changed is not relivant.
17263  *
17264  * Fork - LGPL
17265  * <script type="text/javascript">
17266  */
17267  
17268 /**
17269  * @class Roo.form.Hidden
17270  * @extends Roo.form.TextField
17271  * Simple Hidden element used on forms 
17272  * 
17273  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17274  * 
17275  * @constructor
17276  * Creates a new Hidden form element.
17277  * @param {Object} config Configuration options
17278  */
17279
17280
17281
17282 // easy hidden field...
17283 Roo.form.Hidden = function(config){
17284     Roo.form.Hidden.superclass.constructor.call(this, config);
17285 };
17286   
17287 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17288     fieldLabel:      '',
17289     inputType:      'hidden',
17290     width:          50,
17291     allowBlank:     true,
17292     labelSeparator: '',
17293     hidden:         true,
17294     itemCls :       'x-form-item-display-none'
17295
17296
17297 });
17298
17299
17300 /*
17301  * Based on:
17302  * Ext JS Library 1.1.1
17303  * Copyright(c) 2006-2007, Ext JS, LLC.
17304  *
17305  * Originally Released Under LGPL - original licence link has changed is not relivant.
17306  *
17307  * Fork - LGPL
17308  * <script type="text/javascript">
17309  */
17310  
17311 /**
17312  * @class Roo.form.TriggerField
17313  * @extends Roo.form.TextField
17314  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17315  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17316  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17317  * for which you can provide a custom implementation.  For example:
17318  * <pre><code>
17319 var trigger = new Roo.form.TriggerField();
17320 trigger.onTriggerClick = myTriggerFn;
17321 trigger.applyTo('my-field');
17322 </code></pre>
17323  *
17324  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17325  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17326  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17327  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17328  * @constructor
17329  * Create a new TriggerField.
17330  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17331  * to the base TextField)
17332  */
17333 Roo.form.TriggerField = function(config){
17334     this.mimicing = false;
17335     Roo.form.TriggerField.superclass.constructor.call(this, config);
17336 };
17337
17338 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17339     /**
17340      * @cfg {String} triggerClass A CSS class to apply to the trigger
17341      */
17342     /**
17343      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17344      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17345      */
17346     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17347     /**
17348      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17349      */
17350     hideTrigger:false,
17351
17352     /** @cfg {Boolean} grow @hide */
17353     /** @cfg {Number} growMin @hide */
17354     /** @cfg {Number} growMax @hide */
17355
17356     /**
17357      * @hide 
17358      * @method
17359      */
17360     autoSize: Roo.emptyFn,
17361     // private
17362     monitorTab : true,
17363     // private
17364     deferHeight : true,
17365
17366     
17367     actionMode : 'wrap',
17368     // private
17369     onResize : function(w, h){
17370         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17371         if(typeof w == 'number'){
17372             var x = w - this.trigger.getWidth();
17373             this.el.setWidth(this.adjustWidth('input', x));
17374             this.trigger.setStyle('left', x+'px');
17375         }
17376     },
17377
17378     // private
17379     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17380
17381     // private
17382     getResizeEl : function(){
17383         return this.wrap;
17384     },
17385
17386     // private
17387     getPositionEl : function(){
17388         return this.wrap;
17389     },
17390
17391     // private
17392     alignErrorIcon : function(){
17393         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17394     },
17395
17396     // private
17397     onRender : function(ct, position){
17398         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17399         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17400         this.trigger = this.wrap.createChild(this.triggerConfig ||
17401                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17402         if(this.hideTrigger){
17403             this.trigger.setDisplayed(false);
17404         }
17405         this.initTrigger();
17406         if(!this.width){
17407             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17408         }
17409     },
17410
17411     // private
17412     initTrigger : function(){
17413         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17414         this.trigger.addClassOnOver('x-form-trigger-over');
17415         this.trigger.addClassOnClick('x-form-trigger-click');
17416     },
17417
17418     // private
17419     onDestroy : function(){
17420         if(this.trigger){
17421             this.trigger.removeAllListeners();
17422             this.trigger.remove();
17423         }
17424         if(this.wrap){
17425             this.wrap.remove();
17426         }
17427         Roo.form.TriggerField.superclass.onDestroy.call(this);
17428     },
17429
17430     // private
17431     onFocus : function(){
17432         Roo.form.TriggerField.superclass.onFocus.call(this);
17433         if(!this.mimicing){
17434             this.wrap.addClass('x-trigger-wrap-focus');
17435             this.mimicing = true;
17436             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17437             if(this.monitorTab){
17438                 this.el.on("keydown", this.checkTab, this);
17439             }
17440         }
17441     },
17442
17443     // private
17444     checkTab : function(e){
17445         if(e.getKey() == e.TAB){
17446             this.triggerBlur();
17447         }
17448     },
17449
17450     // private
17451     onBlur : function(){
17452         // do nothing
17453     },
17454
17455     // private
17456     mimicBlur : function(e, t){
17457         if(!this.wrap.contains(t) && this.validateBlur()){
17458             this.triggerBlur();
17459         }
17460     },
17461
17462     // private
17463     triggerBlur : function(){
17464         this.mimicing = false;
17465         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17466         if(this.monitorTab){
17467             this.el.un("keydown", this.checkTab, this);
17468         }
17469         this.wrap.removeClass('x-trigger-wrap-focus');
17470         Roo.form.TriggerField.superclass.onBlur.call(this);
17471     },
17472
17473     // private
17474     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17475     validateBlur : function(e, t){
17476         return true;
17477     },
17478
17479     // private
17480     onDisable : function(){
17481         Roo.form.TriggerField.superclass.onDisable.call(this);
17482         if(this.wrap){
17483             this.wrap.addClass('x-item-disabled');
17484         }
17485     },
17486
17487     // private
17488     onEnable : function(){
17489         Roo.form.TriggerField.superclass.onEnable.call(this);
17490         if(this.wrap){
17491             this.wrap.removeClass('x-item-disabled');
17492         }
17493     },
17494
17495     // private
17496     onShow : function(){
17497         var ae = this.getActionEl();
17498         
17499         if(ae){
17500             ae.dom.style.display = '';
17501             ae.dom.style.visibility = 'visible';
17502         }
17503     },
17504
17505     // private
17506     
17507     onHide : function(){
17508         var ae = this.getActionEl();
17509         ae.dom.style.display = 'none';
17510     },
17511
17512     /**
17513      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17514      * by an implementing function.
17515      * @method
17516      * @param {EventObject} e
17517      */
17518     onTriggerClick : Roo.emptyFn
17519 });
17520
17521 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17522 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17523 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17524 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17525     initComponent : function(){
17526         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17527
17528         this.triggerConfig = {
17529             tag:'span', cls:'x-form-twin-triggers', cn:[
17530             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17531             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17532         ]};
17533     },
17534
17535     getTrigger : function(index){
17536         return this.triggers[index];
17537     },
17538
17539     initTrigger : function(){
17540         var ts = this.trigger.select('.x-form-trigger', true);
17541         this.wrap.setStyle('overflow', 'hidden');
17542         var triggerField = this;
17543         ts.each(function(t, all, index){
17544             t.hide = function(){
17545                 var w = triggerField.wrap.getWidth();
17546                 this.dom.style.display = 'none';
17547                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17548             };
17549             t.show = function(){
17550                 var w = triggerField.wrap.getWidth();
17551                 this.dom.style.display = '';
17552                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17553             };
17554             var triggerIndex = 'Trigger'+(index+1);
17555
17556             if(this['hide'+triggerIndex]){
17557                 t.dom.style.display = 'none';
17558             }
17559             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17560             t.addClassOnOver('x-form-trigger-over');
17561             t.addClassOnClick('x-form-trigger-click');
17562         }, this);
17563         this.triggers = ts.elements;
17564     },
17565
17566     onTrigger1Click : Roo.emptyFn,
17567     onTrigger2Click : Roo.emptyFn
17568 });/*
17569  * Based on:
17570  * Ext JS Library 1.1.1
17571  * Copyright(c) 2006-2007, Ext JS, LLC.
17572  *
17573  * Originally Released Under LGPL - original licence link has changed is not relivant.
17574  *
17575  * Fork - LGPL
17576  * <script type="text/javascript">
17577  */
17578  
17579 /**
17580  * @class Roo.form.TextArea
17581  * @extends Roo.form.TextField
17582  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17583  * support for auto-sizing.
17584  * @constructor
17585  * Creates a new TextArea
17586  * @param {Object} config Configuration options
17587  */
17588 Roo.form.TextArea = function(config){
17589     Roo.form.TextArea.superclass.constructor.call(this, config);
17590     // these are provided exchanges for backwards compat
17591     // minHeight/maxHeight were replaced by growMin/growMax to be
17592     // compatible with TextField growing config values
17593     if(this.minHeight !== undefined){
17594         this.growMin = this.minHeight;
17595     }
17596     if(this.maxHeight !== undefined){
17597         this.growMax = this.maxHeight;
17598     }
17599 };
17600
17601 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17602     /**
17603      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17604      */
17605     growMin : 60,
17606     /**
17607      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17608      */
17609     growMax: 1000,
17610     /**
17611      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17612      * in the field (equivalent to setting overflow: hidden, defaults to false)
17613      */
17614     preventScrollbars: false,
17615     /**
17616      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17617      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17618      */
17619
17620     // private
17621     onRender : function(ct, position){
17622         if(!this.el){
17623             this.defaultAutoCreate = {
17624                 tag: "textarea",
17625                 style:"width:300px;height:60px;",
17626                 autocomplete: "new-password"
17627             };
17628         }
17629         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17630         if(this.grow){
17631             this.textSizeEl = Roo.DomHelper.append(document.body, {
17632                 tag: "pre", cls: "x-form-grow-sizer"
17633             });
17634             if(this.preventScrollbars){
17635                 this.el.setStyle("overflow", "hidden");
17636             }
17637             this.el.setHeight(this.growMin);
17638         }
17639     },
17640
17641     onDestroy : function(){
17642         if(this.textSizeEl){
17643             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17644         }
17645         Roo.form.TextArea.superclass.onDestroy.call(this);
17646     },
17647
17648     // private
17649     onKeyUp : function(e){
17650         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17651             this.autoSize();
17652         }
17653     },
17654
17655     /**
17656      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17657      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17658      */
17659     autoSize : function(){
17660         if(!this.grow || !this.textSizeEl){
17661             return;
17662         }
17663         var el = this.el;
17664         var v = el.dom.value;
17665         var ts = this.textSizeEl;
17666
17667         ts.innerHTML = '';
17668         ts.appendChild(document.createTextNode(v));
17669         v = ts.innerHTML;
17670
17671         Roo.fly(ts).setWidth(this.el.getWidth());
17672         if(v.length < 1){
17673             v = "&#160;&#160;";
17674         }else{
17675             if(Roo.isIE){
17676                 v = v.replace(/\n/g, '<p>&#160;</p>');
17677             }
17678             v += "&#160;\n&#160;";
17679         }
17680         ts.innerHTML = v;
17681         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17682         if(h != this.lastHeight){
17683             this.lastHeight = h;
17684             this.el.setHeight(h);
17685             this.fireEvent("autosize", this, h);
17686         }
17687     }
17688 });/*
17689  * Based on:
17690  * Ext JS Library 1.1.1
17691  * Copyright(c) 2006-2007, Ext JS, LLC.
17692  *
17693  * Originally Released Under LGPL - original licence link has changed is not relivant.
17694  *
17695  * Fork - LGPL
17696  * <script type="text/javascript">
17697  */
17698  
17699
17700 /**
17701  * @class Roo.form.NumberField
17702  * @extends Roo.form.TextField
17703  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17704  * @constructor
17705  * Creates a new NumberField
17706  * @param {Object} config Configuration options
17707  */
17708 Roo.form.NumberField = function(config){
17709     Roo.form.NumberField.superclass.constructor.call(this, config);
17710 };
17711
17712 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17713     /**
17714      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17715      */
17716     fieldClass: "x-form-field x-form-num-field",
17717     /**
17718      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17719      */
17720     allowDecimals : true,
17721     /**
17722      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17723      */
17724     decimalSeparator : ".",
17725     /**
17726      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17727      */
17728     decimalPrecision : 2,
17729     /**
17730      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17731      */
17732     allowNegative : true,
17733     /**
17734      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17735      */
17736     minValue : Number.NEGATIVE_INFINITY,
17737     /**
17738      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17739      */
17740     maxValue : Number.MAX_VALUE,
17741     /**
17742      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17743      */
17744     minText : "The minimum value for this field is {0}",
17745     /**
17746      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17747      */
17748     maxText : "The maximum value for this field is {0}",
17749     /**
17750      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17751      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17752      */
17753     nanText : "{0} is not a valid number",
17754
17755     // private
17756     initEvents : function(){
17757         Roo.form.NumberField.superclass.initEvents.call(this);
17758         var allowed = "0123456789";
17759         if(this.allowDecimals){
17760             allowed += this.decimalSeparator;
17761         }
17762         if(this.allowNegative){
17763             allowed += "-";
17764         }
17765         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17766         var keyPress = function(e){
17767             var k = e.getKey();
17768             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17769                 return;
17770             }
17771             var c = e.getCharCode();
17772             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17773                 e.stopEvent();
17774             }
17775         };
17776         this.el.on("keypress", keyPress, this);
17777     },
17778
17779     // private
17780     validateValue : function(value){
17781         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17782             return false;
17783         }
17784         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17785              return true;
17786         }
17787         var num = this.parseValue(value);
17788         if(isNaN(num)){
17789             this.markInvalid(String.format(this.nanText, value));
17790             return false;
17791         }
17792         if(num < this.minValue){
17793             this.markInvalid(String.format(this.minText, this.minValue));
17794             return false;
17795         }
17796         if(num > this.maxValue){
17797             this.markInvalid(String.format(this.maxText, this.maxValue));
17798             return false;
17799         }
17800         return true;
17801     },
17802
17803     getValue : function(){
17804         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17805     },
17806
17807     // private
17808     parseValue : function(value){
17809         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17810         return isNaN(value) ? '' : value;
17811     },
17812
17813     // private
17814     fixPrecision : function(value){
17815         var nan = isNaN(value);
17816         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17817             return nan ? '' : value;
17818         }
17819         return parseFloat(value).toFixed(this.decimalPrecision);
17820     },
17821
17822     setValue : function(v){
17823         v = this.fixPrecision(v);
17824         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17825     },
17826
17827     // private
17828     decimalPrecisionFcn : function(v){
17829         return Math.floor(v);
17830     },
17831
17832     beforeBlur : function(){
17833         var v = this.parseValue(this.getRawValue());
17834         if(v){
17835             this.setValue(v);
17836         }
17837     }
17838 });/*
17839  * Based on:
17840  * Ext JS Library 1.1.1
17841  * Copyright(c) 2006-2007, Ext JS, LLC.
17842  *
17843  * Originally Released Under LGPL - original licence link has changed is not relivant.
17844  *
17845  * Fork - LGPL
17846  * <script type="text/javascript">
17847  */
17848  
17849 /**
17850  * @class Roo.form.DateField
17851  * @extends Roo.form.TriggerField
17852  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17853 * @constructor
17854 * Create a new DateField
17855 * @param {Object} config
17856  */
17857 Roo.form.DateField = function(config)
17858 {
17859     Roo.form.DateField.superclass.constructor.call(this, config);
17860     
17861       this.addEvents({
17862          
17863         /**
17864          * @event select
17865          * Fires when a date is selected
17866              * @param {Roo.form.DateField} combo This combo box
17867              * @param {Date} date The date selected
17868              */
17869         'select' : true
17870          
17871     });
17872     
17873     
17874     if(typeof this.minValue == "string") {
17875         this.minValue = this.parseDate(this.minValue);
17876     }
17877     if(typeof this.maxValue == "string") {
17878         this.maxValue = this.parseDate(this.maxValue);
17879     }
17880     this.ddMatch = null;
17881     if(this.disabledDates){
17882         var dd = this.disabledDates;
17883         var re = "(?:";
17884         for(var i = 0; i < dd.length; i++){
17885             re += dd[i];
17886             if(i != dd.length-1) {
17887                 re += "|";
17888             }
17889         }
17890         this.ddMatch = new RegExp(re + ")");
17891     }
17892 };
17893
17894 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17895     /**
17896      * @cfg {String} format
17897      * The default date format string which can be overriden for localization support.  The format must be
17898      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17899      */
17900     format : "m/d/y",
17901     /**
17902      * @cfg {String} altFormats
17903      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17904      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17905      */
17906     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17907     /**
17908      * @cfg {Array} disabledDays
17909      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17910      */
17911     disabledDays : null,
17912     /**
17913      * @cfg {String} disabledDaysText
17914      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17915      */
17916     disabledDaysText : "Disabled",
17917     /**
17918      * @cfg {Array} disabledDates
17919      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17920      * expression so they are very powerful. Some examples:
17921      * <ul>
17922      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17923      * <li>["03/08", "09/16"] would disable those days for every year</li>
17924      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17925      * <li>["03/../2006"] would disable every day in March 2006</li>
17926      * <li>["^03"] would disable every day in every March</li>
17927      * </ul>
17928      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17929      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17930      */
17931     disabledDates : null,
17932     /**
17933      * @cfg {String} disabledDatesText
17934      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17935      */
17936     disabledDatesText : "Disabled",
17937     /**
17938      * @cfg {Date/String} minValue
17939      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17940      * valid format (defaults to null).
17941      */
17942     minValue : null,
17943     /**
17944      * @cfg {Date/String} maxValue
17945      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17946      * valid format (defaults to null).
17947      */
17948     maxValue : null,
17949     /**
17950      * @cfg {String} minText
17951      * The error text to display when the date in the cell is before minValue (defaults to
17952      * 'The date in this field must be after {minValue}').
17953      */
17954     minText : "The date in this field must be equal to or after {0}",
17955     /**
17956      * @cfg {String} maxText
17957      * The error text to display when the date in the cell is after maxValue (defaults to
17958      * 'The date in this field must be before {maxValue}').
17959      */
17960     maxText : "The date in this field must be equal to or before {0}",
17961     /**
17962      * @cfg {String} invalidText
17963      * The error text to display when the date in the field is invalid (defaults to
17964      * '{value} is not a valid date - it must be in the format {format}').
17965      */
17966     invalidText : "{0} is not a valid date - it must be in the format {1}",
17967     /**
17968      * @cfg {String} triggerClass
17969      * An additional CSS class used to style the trigger button.  The trigger will always get the
17970      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17971      * which displays a calendar icon).
17972      */
17973     triggerClass : 'x-form-date-trigger',
17974     
17975
17976     /**
17977      * @cfg {Boolean} useIso
17978      * if enabled, then the date field will use a hidden field to store the 
17979      * real value as iso formated date. default (false)
17980      */ 
17981     useIso : false,
17982     /**
17983      * @cfg {String/Object} autoCreate
17984      * A DomHelper element spec, or true for a default element spec (defaults to
17985      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17986      */ 
17987     // private
17988     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17989     
17990     // private
17991     hiddenField: false,
17992     
17993     onRender : function(ct, position)
17994     {
17995         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17996         if (this.useIso) {
17997             //this.el.dom.removeAttribute('name'); 
17998             Roo.log("Changing name?");
17999             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18000             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18001                     'before', true);
18002             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18003             // prevent input submission
18004             this.hiddenName = this.name;
18005         }
18006             
18007             
18008     },
18009     
18010     // private
18011     validateValue : function(value)
18012     {
18013         value = this.formatDate(value);
18014         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18015             Roo.log('super failed');
18016             return false;
18017         }
18018         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18019              return true;
18020         }
18021         var svalue = value;
18022         value = this.parseDate(value);
18023         if(!value){
18024             Roo.log('parse date failed' + svalue);
18025             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18026             return false;
18027         }
18028         var time = value.getTime();
18029         if(this.minValue && time < this.minValue.getTime()){
18030             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18031             return false;
18032         }
18033         if(this.maxValue && time > this.maxValue.getTime()){
18034             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18035             return false;
18036         }
18037         if(this.disabledDays){
18038             var day = value.getDay();
18039             for(var i = 0; i < this.disabledDays.length; i++) {
18040                 if(day === this.disabledDays[i]){
18041                     this.markInvalid(this.disabledDaysText);
18042                     return false;
18043                 }
18044             }
18045         }
18046         var fvalue = this.formatDate(value);
18047         if(this.ddMatch && this.ddMatch.test(fvalue)){
18048             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18049             return false;
18050         }
18051         return true;
18052     },
18053
18054     // private
18055     // Provides logic to override the default TriggerField.validateBlur which just returns true
18056     validateBlur : function(){
18057         return !this.menu || !this.menu.isVisible();
18058     },
18059     
18060     getName: function()
18061     {
18062         // returns hidden if it's set..
18063         if (!this.rendered) {return ''};
18064         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18065         
18066     },
18067
18068     /**
18069      * Returns the current date value of the date field.
18070      * @return {Date} The date value
18071      */
18072     getValue : function(){
18073         
18074         return  this.hiddenField ?
18075                 this.hiddenField.value :
18076                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18077     },
18078
18079     /**
18080      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18081      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18082      * (the default format used is "m/d/y").
18083      * <br />Usage:
18084      * <pre><code>
18085 //All of these calls set the same date value (May 4, 2006)
18086
18087 //Pass a date object:
18088 var dt = new Date('5/4/06');
18089 dateField.setValue(dt);
18090
18091 //Pass a date string (default format):
18092 dateField.setValue('5/4/06');
18093
18094 //Pass a date string (custom format):
18095 dateField.format = 'Y-m-d';
18096 dateField.setValue('2006-5-4');
18097 </code></pre>
18098      * @param {String/Date} date The date or valid date string
18099      */
18100     setValue : function(date){
18101         if (this.hiddenField) {
18102             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18103         }
18104         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18105         // make sure the value field is always stored as a date..
18106         this.value = this.parseDate(date);
18107         
18108         
18109     },
18110
18111     // private
18112     parseDate : function(value){
18113         if(!value || value instanceof Date){
18114             return value;
18115         }
18116         var v = Date.parseDate(value, this.format);
18117          if (!v && this.useIso) {
18118             v = Date.parseDate(value, 'Y-m-d');
18119         }
18120         if(!v && this.altFormats){
18121             if(!this.altFormatsArray){
18122                 this.altFormatsArray = this.altFormats.split("|");
18123             }
18124             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18125                 v = Date.parseDate(value, this.altFormatsArray[i]);
18126             }
18127         }
18128         return v;
18129     },
18130
18131     // private
18132     formatDate : function(date, fmt){
18133         return (!date || !(date instanceof Date)) ?
18134                date : date.dateFormat(fmt || this.format);
18135     },
18136
18137     // private
18138     menuListeners : {
18139         select: function(m, d){
18140             
18141             this.setValue(d);
18142             this.fireEvent('select', this, d);
18143         },
18144         show : function(){ // retain focus styling
18145             this.onFocus();
18146         },
18147         hide : function(){
18148             this.focus.defer(10, this);
18149             var ml = this.menuListeners;
18150             this.menu.un("select", ml.select,  this);
18151             this.menu.un("show", ml.show,  this);
18152             this.menu.un("hide", ml.hide,  this);
18153         }
18154     },
18155
18156     // private
18157     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18158     onTriggerClick : function(){
18159         if(this.disabled){
18160             return;
18161         }
18162         if(this.menu == null){
18163             this.menu = new Roo.menu.DateMenu();
18164         }
18165         Roo.apply(this.menu.picker,  {
18166             showClear: this.allowBlank,
18167             minDate : this.minValue,
18168             maxDate : this.maxValue,
18169             disabledDatesRE : this.ddMatch,
18170             disabledDatesText : this.disabledDatesText,
18171             disabledDays : this.disabledDays,
18172             disabledDaysText : this.disabledDaysText,
18173             format : this.useIso ? 'Y-m-d' : this.format,
18174             minText : String.format(this.minText, this.formatDate(this.minValue)),
18175             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18176         });
18177         this.menu.on(Roo.apply({}, this.menuListeners, {
18178             scope:this
18179         }));
18180         this.menu.picker.setValue(this.getValue() || new Date());
18181         this.menu.show(this.el, "tl-bl?");
18182     },
18183
18184     beforeBlur : function(){
18185         var v = this.parseDate(this.getRawValue());
18186         if(v){
18187             this.setValue(v);
18188         }
18189     },
18190
18191     /*@
18192      * overide
18193      * 
18194      */
18195     isDirty : function() {
18196         if(this.disabled) {
18197             return false;
18198         }
18199         
18200         if(typeof(this.startValue) === 'undefined'){
18201             return false;
18202         }
18203         
18204         return String(this.getValue()) !== String(this.startValue);
18205         
18206     },
18207     // @overide
18208     cleanLeadingSpace : function(e)
18209     {
18210        return;
18211     }
18212     
18213 });/*
18214  * Based on:
18215  * Ext JS Library 1.1.1
18216  * Copyright(c) 2006-2007, Ext JS, LLC.
18217  *
18218  * Originally Released Under LGPL - original licence link has changed is not relivant.
18219  *
18220  * Fork - LGPL
18221  * <script type="text/javascript">
18222  */
18223  
18224 /**
18225  * @class Roo.form.MonthField
18226  * @extends Roo.form.TriggerField
18227  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18228 * @constructor
18229 * Create a new MonthField
18230 * @param {Object} config
18231  */
18232 Roo.form.MonthField = function(config){
18233     
18234     Roo.form.MonthField.superclass.constructor.call(this, config);
18235     
18236       this.addEvents({
18237          
18238         /**
18239          * @event select
18240          * Fires when a date is selected
18241              * @param {Roo.form.MonthFieeld} combo This combo box
18242              * @param {Date} date The date selected
18243              */
18244         'select' : true
18245          
18246     });
18247     
18248     
18249     if(typeof this.minValue == "string") {
18250         this.minValue = this.parseDate(this.minValue);
18251     }
18252     if(typeof this.maxValue == "string") {
18253         this.maxValue = this.parseDate(this.maxValue);
18254     }
18255     this.ddMatch = null;
18256     if(this.disabledDates){
18257         var dd = this.disabledDates;
18258         var re = "(?:";
18259         for(var i = 0; i < dd.length; i++){
18260             re += dd[i];
18261             if(i != dd.length-1) {
18262                 re += "|";
18263             }
18264         }
18265         this.ddMatch = new RegExp(re + ")");
18266     }
18267 };
18268
18269 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18270     /**
18271      * @cfg {String} format
18272      * The default date format string which can be overriden for localization support.  The format must be
18273      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18274      */
18275     format : "M Y",
18276     /**
18277      * @cfg {String} altFormats
18278      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18279      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18280      */
18281     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18282     /**
18283      * @cfg {Array} disabledDays
18284      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18285      */
18286     disabledDays : [0,1,2,3,4,5,6],
18287     /**
18288      * @cfg {String} disabledDaysText
18289      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18290      */
18291     disabledDaysText : "Disabled",
18292     /**
18293      * @cfg {Array} disabledDates
18294      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18295      * expression so they are very powerful. Some examples:
18296      * <ul>
18297      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18298      * <li>["03/08", "09/16"] would disable those days for every year</li>
18299      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18300      * <li>["03/../2006"] would disable every day in March 2006</li>
18301      * <li>["^03"] would disable every day in every March</li>
18302      * </ul>
18303      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18304      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18305      */
18306     disabledDates : null,
18307     /**
18308      * @cfg {String} disabledDatesText
18309      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18310      */
18311     disabledDatesText : "Disabled",
18312     /**
18313      * @cfg {Date/String} minValue
18314      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18315      * valid format (defaults to null).
18316      */
18317     minValue : null,
18318     /**
18319      * @cfg {Date/String} maxValue
18320      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18321      * valid format (defaults to null).
18322      */
18323     maxValue : null,
18324     /**
18325      * @cfg {String} minText
18326      * The error text to display when the date in the cell is before minValue (defaults to
18327      * 'The date in this field must be after {minValue}').
18328      */
18329     minText : "The date in this field must be equal to or after {0}",
18330     /**
18331      * @cfg {String} maxTextf
18332      * The error text to display when the date in the cell is after maxValue (defaults to
18333      * 'The date in this field must be before {maxValue}').
18334      */
18335     maxText : "The date in this field must be equal to or before {0}",
18336     /**
18337      * @cfg {String} invalidText
18338      * The error text to display when the date in the field is invalid (defaults to
18339      * '{value} is not a valid date - it must be in the format {format}').
18340      */
18341     invalidText : "{0} is not a valid date - it must be in the format {1}",
18342     /**
18343      * @cfg {String} triggerClass
18344      * An additional CSS class used to style the trigger button.  The trigger will always get the
18345      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18346      * which displays a calendar icon).
18347      */
18348     triggerClass : 'x-form-date-trigger',
18349     
18350
18351     /**
18352      * @cfg {Boolean} useIso
18353      * if enabled, then the date field will use a hidden field to store the 
18354      * real value as iso formated date. default (true)
18355      */ 
18356     useIso : true,
18357     /**
18358      * @cfg {String/Object} autoCreate
18359      * A DomHelper element spec, or true for a default element spec (defaults to
18360      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18361      */ 
18362     // private
18363     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18364     
18365     // private
18366     hiddenField: false,
18367     
18368     hideMonthPicker : false,
18369     
18370     onRender : function(ct, position)
18371     {
18372         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18373         if (this.useIso) {
18374             this.el.dom.removeAttribute('name'); 
18375             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18376                     'before', true);
18377             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18378             // prevent input submission
18379             this.hiddenName = this.name;
18380         }
18381             
18382             
18383     },
18384     
18385     // private
18386     validateValue : function(value)
18387     {
18388         value = this.formatDate(value);
18389         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18390             return false;
18391         }
18392         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18393              return true;
18394         }
18395         var svalue = value;
18396         value = this.parseDate(value);
18397         if(!value){
18398             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18399             return false;
18400         }
18401         var time = value.getTime();
18402         if(this.minValue && time < this.minValue.getTime()){
18403             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18404             return false;
18405         }
18406         if(this.maxValue && time > this.maxValue.getTime()){
18407             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18408             return false;
18409         }
18410         /*if(this.disabledDays){
18411             var day = value.getDay();
18412             for(var i = 0; i < this.disabledDays.length; i++) {
18413                 if(day === this.disabledDays[i]){
18414                     this.markInvalid(this.disabledDaysText);
18415                     return false;
18416                 }
18417             }
18418         }
18419         */
18420         var fvalue = this.formatDate(value);
18421         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18422             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18423             return false;
18424         }
18425         */
18426         return true;
18427     },
18428
18429     // private
18430     // Provides logic to override the default TriggerField.validateBlur which just returns true
18431     validateBlur : function(){
18432         return !this.menu || !this.menu.isVisible();
18433     },
18434
18435     /**
18436      * Returns the current date value of the date field.
18437      * @return {Date} The date value
18438      */
18439     getValue : function(){
18440         
18441         
18442         
18443         return  this.hiddenField ?
18444                 this.hiddenField.value :
18445                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18446     },
18447
18448     /**
18449      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18450      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18451      * (the default format used is "m/d/y").
18452      * <br />Usage:
18453      * <pre><code>
18454 //All of these calls set the same date value (May 4, 2006)
18455
18456 //Pass a date object:
18457 var dt = new Date('5/4/06');
18458 monthField.setValue(dt);
18459
18460 //Pass a date string (default format):
18461 monthField.setValue('5/4/06');
18462
18463 //Pass a date string (custom format):
18464 monthField.format = 'Y-m-d';
18465 monthField.setValue('2006-5-4');
18466 </code></pre>
18467      * @param {String/Date} date The date or valid date string
18468      */
18469     setValue : function(date){
18470         Roo.log('month setValue' + date);
18471         // can only be first of month..
18472         
18473         var val = this.parseDate(date);
18474         
18475         if (this.hiddenField) {
18476             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18477         }
18478         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18479         this.value = this.parseDate(date);
18480     },
18481
18482     // private
18483     parseDate : function(value){
18484         if(!value || value instanceof Date){
18485             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18486             return value;
18487         }
18488         var v = Date.parseDate(value, this.format);
18489         if (!v && this.useIso) {
18490             v = Date.parseDate(value, 'Y-m-d');
18491         }
18492         if (v) {
18493             // 
18494             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18495         }
18496         
18497         
18498         if(!v && this.altFormats){
18499             if(!this.altFormatsArray){
18500                 this.altFormatsArray = this.altFormats.split("|");
18501             }
18502             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18503                 v = Date.parseDate(value, this.altFormatsArray[i]);
18504             }
18505         }
18506         return v;
18507     },
18508
18509     // private
18510     formatDate : function(date, fmt){
18511         return (!date || !(date instanceof Date)) ?
18512                date : date.dateFormat(fmt || this.format);
18513     },
18514
18515     // private
18516     menuListeners : {
18517         select: function(m, d){
18518             this.setValue(d);
18519             this.fireEvent('select', this, d);
18520         },
18521         show : function(){ // retain focus styling
18522             this.onFocus();
18523         },
18524         hide : function(){
18525             this.focus.defer(10, this);
18526             var ml = this.menuListeners;
18527             this.menu.un("select", ml.select,  this);
18528             this.menu.un("show", ml.show,  this);
18529             this.menu.un("hide", ml.hide,  this);
18530         }
18531     },
18532     // private
18533     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18534     onTriggerClick : function(){
18535         if(this.disabled){
18536             return;
18537         }
18538         if(this.menu == null){
18539             this.menu = new Roo.menu.DateMenu();
18540            
18541         }
18542         
18543         Roo.apply(this.menu.picker,  {
18544             
18545             showClear: this.allowBlank,
18546             minDate : this.minValue,
18547             maxDate : this.maxValue,
18548             disabledDatesRE : this.ddMatch,
18549             disabledDatesText : this.disabledDatesText,
18550             
18551             format : this.useIso ? 'Y-m-d' : this.format,
18552             minText : String.format(this.minText, this.formatDate(this.minValue)),
18553             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18554             
18555         });
18556          this.menu.on(Roo.apply({}, this.menuListeners, {
18557             scope:this
18558         }));
18559        
18560         
18561         var m = this.menu;
18562         var p = m.picker;
18563         
18564         // hide month picker get's called when we called by 'before hide';
18565         
18566         var ignorehide = true;
18567         p.hideMonthPicker  = function(disableAnim){
18568             if (ignorehide) {
18569                 return;
18570             }
18571              if(this.monthPicker){
18572                 Roo.log("hideMonthPicker called");
18573                 if(disableAnim === true){
18574                     this.monthPicker.hide();
18575                 }else{
18576                     this.monthPicker.slideOut('t', {duration:.2});
18577                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18578                     p.fireEvent("select", this, this.value);
18579                     m.hide();
18580                 }
18581             }
18582         }
18583         
18584         Roo.log('picker set value');
18585         Roo.log(this.getValue());
18586         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18587         m.show(this.el, 'tl-bl?');
18588         ignorehide  = false;
18589         // this will trigger hideMonthPicker..
18590         
18591         
18592         // hidden the day picker
18593         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18594         
18595         
18596         
18597       
18598         
18599         p.showMonthPicker.defer(100, p);
18600     
18601         
18602        
18603     },
18604
18605     beforeBlur : function(){
18606         var v = this.parseDate(this.getRawValue());
18607         if(v){
18608             this.setValue(v);
18609         }
18610     }
18611
18612     /** @cfg {Boolean} grow @hide */
18613     /** @cfg {Number} growMin @hide */
18614     /** @cfg {Number} growMax @hide */
18615     /**
18616      * @hide
18617      * @method autoSize
18618      */
18619 });/*
18620  * Based on:
18621  * Ext JS Library 1.1.1
18622  * Copyright(c) 2006-2007, Ext JS, LLC.
18623  *
18624  * Originally Released Under LGPL - original licence link has changed is not relivant.
18625  *
18626  * Fork - LGPL
18627  * <script type="text/javascript">
18628  */
18629  
18630
18631 /**
18632  * @class Roo.form.ComboBox
18633  * @extends Roo.form.TriggerField
18634  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18635  * @constructor
18636  * Create a new ComboBox.
18637  * @param {Object} config Configuration options
18638  */
18639 Roo.form.ComboBox = function(config){
18640     Roo.form.ComboBox.superclass.constructor.call(this, config);
18641     this.addEvents({
18642         /**
18643          * @event expand
18644          * Fires when the dropdown list is expanded
18645              * @param {Roo.form.ComboBox} combo This combo box
18646              */
18647         'expand' : true,
18648         /**
18649          * @event collapse
18650          * Fires when the dropdown list is collapsed
18651              * @param {Roo.form.ComboBox} combo This combo box
18652              */
18653         'collapse' : true,
18654         /**
18655          * @event beforeselect
18656          * Fires before a list item is selected. Return false to cancel the selection.
18657              * @param {Roo.form.ComboBox} combo This combo box
18658              * @param {Roo.data.Record} record The data record returned from the underlying store
18659              * @param {Number} index The index of the selected item in the dropdown list
18660              */
18661         'beforeselect' : true,
18662         /**
18663          * @event select
18664          * Fires when a list item is selected
18665              * @param {Roo.form.ComboBox} combo This combo box
18666              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18667              * @param {Number} index The index of the selected item in the dropdown list
18668              */
18669         'select' : true,
18670         /**
18671          * @event beforequery
18672          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18673          * The event object passed has these properties:
18674              * @param {Roo.form.ComboBox} combo This combo box
18675              * @param {String} query The query
18676              * @param {Boolean} forceAll true to force "all" query
18677              * @param {Boolean} cancel true to cancel the query
18678              * @param {Object} e The query event object
18679              */
18680         'beforequery': true,
18681          /**
18682          * @event add
18683          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18684              * @param {Roo.form.ComboBox} combo This combo box
18685              */
18686         'add' : true,
18687         /**
18688          * @event edit
18689          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18690              * @param {Roo.form.ComboBox} combo This combo box
18691              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18692              */
18693         'edit' : true
18694         
18695         
18696     });
18697     if(this.transform){
18698         this.allowDomMove = false;
18699         var s = Roo.getDom(this.transform);
18700         if(!this.hiddenName){
18701             this.hiddenName = s.name;
18702         }
18703         if(!this.store){
18704             this.mode = 'local';
18705             var d = [], opts = s.options;
18706             for(var i = 0, len = opts.length;i < len; i++){
18707                 var o = opts[i];
18708                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18709                 if(o.selected) {
18710                     this.value = value;
18711                 }
18712                 d.push([value, o.text]);
18713             }
18714             this.store = new Roo.data.SimpleStore({
18715                 'id': 0,
18716                 fields: ['value', 'text'],
18717                 data : d
18718             });
18719             this.valueField = 'value';
18720             this.displayField = 'text';
18721         }
18722         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18723         if(!this.lazyRender){
18724             this.target = true;
18725             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18726             s.parentNode.removeChild(s); // remove it
18727             this.render(this.el.parentNode);
18728         }else{
18729             s.parentNode.removeChild(s); // remove it
18730         }
18731
18732     }
18733     if (this.store) {
18734         this.store = Roo.factory(this.store, Roo.data);
18735     }
18736     
18737     this.selectedIndex = -1;
18738     if(this.mode == 'local'){
18739         if(config.queryDelay === undefined){
18740             this.queryDelay = 10;
18741         }
18742         if(config.minChars === undefined){
18743             this.minChars = 0;
18744         }
18745     }
18746 };
18747
18748 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18749     /**
18750      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18751      */
18752     /**
18753      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18754      * rendering into an Roo.Editor, defaults to false)
18755      */
18756     /**
18757      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18758      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18759      */
18760     /**
18761      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18762      */
18763     /**
18764      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18765      * the dropdown list (defaults to undefined, with no header element)
18766      */
18767
18768      /**
18769      * @cfg {String/Roo.Template} tpl The template to use to render the output
18770      */
18771      
18772     // private
18773     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18774     /**
18775      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18776      */
18777     listWidth: undefined,
18778     /**
18779      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18780      * mode = 'remote' or 'text' if mode = 'local')
18781      */
18782     displayField: undefined,
18783     /**
18784      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18785      * mode = 'remote' or 'value' if mode = 'local'). 
18786      * Note: use of a valueField requires the user make a selection
18787      * in order for a value to be mapped.
18788      */
18789     valueField: undefined,
18790     
18791     
18792     /**
18793      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18794      * field's data value (defaults to the underlying DOM element's name)
18795      */
18796     hiddenName: undefined,
18797     /**
18798      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18799      */
18800     listClass: '',
18801     /**
18802      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18803      */
18804     selectedClass: 'x-combo-selected',
18805     /**
18806      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18807      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18808      * which displays a downward arrow icon).
18809      */
18810     triggerClass : 'x-form-arrow-trigger',
18811     /**
18812      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18813      */
18814     shadow:'sides',
18815     /**
18816      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18817      * anchor positions (defaults to 'tl-bl')
18818      */
18819     listAlign: 'tl-bl?',
18820     /**
18821      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18822      */
18823     maxHeight: 300,
18824     /**
18825      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18826      * query specified by the allQuery config option (defaults to 'query')
18827      */
18828     triggerAction: 'query',
18829     /**
18830      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18831      * (defaults to 4, does not apply if editable = false)
18832      */
18833     minChars : 4,
18834     /**
18835      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18836      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18837      */
18838     typeAhead: false,
18839     /**
18840      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18841      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18842      */
18843     queryDelay: 500,
18844     /**
18845      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18846      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18847      */
18848     pageSize: 0,
18849     /**
18850      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18851      * when editable = true (defaults to false)
18852      */
18853     selectOnFocus:false,
18854     /**
18855      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18856      */
18857     queryParam: 'query',
18858     /**
18859      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18860      * when mode = 'remote' (defaults to 'Loading...')
18861      */
18862     loadingText: 'Loading...',
18863     /**
18864      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18865      */
18866     resizable: false,
18867     /**
18868      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18869      */
18870     handleHeight : 8,
18871     /**
18872      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18873      * traditional select (defaults to true)
18874      */
18875     editable: true,
18876     /**
18877      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18878      */
18879     allQuery: '',
18880     /**
18881      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18882      */
18883     mode: 'remote',
18884     /**
18885      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18886      * listWidth has a higher value)
18887      */
18888     minListWidth : 70,
18889     /**
18890      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18891      * allow the user to set arbitrary text into the field (defaults to false)
18892      */
18893     forceSelection:false,
18894     /**
18895      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18896      * if typeAhead = true (defaults to 250)
18897      */
18898     typeAheadDelay : 250,
18899     /**
18900      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18901      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18902      */
18903     valueNotFoundText : undefined,
18904     /**
18905      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18906      */
18907     blockFocus : false,
18908     
18909     /**
18910      * @cfg {Boolean} disableClear Disable showing of clear button.
18911      */
18912     disableClear : false,
18913     /**
18914      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18915      */
18916     alwaysQuery : false,
18917     
18918     //private
18919     addicon : false,
18920     editicon: false,
18921     
18922     // element that contains real text value.. (when hidden is used..)
18923      
18924     // private
18925     onRender : function(ct, position)
18926     {
18927         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18928         
18929         if(this.hiddenName){
18930             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18931                     'before', true);
18932             this.hiddenField.value =
18933                 this.hiddenValue !== undefined ? this.hiddenValue :
18934                 this.value !== undefined ? this.value : '';
18935
18936             // prevent input submission
18937             this.el.dom.removeAttribute('name');
18938              
18939              
18940         }
18941         
18942         if(Roo.isGecko){
18943             this.el.dom.setAttribute('autocomplete', 'off');
18944         }
18945
18946         var cls = 'x-combo-list';
18947
18948         this.list = new Roo.Layer({
18949             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18950         });
18951
18952         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18953         this.list.setWidth(lw);
18954         this.list.swallowEvent('mousewheel');
18955         this.assetHeight = 0;
18956
18957         if(this.title){
18958             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18959             this.assetHeight += this.header.getHeight();
18960         }
18961
18962         this.innerList = this.list.createChild({cls:cls+'-inner'});
18963         this.innerList.on('mouseover', this.onViewOver, this);
18964         this.innerList.on('mousemove', this.onViewMove, this);
18965         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18966         
18967         if(this.allowBlank && !this.pageSize && !this.disableClear){
18968             this.footer = this.list.createChild({cls:cls+'-ft'});
18969             this.pageTb = new Roo.Toolbar(this.footer);
18970            
18971         }
18972         if(this.pageSize){
18973             this.footer = this.list.createChild({cls:cls+'-ft'});
18974             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18975                     {pageSize: this.pageSize});
18976             
18977         }
18978         
18979         if (this.pageTb && this.allowBlank && !this.disableClear) {
18980             var _this = this;
18981             this.pageTb.add(new Roo.Toolbar.Fill(), {
18982                 cls: 'x-btn-icon x-btn-clear',
18983                 text: '&#160;',
18984                 handler: function()
18985                 {
18986                     _this.collapse();
18987                     _this.clearValue();
18988                     _this.onSelect(false, -1);
18989                 }
18990             });
18991         }
18992         if (this.footer) {
18993             this.assetHeight += this.footer.getHeight();
18994         }
18995         
18996
18997         if(!this.tpl){
18998             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18999         }
19000
19001         this.view = new Roo.View(this.innerList, this.tpl, {
19002             singleSelect:true,
19003             store: this.store,
19004             selectedClass: this.selectedClass
19005         });
19006
19007         this.view.on('click', this.onViewClick, this);
19008
19009         this.store.on('beforeload', this.onBeforeLoad, this);
19010         this.store.on('load', this.onLoad, this);
19011         this.store.on('loadexception', this.onLoadException, this);
19012
19013         if(this.resizable){
19014             this.resizer = new Roo.Resizable(this.list,  {
19015                pinned:true, handles:'se'
19016             });
19017             this.resizer.on('resize', function(r, w, h){
19018                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19019                 this.listWidth = w;
19020                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19021                 this.restrictHeight();
19022             }, this);
19023             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19024         }
19025         if(!this.editable){
19026             this.editable = true;
19027             this.setEditable(false);
19028         }  
19029         
19030         
19031         if (typeof(this.events.add.listeners) != 'undefined') {
19032             
19033             this.addicon = this.wrap.createChild(
19034                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19035        
19036             this.addicon.on('click', function(e) {
19037                 this.fireEvent('add', this);
19038             }, this);
19039         }
19040         if (typeof(this.events.edit.listeners) != 'undefined') {
19041             
19042             this.editicon = this.wrap.createChild(
19043                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19044             if (this.addicon) {
19045                 this.editicon.setStyle('margin-left', '40px');
19046             }
19047             this.editicon.on('click', function(e) {
19048                 
19049                 // we fire even  if inothing is selected..
19050                 this.fireEvent('edit', this, this.lastData );
19051                 
19052             }, this);
19053         }
19054         
19055         
19056         
19057     },
19058
19059     // private
19060     initEvents : function(){
19061         Roo.form.ComboBox.superclass.initEvents.call(this);
19062
19063         this.keyNav = new Roo.KeyNav(this.el, {
19064             "up" : function(e){
19065                 this.inKeyMode = true;
19066                 this.selectPrev();
19067             },
19068
19069             "down" : function(e){
19070                 if(!this.isExpanded()){
19071                     this.onTriggerClick();
19072                 }else{
19073                     this.inKeyMode = true;
19074                     this.selectNext();
19075                 }
19076             },
19077
19078             "enter" : function(e){
19079                 this.onViewClick();
19080                 //return true;
19081             },
19082
19083             "esc" : function(e){
19084                 this.collapse();
19085             },
19086
19087             "tab" : function(e){
19088                 this.onViewClick(false);
19089                 this.fireEvent("specialkey", this, e);
19090                 return true;
19091             },
19092
19093             scope : this,
19094
19095             doRelay : function(foo, bar, hname){
19096                 if(hname == 'down' || this.scope.isExpanded()){
19097                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19098                 }
19099                 return true;
19100             },
19101
19102             forceKeyDown: true
19103         });
19104         this.queryDelay = Math.max(this.queryDelay || 10,
19105                 this.mode == 'local' ? 10 : 250);
19106         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19107         if(this.typeAhead){
19108             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19109         }
19110         if(this.editable !== false){
19111             this.el.on("keyup", this.onKeyUp, this);
19112         }
19113         if(this.forceSelection){
19114             this.on('blur', this.doForce, this);
19115         }
19116     },
19117
19118     onDestroy : function(){
19119         if(this.view){
19120             this.view.setStore(null);
19121             this.view.el.removeAllListeners();
19122             this.view.el.remove();
19123             this.view.purgeListeners();
19124         }
19125         if(this.list){
19126             this.list.destroy();
19127         }
19128         if(this.store){
19129             this.store.un('beforeload', this.onBeforeLoad, this);
19130             this.store.un('load', this.onLoad, this);
19131             this.store.un('loadexception', this.onLoadException, this);
19132         }
19133         Roo.form.ComboBox.superclass.onDestroy.call(this);
19134     },
19135
19136     // private
19137     fireKey : function(e){
19138         if(e.isNavKeyPress() && !this.list.isVisible()){
19139             this.fireEvent("specialkey", this, e);
19140         }
19141     },
19142
19143     // private
19144     onResize: function(w, h){
19145         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19146         
19147         if(typeof w != 'number'){
19148             // we do not handle it!?!?
19149             return;
19150         }
19151         var tw = this.trigger.getWidth();
19152         tw += this.addicon ? this.addicon.getWidth() : 0;
19153         tw += this.editicon ? this.editicon.getWidth() : 0;
19154         var x = w - tw;
19155         this.el.setWidth( this.adjustWidth('input', x));
19156             
19157         this.trigger.setStyle('left', x+'px');
19158         
19159         if(this.list && this.listWidth === undefined){
19160             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19161             this.list.setWidth(lw);
19162             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19163         }
19164         
19165     
19166         
19167     },
19168
19169     /**
19170      * Allow or prevent the user from directly editing the field text.  If false is passed,
19171      * the user will only be able to select from the items defined in the dropdown list.  This method
19172      * is the runtime equivalent of setting the 'editable' config option at config time.
19173      * @param {Boolean} value True to allow the user to directly edit the field text
19174      */
19175     setEditable : function(value){
19176         if(value == this.editable){
19177             return;
19178         }
19179         this.editable = value;
19180         if(!value){
19181             this.el.dom.setAttribute('readOnly', true);
19182             this.el.on('mousedown', this.onTriggerClick,  this);
19183             this.el.addClass('x-combo-noedit');
19184         }else{
19185             this.el.dom.setAttribute('readOnly', false);
19186             this.el.un('mousedown', this.onTriggerClick,  this);
19187             this.el.removeClass('x-combo-noedit');
19188         }
19189     },
19190
19191     // private
19192     onBeforeLoad : function(){
19193         if(!this.hasFocus){
19194             return;
19195         }
19196         this.innerList.update(this.loadingText ?
19197                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19198         this.restrictHeight();
19199         this.selectedIndex = -1;
19200     },
19201
19202     // private
19203     onLoad : function(){
19204         if(!this.hasFocus){
19205             return;
19206         }
19207         if(this.store.getCount() > 0){
19208             this.expand();
19209             this.restrictHeight();
19210             if(this.lastQuery == this.allQuery){
19211                 if(this.editable){
19212                     this.el.dom.select();
19213                 }
19214                 if(!this.selectByValue(this.value, true)){
19215                     this.select(0, true);
19216                 }
19217             }else{
19218                 this.selectNext();
19219                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19220                     this.taTask.delay(this.typeAheadDelay);
19221                 }
19222             }
19223         }else{
19224             this.onEmptyResults();
19225         }
19226         //this.el.focus();
19227     },
19228     // private
19229     onLoadException : function()
19230     {
19231         this.collapse();
19232         Roo.log(this.store.reader.jsonData);
19233         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19234             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19235         }
19236         
19237         
19238     },
19239     // private
19240     onTypeAhead : function(){
19241         if(this.store.getCount() > 0){
19242             var r = this.store.getAt(0);
19243             var newValue = r.data[this.displayField];
19244             var len = newValue.length;
19245             var selStart = this.getRawValue().length;
19246             if(selStart != len){
19247                 this.setRawValue(newValue);
19248                 this.selectText(selStart, newValue.length);
19249             }
19250         }
19251     },
19252
19253     // private
19254     onSelect : function(record, index){
19255         if(this.fireEvent('beforeselect', this, record, index) !== false){
19256             this.setFromData(index > -1 ? record.data : false);
19257             this.collapse();
19258             this.fireEvent('select', this, record, index);
19259         }
19260     },
19261
19262     /**
19263      * Returns the currently selected field value or empty string if no value is set.
19264      * @return {String} value The selected value
19265      */
19266     getValue : function(){
19267         if(this.valueField){
19268             return typeof this.value != 'undefined' ? this.value : '';
19269         }
19270         return Roo.form.ComboBox.superclass.getValue.call(this);
19271     },
19272
19273     /**
19274      * Clears any text/value currently set in the field
19275      */
19276     clearValue : function(){
19277         if(this.hiddenField){
19278             this.hiddenField.value = '';
19279         }
19280         this.value = '';
19281         this.setRawValue('');
19282         this.lastSelectionText = '';
19283         
19284     },
19285
19286     /**
19287      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19288      * will be displayed in the field.  If the value does not match the data value of an existing item,
19289      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19290      * Otherwise the field will be blank (although the value will still be set).
19291      * @param {String} value The value to match
19292      */
19293     setValue : function(v){
19294         var text = v;
19295         if(this.valueField){
19296             var r = this.findRecord(this.valueField, v);
19297             if(r){
19298                 text = r.data[this.displayField];
19299             }else if(this.valueNotFoundText !== undefined){
19300                 text = this.valueNotFoundText;
19301             }
19302         }
19303         this.lastSelectionText = text;
19304         if(this.hiddenField){
19305             this.hiddenField.value = v;
19306         }
19307         Roo.form.ComboBox.superclass.setValue.call(this, text);
19308         this.value = v;
19309     },
19310     /**
19311      * @property {Object} the last set data for the element
19312      */
19313     
19314     lastData : false,
19315     /**
19316      * Sets the value of the field based on a object which is related to the record format for the store.
19317      * @param {Object} value the value to set as. or false on reset?
19318      */
19319     setFromData : function(o){
19320         var dv = ''; // display value
19321         var vv = ''; // value value..
19322         this.lastData = o;
19323         if (this.displayField) {
19324             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19325         } else {
19326             // this is an error condition!!!
19327             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19328         }
19329         
19330         if(this.valueField){
19331             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19332         }
19333         if(this.hiddenField){
19334             this.hiddenField.value = vv;
19335             
19336             this.lastSelectionText = dv;
19337             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19338             this.value = vv;
19339             return;
19340         }
19341         // no hidden field.. - we store the value in 'value', but still display
19342         // display field!!!!
19343         this.lastSelectionText = dv;
19344         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19345         this.value = vv;
19346         
19347         
19348     },
19349     // private
19350     reset : function(){
19351         // overridden so that last data is reset..
19352         this.setValue(this.resetValue);
19353         this.originalValue = this.getValue();
19354         this.clearInvalid();
19355         this.lastData = false;
19356         if (this.view) {
19357             this.view.clearSelections();
19358         }
19359     },
19360     // private
19361     findRecord : function(prop, value){
19362         var record;
19363         if(this.store.getCount() > 0){
19364             this.store.each(function(r){
19365                 if(r.data[prop] == value){
19366                     record = r;
19367                     return false;
19368                 }
19369                 return true;
19370             });
19371         }
19372         return record;
19373     },
19374     
19375     getName: function()
19376     {
19377         // returns hidden if it's set..
19378         if (!this.rendered) {return ''};
19379         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19380         
19381     },
19382     // private
19383     onViewMove : function(e, t){
19384         this.inKeyMode = false;
19385     },
19386
19387     // private
19388     onViewOver : function(e, t){
19389         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19390             return;
19391         }
19392         var item = this.view.findItemFromChild(t);
19393         if(item){
19394             var index = this.view.indexOf(item);
19395             this.select(index, false);
19396         }
19397     },
19398
19399     // private
19400     onViewClick : function(doFocus)
19401     {
19402         var index = this.view.getSelectedIndexes()[0];
19403         var r = this.store.getAt(index);
19404         if(r){
19405             this.onSelect(r, index);
19406         }
19407         if(doFocus !== false && !this.blockFocus){
19408             this.el.focus();
19409         }
19410     },
19411
19412     // private
19413     restrictHeight : function(){
19414         this.innerList.dom.style.height = '';
19415         var inner = this.innerList.dom;
19416         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19417         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19418         this.list.beginUpdate();
19419         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19420         this.list.alignTo(this.el, this.listAlign);
19421         this.list.endUpdate();
19422     },
19423
19424     // private
19425     onEmptyResults : function(){
19426         this.collapse();
19427     },
19428
19429     /**
19430      * Returns true if the dropdown list is expanded, else false.
19431      */
19432     isExpanded : function(){
19433         return this.list.isVisible();
19434     },
19435
19436     /**
19437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19439      * @param {String} value The data value of the item to select
19440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19441      * selected item if it is not currently in view (defaults to true)
19442      * @return {Boolean} True if the value matched an item in the list, else false
19443      */
19444     selectByValue : function(v, scrollIntoView){
19445         if(v !== undefined && v !== null){
19446             var r = this.findRecord(this.valueField || this.displayField, v);
19447             if(r){
19448                 this.select(this.store.indexOf(r), scrollIntoView);
19449                 return true;
19450             }
19451         }
19452         return false;
19453     },
19454
19455     /**
19456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19458      * @param {Number} index The zero-based index of the list item to select
19459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19460      * selected item if it is not currently in view (defaults to true)
19461      */
19462     select : function(index, scrollIntoView){
19463         this.selectedIndex = index;
19464         this.view.select(index);
19465         if(scrollIntoView !== false){
19466             var el = this.view.getNode(index);
19467             if(el){
19468                 this.innerList.scrollChildIntoView(el, false);
19469             }
19470         }
19471     },
19472
19473     // private
19474     selectNext : function(){
19475         var ct = this.store.getCount();
19476         if(ct > 0){
19477             if(this.selectedIndex == -1){
19478                 this.select(0);
19479             }else if(this.selectedIndex < ct-1){
19480                 this.select(this.selectedIndex+1);
19481             }
19482         }
19483     },
19484
19485     // private
19486     selectPrev : function(){
19487         var ct = this.store.getCount();
19488         if(ct > 0){
19489             if(this.selectedIndex == -1){
19490                 this.select(0);
19491             }else if(this.selectedIndex != 0){
19492                 this.select(this.selectedIndex-1);
19493             }
19494         }
19495     },
19496
19497     // private
19498     onKeyUp : function(e){
19499         if(this.editable !== false && !e.isSpecialKey()){
19500             this.lastKey = e.getKey();
19501             this.dqTask.delay(this.queryDelay);
19502         }
19503     },
19504
19505     // private
19506     validateBlur : function(){
19507         return !this.list || !this.list.isVisible();   
19508     },
19509
19510     // private
19511     initQuery : function(){
19512         this.doQuery(this.getRawValue());
19513     },
19514
19515     // private
19516     doForce : function(){
19517         if(this.el.dom.value.length > 0){
19518             this.el.dom.value =
19519                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19520              
19521         }
19522     },
19523
19524     /**
19525      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19526      * query allowing the query action to be canceled if needed.
19527      * @param {String} query The SQL query to execute
19528      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19529      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19530      * saved in the current store (defaults to false)
19531      */
19532     doQuery : function(q, forceAll){
19533         if(q === undefined || q === null){
19534             q = '';
19535         }
19536         var qe = {
19537             query: q,
19538             forceAll: forceAll,
19539             combo: this,
19540             cancel:false
19541         };
19542         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19543             return false;
19544         }
19545         q = qe.query;
19546         forceAll = qe.forceAll;
19547         if(forceAll === true || (q.length >= this.minChars)){
19548             if(this.lastQuery != q || this.alwaysQuery){
19549                 this.lastQuery = q;
19550                 if(this.mode == 'local'){
19551                     this.selectedIndex = -1;
19552                     if(forceAll){
19553                         this.store.clearFilter();
19554                     }else{
19555                         this.store.filter(this.displayField, q);
19556                     }
19557                     this.onLoad();
19558                 }else{
19559                     this.store.baseParams[this.queryParam] = q;
19560                     this.store.load({
19561                         params: this.getParams(q)
19562                     });
19563                     this.expand();
19564                 }
19565             }else{
19566                 this.selectedIndex = -1;
19567                 this.onLoad();   
19568             }
19569         }
19570     },
19571
19572     // private
19573     getParams : function(q){
19574         var p = {};
19575         //p[this.queryParam] = q;
19576         if(this.pageSize){
19577             p.start = 0;
19578             p.limit = this.pageSize;
19579         }
19580         return p;
19581     },
19582
19583     /**
19584      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19585      */
19586     collapse : function(){
19587         if(!this.isExpanded()){
19588             return;
19589         }
19590         this.list.hide();
19591         Roo.get(document).un('mousedown', this.collapseIf, this);
19592         Roo.get(document).un('mousewheel', this.collapseIf, this);
19593         if (!this.editable) {
19594             Roo.get(document).un('keydown', this.listKeyPress, this);
19595         }
19596         this.fireEvent('collapse', this);
19597     },
19598
19599     // private
19600     collapseIf : function(e){
19601         if(!e.within(this.wrap) && !e.within(this.list)){
19602             this.collapse();
19603         }
19604     },
19605
19606     /**
19607      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19608      */
19609     expand : function(){
19610         if(this.isExpanded() || !this.hasFocus){
19611             return;
19612         }
19613         this.list.alignTo(this.el, this.listAlign);
19614         this.list.show();
19615         Roo.get(document).on('mousedown', this.collapseIf, this);
19616         Roo.get(document).on('mousewheel', this.collapseIf, this);
19617         if (!this.editable) {
19618             Roo.get(document).on('keydown', this.listKeyPress, this);
19619         }
19620         
19621         this.fireEvent('expand', this);
19622     },
19623
19624     // private
19625     // Implements the default empty TriggerField.onTriggerClick function
19626     onTriggerClick : function(){
19627         if(this.disabled){
19628             return;
19629         }
19630         if(this.isExpanded()){
19631             this.collapse();
19632             if (!this.blockFocus) {
19633                 this.el.focus();
19634             }
19635             
19636         }else {
19637             this.hasFocus = true;
19638             if(this.triggerAction == 'all') {
19639                 this.doQuery(this.allQuery, true);
19640             } else {
19641                 this.doQuery(this.getRawValue());
19642             }
19643             if (!this.blockFocus) {
19644                 this.el.focus();
19645             }
19646         }
19647     },
19648     listKeyPress : function(e)
19649     {
19650         //Roo.log('listkeypress');
19651         // scroll to first matching element based on key pres..
19652         if (e.isSpecialKey()) {
19653             return false;
19654         }
19655         var k = String.fromCharCode(e.getKey()).toUpperCase();
19656         //Roo.log(k);
19657         var match  = false;
19658         var csel = this.view.getSelectedNodes();
19659         var cselitem = false;
19660         if (csel.length) {
19661             var ix = this.view.indexOf(csel[0]);
19662             cselitem  = this.store.getAt(ix);
19663             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19664                 cselitem = false;
19665             }
19666             
19667         }
19668         
19669         this.store.each(function(v) { 
19670             if (cselitem) {
19671                 // start at existing selection.
19672                 if (cselitem.id == v.id) {
19673                     cselitem = false;
19674                 }
19675                 return;
19676             }
19677                 
19678             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19679                 match = this.store.indexOf(v);
19680                 return false;
19681             }
19682         }, this);
19683         
19684         if (match === false) {
19685             return true; // no more action?
19686         }
19687         // scroll to?
19688         this.view.select(match);
19689         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19690         sn.scrollIntoView(sn.dom.parentNode, false);
19691     } 
19692
19693     /** 
19694     * @cfg {Boolean} grow 
19695     * @hide 
19696     */
19697     /** 
19698     * @cfg {Number} growMin 
19699     * @hide 
19700     */
19701     /** 
19702     * @cfg {Number} growMax 
19703     * @hide 
19704     */
19705     /**
19706      * @hide
19707      * @method autoSize
19708      */
19709 });/*
19710  * Copyright(c) 2010-2012, Roo J Solutions Limited
19711  *
19712  * Licence LGPL
19713  *
19714  */
19715
19716 /**
19717  * @class Roo.form.ComboBoxArray
19718  * @extends Roo.form.TextField
19719  * A facebook style adder... for lists of email / people / countries  etc...
19720  * pick multiple items from a combo box, and shows each one.
19721  *
19722  *  Fred [x]  Brian [x]  [Pick another |v]
19723  *
19724  *
19725  *  For this to work: it needs various extra information
19726  *    - normal combo problay has
19727  *      name, hiddenName
19728  *    + displayField, valueField
19729  *
19730  *    For our purpose...
19731  *
19732  *
19733  *   If we change from 'extends' to wrapping...
19734  *   
19735  *  
19736  *
19737  
19738  
19739  * @constructor
19740  * Create a new ComboBoxArray.
19741  * @param {Object} config Configuration options
19742  */
19743  
19744
19745 Roo.form.ComboBoxArray = function(config)
19746 {
19747     this.addEvents({
19748         /**
19749          * @event beforeremove
19750          * Fires before remove the value from the list
19751              * @param {Roo.form.ComboBoxArray} _self This combo box array
19752              * @param {Roo.form.ComboBoxArray.Item} item removed item
19753              */
19754         'beforeremove' : true,
19755         /**
19756          * @event remove
19757          * Fires when remove the value from the list
19758              * @param {Roo.form.ComboBoxArray} _self This combo box array
19759              * @param {Roo.form.ComboBoxArray.Item} item removed item
19760              */
19761         'remove' : true
19762         
19763         
19764     });
19765     
19766     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19767     
19768     this.items = new Roo.util.MixedCollection(false);
19769     
19770     // construct the child combo...
19771     
19772     
19773     
19774     
19775    
19776     
19777 }
19778
19779  
19780 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19781
19782     /**
19783      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19784      */
19785     
19786     lastData : false,
19787     
19788     // behavies liek a hiddne field
19789     inputType:      'hidden',
19790     /**
19791      * @cfg {Number} width The width of the box that displays the selected element
19792      */ 
19793     width:          300,
19794
19795     
19796     
19797     /**
19798      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19799      */
19800     name : false,
19801     /**
19802      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19803      */
19804     hiddenName : false,
19805     
19806     
19807     // private the array of items that are displayed..
19808     items  : false,
19809     // private - the hidden field el.
19810     hiddenEl : false,
19811     // private - the filed el..
19812     el : false,
19813     
19814     //validateValue : function() { return true; }, // all values are ok!
19815     //onAddClick: function() { },
19816     
19817     onRender : function(ct, position) 
19818     {
19819         
19820         // create the standard hidden element
19821         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19822         
19823         
19824         // give fake names to child combo;
19825         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19826         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19827         
19828         this.combo = Roo.factory(this.combo, Roo.form);
19829         this.combo.onRender(ct, position);
19830         if (typeof(this.combo.width) != 'undefined') {
19831             this.combo.onResize(this.combo.width,0);
19832         }
19833         
19834         this.combo.initEvents();
19835         
19836         // assigned so form know we need to do this..
19837         this.store          = this.combo.store;
19838         this.valueField     = this.combo.valueField;
19839         this.displayField   = this.combo.displayField ;
19840         
19841         
19842         this.combo.wrap.addClass('x-cbarray-grp');
19843         
19844         var cbwrap = this.combo.wrap.createChild(
19845             {tag: 'div', cls: 'x-cbarray-cb'},
19846             this.combo.el.dom
19847         );
19848         
19849              
19850         this.hiddenEl = this.combo.wrap.createChild({
19851             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19852         });
19853         this.el = this.combo.wrap.createChild({
19854             tag: 'input',  type:'hidden' , name: this.name, value : ''
19855         });
19856          //   this.el.dom.removeAttribute("name");
19857         
19858         
19859         this.outerWrap = this.combo.wrap;
19860         this.wrap = cbwrap;
19861         
19862         this.outerWrap.setWidth(this.width);
19863         this.outerWrap.dom.removeChild(this.el.dom);
19864         
19865         this.wrap.dom.appendChild(this.el.dom);
19866         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19867         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19868         
19869         this.combo.trigger.setStyle('position','relative');
19870         this.combo.trigger.setStyle('left', '0px');
19871         this.combo.trigger.setStyle('top', '2px');
19872         
19873         this.combo.el.setStyle('vertical-align', 'text-bottom');
19874         
19875         //this.trigger.setStyle('vertical-align', 'top');
19876         
19877         // this should use the code from combo really... on('add' ....)
19878         if (this.adder) {
19879             
19880         
19881             this.adder = this.outerWrap.createChild(
19882                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19883             var _t = this;
19884             this.adder.on('click', function(e) {
19885                 _t.fireEvent('adderclick', this, e);
19886             }, _t);
19887         }
19888         //var _t = this;
19889         //this.adder.on('click', this.onAddClick, _t);
19890         
19891         
19892         this.combo.on('select', function(cb, rec, ix) {
19893             this.addItem(rec.data);
19894             
19895             cb.setValue('');
19896             cb.el.dom.value = '';
19897             //cb.lastData = rec.data;
19898             // add to list
19899             
19900         }, this);
19901         
19902         
19903     },
19904     
19905     
19906     getName: function()
19907     {
19908         // returns hidden if it's set..
19909         if (!this.rendered) {return ''};
19910         return  this.hiddenName ? this.hiddenName : this.name;
19911         
19912     },
19913     
19914     
19915     onResize: function(w, h){
19916         
19917         return;
19918         // not sure if this is needed..
19919         //this.combo.onResize(w,h);
19920         
19921         if(typeof w != 'number'){
19922             // we do not handle it!?!?
19923             return;
19924         }
19925         var tw = this.combo.trigger.getWidth();
19926         tw += this.addicon ? this.addicon.getWidth() : 0;
19927         tw += this.editicon ? this.editicon.getWidth() : 0;
19928         var x = w - tw;
19929         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19930             
19931         this.combo.trigger.setStyle('left', '0px');
19932         
19933         if(this.list && this.listWidth === undefined){
19934             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19935             this.list.setWidth(lw);
19936             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19937         }
19938         
19939     
19940         
19941     },
19942     
19943     addItem: function(rec)
19944     {
19945         var valueField = this.combo.valueField;
19946         var displayField = this.combo.displayField;
19947         
19948         if (this.items.indexOfKey(rec[valueField]) > -1) {
19949             //console.log("GOT " + rec.data.id);
19950             return;
19951         }
19952         
19953         var x = new Roo.form.ComboBoxArray.Item({
19954             //id : rec[this.idField],
19955             data : rec,
19956             displayField : displayField ,
19957             tipField : displayField ,
19958             cb : this
19959         });
19960         // use the 
19961         this.items.add(rec[valueField],x);
19962         // add it before the element..
19963         this.updateHiddenEl();
19964         x.render(this.outerWrap, this.wrap.dom);
19965         // add the image handler..
19966     },
19967     
19968     updateHiddenEl : function()
19969     {
19970         this.validate();
19971         if (!this.hiddenEl) {
19972             return;
19973         }
19974         var ar = [];
19975         var idField = this.combo.valueField;
19976         
19977         this.items.each(function(f) {
19978             ar.push(f.data[idField]);
19979         });
19980         this.hiddenEl.dom.value = ar.join(',');
19981         this.validate();
19982     },
19983     
19984     reset : function()
19985     {
19986         this.items.clear();
19987         
19988         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19989            el.remove();
19990         });
19991         
19992         this.el.dom.value = '';
19993         if (this.hiddenEl) {
19994             this.hiddenEl.dom.value = '';
19995         }
19996         
19997     },
19998     getValue: function()
19999     {
20000         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20001     },
20002     setValue: function(v) // not a valid action - must use addItems..
20003     {
20004         
20005         this.reset();
20006          
20007         if (this.store.isLocal && (typeof(v) == 'string')) {
20008             // then we can use the store to find the values..
20009             // comma seperated at present.. this needs to allow JSON based encoding..
20010             this.hiddenEl.value  = v;
20011             var v_ar = [];
20012             Roo.each(v.split(','), function(k) {
20013                 Roo.log("CHECK " + this.valueField + ',' + k);
20014                 var li = this.store.query(this.valueField, k);
20015                 if (!li.length) {
20016                     return;
20017                 }
20018                 var add = {};
20019                 add[this.valueField] = k;
20020                 add[this.displayField] = li.item(0).data[this.displayField];
20021                 
20022                 this.addItem(add);
20023             }, this) 
20024              
20025         }
20026         if (typeof(v) == 'object' ) {
20027             // then let's assume it's an array of objects..
20028             Roo.each(v, function(l) {
20029                 this.addItem(l);
20030             }, this);
20031              
20032         }
20033         
20034         
20035     },
20036     setFromData: function(v)
20037     {
20038         // this recieves an object, if setValues is called.
20039         this.reset();
20040         this.el.dom.value = v[this.displayField];
20041         this.hiddenEl.dom.value = v[this.valueField];
20042         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20043             return;
20044         }
20045         var kv = v[this.valueField];
20046         var dv = v[this.displayField];
20047         kv = typeof(kv) != 'string' ? '' : kv;
20048         dv = typeof(dv) != 'string' ? '' : dv;
20049         
20050         
20051         var keys = kv.split(',');
20052         var display = dv.split(',');
20053         for (var i = 0 ; i < keys.length; i++) {
20054             
20055             add = {};
20056             add[this.valueField] = keys[i];
20057             add[this.displayField] = display[i];
20058             this.addItem(add);
20059         }
20060       
20061         
20062     },
20063     
20064     /**
20065      * Validates the combox array value
20066      * @return {Boolean} True if the value is valid, else false
20067      */
20068     validate : function(){
20069         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20070             this.clearInvalid();
20071             return true;
20072         }
20073         return false;
20074     },
20075     
20076     validateValue : function(value){
20077         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20078         
20079     },
20080     
20081     /*@
20082      * overide
20083      * 
20084      */
20085     isDirty : function() {
20086         if(this.disabled) {
20087             return false;
20088         }
20089         
20090         try {
20091             var d = Roo.decode(String(this.originalValue));
20092         } catch (e) {
20093             return String(this.getValue()) !== String(this.originalValue);
20094         }
20095         
20096         var originalValue = [];
20097         
20098         for (var i = 0; i < d.length; i++){
20099             originalValue.push(d[i][this.valueField]);
20100         }
20101         
20102         return String(this.getValue()) !== String(originalValue.join(','));
20103         
20104     }
20105     
20106 });
20107
20108
20109
20110 /**
20111  * @class Roo.form.ComboBoxArray.Item
20112  * @extends Roo.BoxComponent
20113  * A selected item in the list
20114  *  Fred [x]  Brian [x]  [Pick another |v]
20115  * 
20116  * @constructor
20117  * Create a new item.
20118  * @param {Object} config Configuration options
20119  */
20120  
20121 Roo.form.ComboBoxArray.Item = function(config) {
20122     config.id = Roo.id();
20123     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20124 }
20125
20126 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20127     data : {},
20128     cb: false,
20129     displayField : false,
20130     tipField : false,
20131     
20132     
20133     defaultAutoCreate : {
20134         tag: 'div',
20135         cls: 'x-cbarray-item',
20136         cn : [ 
20137             { tag: 'div' },
20138             {
20139                 tag: 'img',
20140                 width:16,
20141                 height : 16,
20142                 src : Roo.BLANK_IMAGE_URL ,
20143                 align: 'center'
20144             }
20145         ]
20146         
20147     },
20148     
20149  
20150     onRender : function(ct, position)
20151     {
20152         Roo.form.Field.superclass.onRender.call(this, ct, position);
20153         
20154         if(!this.el){
20155             var cfg = this.getAutoCreate();
20156             this.el = ct.createChild(cfg, position);
20157         }
20158         
20159         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20160         
20161         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20162             this.cb.renderer(this.data) :
20163             String.format('{0}',this.data[this.displayField]);
20164         
20165             
20166         this.el.child('div').dom.setAttribute('qtip',
20167                         String.format('{0}',this.data[this.tipField])
20168         );
20169         
20170         this.el.child('img').on('click', this.remove, this);
20171         
20172     },
20173    
20174     remove : function()
20175     {
20176         if(this.cb.disabled){
20177             return;
20178         }
20179         
20180         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20181             this.cb.items.remove(this);
20182             this.el.child('img').un('click', this.remove, this);
20183             this.el.remove();
20184             this.cb.updateHiddenEl();
20185
20186             this.cb.fireEvent('remove', this.cb, this);
20187         }
20188         
20189     }
20190 });/*
20191  * RooJS Library 1.1.1
20192  * Copyright(c) 2008-2011  Alan Knowles
20193  *
20194  * License - LGPL
20195  */
20196  
20197
20198 /**
20199  * @class Roo.form.ComboNested
20200  * @extends Roo.form.ComboBox
20201  * A combobox for that allows selection of nested items in a list,
20202  * eg.
20203  *
20204  *  Book
20205  *    -> red
20206  *    -> green
20207  *  Table
20208  *    -> square
20209  *      ->red
20210  *      ->green
20211  *    -> rectangle
20212  *      ->green
20213  *      
20214  * 
20215  * @constructor
20216  * Create a new ComboNested
20217  * @param {Object} config Configuration options
20218  */
20219 Roo.form.ComboNested = function(config){
20220     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20221     // should verify some data...
20222     // like
20223     // hiddenName = required..
20224     // displayField = required
20225     // valudField == required
20226     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20227     var _t = this;
20228     Roo.each(req, function(e) {
20229         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20230             throw "Roo.form.ComboNested : missing value for: " + e;
20231         }
20232     });
20233      
20234     
20235 };
20236
20237 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20238    
20239     /*
20240      * @config {Number} max Number of columns to show
20241      */
20242     
20243     maxColumns : 3,
20244    
20245     list : null, // the outermost div..
20246     innerLists : null, // the
20247     views : null,
20248     stores : null,
20249     // private
20250     onRender : function(ct, position)
20251     {
20252         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20253         
20254         if(this.hiddenName){
20255             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20256                     'before', true);
20257             this.hiddenField.value =
20258                 this.hiddenValue !== undefined ? this.hiddenValue :
20259                 this.value !== undefined ? this.value : '';
20260
20261             // prevent input submission
20262             this.el.dom.removeAttribute('name');
20263              
20264              
20265         }
20266         
20267         if(Roo.isGecko){
20268             this.el.dom.setAttribute('autocomplete', 'off');
20269         }
20270
20271         var cls = 'x-combo-list';
20272
20273         this.list = new Roo.Layer({
20274             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20275         });
20276
20277         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20278         this.list.setWidth(lw);
20279         this.list.swallowEvent('mousewheel');
20280         this.assetHeight = 0;
20281
20282         if(this.title){
20283             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20284             this.assetHeight += this.header.getHeight();
20285         }
20286         this.innerLists = [];
20287         this.views = [];
20288         this.stores = [];
20289         for (var i =0 ; i < this.maxColumns; i++) {
20290             this.onRenderList( cls, i);
20291         }
20292         
20293         // always needs footer, as we are going to have an 'OK' button.
20294         this.footer = this.list.createChild({cls:cls+'-ft'});
20295         this.pageTb = new Roo.Toolbar(this.footer);  
20296         var _this = this;
20297         this.pageTb.add(  {
20298             
20299             text: 'Done',
20300             handler: function()
20301             {
20302                 _this.collapse();
20303             }
20304         });
20305         
20306         if ( this.allowBlank && !this.disableClear) {
20307             
20308             this.pageTb.add(new Roo.Toolbar.Fill(), {
20309                 cls: 'x-btn-icon x-btn-clear',
20310                 text: '&#160;',
20311                 handler: function()
20312                 {
20313                     _this.collapse();
20314                     _this.clearValue();
20315                     _this.onSelect(false, -1);
20316                 }
20317             });
20318         }
20319         if (this.footer) {
20320             this.assetHeight += this.footer.getHeight();
20321         }
20322         
20323     },
20324     onRenderList : function (  cls, i)
20325     {
20326         
20327         var lw = Math.floor(
20328                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20329         );
20330         
20331         this.list.setWidth(lw); // default to '1'
20332
20333         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20334         //il.on('mouseover', this.onViewOver, this, { list:  i });
20335         //il.on('mousemove', this.onViewMove, this, { list:  i });
20336         il.setWidth(lw);
20337         il.setStyle({ 'overflow-x' : 'hidden'});
20338
20339         if(!this.tpl){
20340             this.tpl = new Roo.Template({
20341                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20342                 isEmpty: function (value, allValues) {
20343                     //Roo.log(value);
20344                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20345                     return dl ? 'has-children' : 'no-children'
20346                 }
20347             });
20348         }
20349         
20350         var store  = this.store;
20351         if (i > 0) {
20352             store  = new Roo.data.SimpleStore({
20353                 //fields : this.store.reader.meta.fields,
20354                 reader : this.store.reader,
20355                 data : [ ]
20356             });
20357         }
20358         this.stores[i]  = store;
20359                 
20360         
20361         
20362         var view = this.views[i] = new Roo.View(
20363             il,
20364             this.tpl,
20365             {
20366                 singleSelect:true,
20367                 store: store,
20368                 selectedClass: this.selectedClass
20369             }
20370         );
20371         view.getEl().setWidth(lw);
20372         view.getEl().setStyle({
20373             position: i < 1 ? 'relative' : 'absolute',
20374             top: 0,
20375             left: (i * lw ) + 'px',
20376             display : i > 0 ? 'none' : 'block'
20377         });
20378         view.on('selectionchange', this.onSelectChange, this, {list : i });
20379         view.on('dblclick', this.onDoubleClick, this, {list : i });
20380         //view.on('click', this.onViewClick, this, { list : i });
20381
20382         store.on('beforeload', this.onBeforeLoad, this);
20383         store.on('load',  this.onLoad, this, { list  : i});
20384         store.on('loadexception', this.onLoadException, this);
20385
20386         // hide the other vies..
20387         
20388         
20389         
20390     },
20391     onResize : function()  {},
20392     
20393     restrictHeight : function()
20394     {
20395         var mh = 0;
20396         Roo.each(this.innerLists, function(il,i) {
20397             var el = this.views[i].getEl();
20398             el.dom.style.height = '';
20399             var inner = el.dom;
20400             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20401             // only adjust heights on other ones..
20402             if (i < 1) {
20403                 
20404                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20405                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20406                 mh = Math.max(el.getHeight(), mh);
20407             }
20408             
20409             
20410         }, this);
20411         
20412         this.list.beginUpdate();
20413         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20414         this.list.alignTo(this.el, this.listAlign);
20415         this.list.endUpdate();
20416         
20417     },
20418      
20419     
20420     // -- store handlers..
20421     // private
20422     onBeforeLoad : function()
20423     {
20424         if(!this.hasFocus){
20425             return;
20426         }
20427         this.innerLists[0].update(this.loadingText ?
20428                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20429         this.restrictHeight();
20430         this.selectedIndex = -1;
20431     },
20432     // private
20433     onLoad : function(a,b,c,d)
20434     {
20435         
20436         if(!this.hasFocus){
20437             return;
20438         }
20439         
20440         if(this.store.getCount() > 0) {
20441             this.expand();
20442             this.restrictHeight();   
20443         } else {
20444             this.onEmptyResults();
20445         }
20446         /*
20447         this.stores[1].loadData([]);
20448         this.stores[2].loadData([]);
20449         this.views
20450         */    
20451     
20452         //this.el.focus();
20453     },
20454     
20455     
20456     // private
20457     onLoadException : function()
20458     {
20459         this.collapse();
20460         Roo.log(this.store.reader.jsonData);
20461         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20462             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20463         }
20464         
20465         
20466     } ,
20467      
20468      
20469
20470     onSelectChange : function (view, sels, opts )
20471     {
20472         var ix = view.getSelectedIndexes();
20473         
20474         
20475         if (opts.list > this.maxColumns - 2) {
20476              
20477             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20478             return;
20479         }
20480         
20481         if (!ix.length) {
20482             this.setFromData({});
20483             this.stores[opts.list+1].loadData( [] );
20484             return;
20485         }
20486         
20487         var rec = view.store.getAt(ix[0]);
20488         this.setFromData(rec.data);
20489         
20490         var lw = Math.floor(
20491                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20492         );
20493         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20494         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20495         this.stores[opts.list+1].loadData( data );
20496         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20497         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20498         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20499         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20500     },
20501     onDoubleClick : function()
20502     {
20503         this.collapse(); //??
20504     },
20505     
20506      
20507     
20508     findRecord : function (prop,value)
20509     {
20510         return this.findRecordInStore(this.store, prop,value);
20511     },
20512     
20513      // private
20514     findRecordInStore : function(store, prop, value)
20515     {
20516         var cstore = new Roo.data.SimpleStore({
20517             //fields : this.store.reader.meta.fields, // we need array reader.. for
20518             reader : this.store.reader,
20519             data : [ ]
20520         });
20521         var _this = this;
20522         var record  = false;
20523         if(store.getCount() > 0){
20524            store.each(function(r){
20525                 if(r.data[prop] == value){
20526                     record = r;
20527                     return false;
20528                 }
20529                 if (r.data.cn && r.data.cn.length) {
20530                     cstore.loadData( r.data.cn);
20531                     var cret = _this.findRecordInStore(cstore, prop, value);
20532                     if (cret !== false) {
20533                         record = cret;
20534                         return false;
20535                     }
20536                 }
20537                 
20538                 return true;
20539             });
20540         }
20541         return record;
20542     }
20543     
20544     
20545     
20546     
20547 });/*
20548  * Based on:
20549  * Ext JS Library 1.1.1
20550  * Copyright(c) 2006-2007, Ext JS, LLC.
20551  *
20552  * Originally Released Under LGPL - original licence link has changed is not relivant.
20553  *
20554  * Fork - LGPL
20555  * <script type="text/javascript">
20556  */
20557 /**
20558  * @class Roo.form.Checkbox
20559  * @extends Roo.form.Field
20560  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20561  * @constructor
20562  * Creates a new Checkbox
20563  * @param {Object} config Configuration options
20564  */
20565 Roo.form.Checkbox = function(config){
20566     Roo.form.Checkbox.superclass.constructor.call(this, config);
20567     this.addEvents({
20568         /**
20569          * @event check
20570          * Fires when the checkbox is checked or unchecked.
20571              * @param {Roo.form.Checkbox} this This checkbox
20572              * @param {Boolean} checked The new checked value
20573              */
20574         check : true
20575     });
20576 };
20577
20578 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20579     /**
20580      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20581      */
20582     focusClass : undefined,
20583     /**
20584      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20585      */
20586     fieldClass: "x-form-field",
20587     /**
20588      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20589      */
20590     checked: false,
20591     /**
20592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20593      * {tag: "input", type: "checkbox", autocomplete: "off"})
20594      */
20595     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20596     /**
20597      * @cfg {String} boxLabel The text that appears beside the checkbox
20598      */
20599     boxLabel : "",
20600     /**
20601      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20602      */  
20603     inputValue : '1',
20604     /**
20605      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20606      */
20607      valueOff: '0', // value when not checked..
20608
20609     actionMode : 'viewEl', 
20610     //
20611     // private
20612     itemCls : 'x-menu-check-item x-form-item',
20613     groupClass : 'x-menu-group-item',
20614     inputType : 'hidden',
20615     
20616     
20617     inSetChecked: false, // check that we are not calling self...
20618     
20619     inputElement: false, // real input element?
20620     basedOn: false, // ????
20621     
20622     isFormField: true, // not sure where this is needed!!!!
20623
20624     onResize : function(){
20625         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20626         if(!this.boxLabel){
20627             this.el.alignTo(this.wrap, 'c-c');
20628         }
20629     },
20630
20631     initEvents : function(){
20632         Roo.form.Checkbox.superclass.initEvents.call(this);
20633         this.el.on("click", this.onClick,  this);
20634         this.el.on("change", this.onClick,  this);
20635     },
20636
20637
20638     getResizeEl : function(){
20639         return this.wrap;
20640     },
20641
20642     getPositionEl : function(){
20643         return this.wrap;
20644     },
20645
20646     // private
20647     onRender : function(ct, position){
20648         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20649         /*
20650         if(this.inputValue !== undefined){
20651             this.el.dom.value = this.inputValue;
20652         }
20653         */
20654         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20655         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20656         var viewEl = this.wrap.createChild({ 
20657             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20658         this.viewEl = viewEl;   
20659         this.wrap.on('click', this.onClick,  this); 
20660         
20661         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20662         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20663         
20664         
20665         
20666         if(this.boxLabel){
20667             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20668         //    viewEl.on('click', this.onClick,  this); 
20669         }
20670         //if(this.checked){
20671             this.setChecked(this.checked);
20672         //}else{
20673             //this.checked = this.el.dom;
20674         //}
20675
20676     },
20677
20678     // private
20679     initValue : Roo.emptyFn,
20680
20681     /**
20682      * Returns the checked state of the checkbox.
20683      * @return {Boolean} True if checked, else false
20684      */
20685     getValue : function(){
20686         if(this.el){
20687             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20688         }
20689         return this.valueOff;
20690         
20691     },
20692
20693         // private
20694     onClick : function(){ 
20695         if (this.disabled) {
20696             return;
20697         }
20698         this.setChecked(!this.checked);
20699
20700         //if(this.el.dom.checked != this.checked){
20701         //    this.setValue(this.el.dom.checked);
20702        // }
20703     },
20704
20705     /**
20706      * Sets the checked state of the checkbox.
20707      * On is always based on a string comparison between inputValue and the param.
20708      * @param {Boolean/String} value - the value to set 
20709      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20710      */
20711     setValue : function(v,suppressEvent){
20712         
20713         
20714         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20715         //if(this.el && this.el.dom){
20716         //    this.el.dom.checked = this.checked;
20717         //    this.el.dom.defaultChecked = this.checked;
20718         //}
20719         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20720         //this.fireEvent("check", this, this.checked);
20721     },
20722     // private..
20723     setChecked : function(state,suppressEvent)
20724     {
20725         if (this.inSetChecked) {
20726             this.checked = state;
20727             return;
20728         }
20729         
20730     
20731         if(this.wrap){
20732             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20733         }
20734         this.checked = state;
20735         if(suppressEvent !== true){
20736             this.fireEvent('check', this, state);
20737         }
20738         this.inSetChecked = true;
20739         this.el.dom.value = state ? this.inputValue : this.valueOff;
20740         this.inSetChecked = false;
20741         
20742     },
20743     // handle setting of hidden value by some other method!!?!?
20744     setFromHidden: function()
20745     {
20746         if(!this.el){
20747             return;
20748         }
20749         //console.log("SET FROM HIDDEN");
20750         //alert('setFrom hidden');
20751         this.setValue(this.el.dom.value);
20752     },
20753     
20754     onDestroy : function()
20755     {
20756         if(this.viewEl){
20757             Roo.get(this.viewEl).remove();
20758         }
20759          
20760         Roo.form.Checkbox.superclass.onDestroy.call(this);
20761     },
20762     
20763     setBoxLabel : function(str)
20764     {
20765         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20766     }
20767
20768 });/*
20769  * Based on:
20770  * Ext JS Library 1.1.1
20771  * Copyright(c) 2006-2007, Ext JS, LLC.
20772  *
20773  * Originally Released Under LGPL - original licence link has changed is not relivant.
20774  *
20775  * Fork - LGPL
20776  * <script type="text/javascript">
20777  */
20778  
20779 /**
20780  * @class Roo.form.Radio
20781  * @extends Roo.form.Checkbox
20782  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20783  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20784  * @constructor
20785  * Creates a new Radio
20786  * @param {Object} config Configuration options
20787  */
20788 Roo.form.Radio = function(){
20789     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20790 };
20791 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20792     inputType: 'radio',
20793
20794     /**
20795      * If this radio is part of a group, it will return the selected value
20796      * @return {String}
20797      */
20798     getGroupValue : function(){
20799         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20800     },
20801     
20802     
20803     onRender : function(ct, position){
20804         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20805         
20806         if(this.inputValue !== undefined){
20807             this.el.dom.value = this.inputValue;
20808         }
20809          
20810         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20811         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20812         //var viewEl = this.wrap.createChild({ 
20813         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20814         //this.viewEl = viewEl;   
20815         //this.wrap.on('click', this.onClick,  this); 
20816         
20817         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20818         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20819         
20820         
20821         
20822         if(this.boxLabel){
20823             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20824         //    viewEl.on('click', this.onClick,  this); 
20825         }
20826          if(this.checked){
20827             this.el.dom.checked =   'checked' ;
20828         }
20829          
20830     } 
20831     
20832     
20833 });//<script type="text/javascript">
20834
20835 /*
20836  * Based  Ext JS Library 1.1.1
20837  * Copyright(c) 2006-2007, Ext JS, LLC.
20838  * LGPL
20839  *
20840  */
20841  
20842 /**
20843  * @class Roo.HtmlEditorCore
20844  * @extends Roo.Component
20845  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20846  *
20847  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20848  */
20849
20850 Roo.HtmlEditorCore = function(config){
20851     
20852     
20853     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20854     
20855     
20856     this.addEvents({
20857         /**
20858          * @event initialize
20859          * Fires when the editor is fully initialized (including the iframe)
20860          * @param {Roo.HtmlEditorCore} this
20861          */
20862         initialize: true,
20863         /**
20864          * @event activate
20865          * Fires when the editor is first receives the focus. Any insertion must wait
20866          * until after this event.
20867          * @param {Roo.HtmlEditorCore} this
20868          */
20869         activate: true,
20870          /**
20871          * @event beforesync
20872          * Fires before the textarea is updated with content from the editor iframe. Return false
20873          * to cancel the sync.
20874          * @param {Roo.HtmlEditorCore} this
20875          * @param {String} html
20876          */
20877         beforesync: true,
20878          /**
20879          * @event beforepush
20880          * Fires before the iframe editor is updated with content from the textarea. Return false
20881          * to cancel the push.
20882          * @param {Roo.HtmlEditorCore} this
20883          * @param {String} html
20884          */
20885         beforepush: true,
20886          /**
20887          * @event sync
20888          * Fires when the textarea is updated with content from the editor iframe.
20889          * @param {Roo.HtmlEditorCore} this
20890          * @param {String} html
20891          */
20892         sync: true,
20893          /**
20894          * @event push
20895          * Fires when the iframe editor is updated with content from the textarea.
20896          * @param {Roo.HtmlEditorCore} this
20897          * @param {String} html
20898          */
20899         push: true,
20900         
20901         /**
20902          * @event editorevent
20903          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20904          * @param {Roo.HtmlEditorCore} this
20905          */
20906         editorevent: true
20907         
20908     });
20909     
20910     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20911     
20912     // defaults : white / black...
20913     this.applyBlacklists();
20914     
20915     
20916     
20917 };
20918
20919
20920 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20921
20922
20923      /**
20924      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20925      */
20926     
20927     owner : false,
20928     
20929      /**
20930      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20931      *                        Roo.resizable.
20932      */
20933     resizable : false,
20934      /**
20935      * @cfg {Number} height (in pixels)
20936      */   
20937     height: 300,
20938    /**
20939      * @cfg {Number} width (in pixels)
20940      */   
20941     width: 500,
20942     
20943     /**
20944      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20945      * 
20946      */
20947     stylesheets: false,
20948     
20949     // id of frame..
20950     frameId: false,
20951     
20952     // private properties
20953     validationEvent : false,
20954     deferHeight: true,
20955     initialized : false,
20956     activated : false,
20957     sourceEditMode : false,
20958     onFocus : Roo.emptyFn,
20959     iframePad:3,
20960     hideMode:'offsets',
20961     
20962     clearUp: true,
20963     
20964     // blacklist + whitelisted elements..
20965     black: false,
20966     white: false,
20967      
20968     bodyCls : '',
20969
20970     /**
20971      * Protected method that will not generally be called directly. It
20972      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20973      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20974      */
20975     getDocMarkup : function(){
20976         // body styles..
20977         var st = '';
20978         
20979         // inherit styels from page...?? 
20980         if (this.stylesheets === false) {
20981             
20982             Roo.get(document.head).select('style').each(function(node) {
20983                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20984             });
20985             
20986             Roo.get(document.head).select('link').each(function(node) { 
20987                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20988             });
20989             
20990         } else if (!this.stylesheets.length) {
20991                 // simple..
20992                 st = '<style type="text/css">' +
20993                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20994                    '</style>';
20995         } else { 
20996             st = '<style type="text/css">' +
20997                     this.stylesheets +
20998                 '</style>';
20999         }
21000         
21001         st +=  '<style type="text/css">' +
21002             'IMG { cursor: pointer } ' +
21003         '</style>';
21004
21005         var cls = 'roo-htmleditor-body';
21006         
21007         if(this.bodyCls.length){
21008             cls += ' ' + this.bodyCls;
21009         }
21010         
21011         return '<html><head>' + st  +
21012             //<style type="text/css">' +
21013             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21014             //'</style>' +
21015             ' </head><body class="' +  cls + '"></body></html>';
21016     },
21017
21018     // private
21019     onRender : function(ct, position)
21020     {
21021         var _t = this;
21022         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21023         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21024         
21025         
21026         this.el.dom.style.border = '0 none';
21027         this.el.dom.setAttribute('tabIndex', -1);
21028         this.el.addClass('x-hidden hide');
21029         
21030         
21031         
21032         if(Roo.isIE){ // fix IE 1px bogus margin
21033             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21034         }
21035        
21036         
21037         this.frameId = Roo.id();
21038         
21039          
21040         
21041         var iframe = this.owner.wrap.createChild({
21042             tag: 'iframe',
21043             cls: 'form-control', // bootstrap..
21044             id: this.frameId,
21045             name: this.frameId,
21046             frameBorder : 'no',
21047             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21048         }, this.el
21049         );
21050         
21051         
21052         this.iframe = iframe.dom;
21053
21054          this.assignDocWin();
21055         
21056         this.doc.designMode = 'on';
21057        
21058         this.doc.open();
21059         this.doc.write(this.getDocMarkup());
21060         this.doc.close();
21061
21062         
21063         var task = { // must defer to wait for browser to be ready
21064             run : function(){
21065                 //console.log("run task?" + this.doc.readyState);
21066                 this.assignDocWin();
21067                 if(this.doc.body || this.doc.readyState == 'complete'){
21068                     try {
21069                         this.doc.designMode="on";
21070                     } catch (e) {
21071                         return;
21072                     }
21073                     Roo.TaskMgr.stop(task);
21074                     this.initEditor.defer(10, this);
21075                 }
21076             },
21077             interval : 10,
21078             duration: 10000,
21079             scope: this
21080         };
21081         Roo.TaskMgr.start(task);
21082
21083     },
21084
21085     // private
21086     onResize : function(w, h)
21087     {
21088          Roo.log('resize: ' +w + ',' + h );
21089         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21090         if(!this.iframe){
21091             return;
21092         }
21093         if(typeof w == 'number'){
21094             
21095             this.iframe.style.width = w + 'px';
21096         }
21097         if(typeof h == 'number'){
21098             
21099             this.iframe.style.height = h + 'px';
21100             if(this.doc){
21101                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21102             }
21103         }
21104         
21105     },
21106
21107     /**
21108      * Toggles the editor between standard and source edit mode.
21109      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21110      */
21111     toggleSourceEdit : function(sourceEditMode){
21112         
21113         this.sourceEditMode = sourceEditMode === true;
21114         
21115         if(this.sourceEditMode){
21116  
21117             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21118             
21119         }else{
21120             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21121             //this.iframe.className = '';
21122             this.deferFocus();
21123         }
21124         //this.setSize(this.owner.wrap.getSize());
21125         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21126     },
21127
21128     
21129   
21130
21131     /**
21132      * Protected method that will not generally be called directly. If you need/want
21133      * custom HTML cleanup, this is the method you should override.
21134      * @param {String} html The HTML to be cleaned
21135      * return {String} The cleaned HTML
21136      */
21137     cleanHtml : function(html){
21138         html = String(html);
21139         if(html.length > 5){
21140             if(Roo.isSafari){ // strip safari nonsense
21141                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21142             }
21143         }
21144         if(html == '&nbsp;'){
21145             html = '';
21146         }
21147         return html;
21148     },
21149
21150     /**
21151      * HTML Editor -> Textarea
21152      * Protected method that will not generally be called directly. Syncs the contents
21153      * of the editor iframe with the textarea.
21154      */
21155     syncValue : function(){
21156         if(this.initialized){
21157             var bd = (this.doc.body || this.doc.documentElement);
21158             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21159             var html = bd.innerHTML;
21160             if(Roo.isSafari){
21161                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21162                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21163                 if(m && m[1]){
21164                     html = '<div style="'+m[0]+'">' + html + '</div>';
21165                 }
21166             }
21167             html = this.cleanHtml(html);
21168             // fix up the special chars.. normaly like back quotes in word...
21169             // however we do not want to do this with chinese..
21170             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21171                 
21172                 var cc = match.charCodeAt();
21173
21174                 // Get the character value, handling surrogate pairs
21175                 if (match.length == 2) {
21176                     // It's a surrogate pair, calculate the Unicode code point
21177                     var high = match.charCodeAt(0) - 0xD800;
21178                     var low  = match.charCodeAt(1) - 0xDC00;
21179                     cc = (high * 0x400) + low + 0x10000;
21180                 }  else if (
21181                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21182                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21183                     (cc >= 0xf900 && cc < 0xfb00 )
21184                 ) {
21185                         return match;
21186                 }  
21187          
21188                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21189                 return "&#" + cc + ";";
21190                 
21191                 
21192             });
21193             
21194             
21195              
21196             if(this.owner.fireEvent('beforesync', this, html) !== false){
21197                 this.el.dom.value = html;
21198                 this.owner.fireEvent('sync', this, html);
21199             }
21200         }
21201     },
21202
21203     /**
21204      * Protected method that will not generally be called directly. Pushes the value of the textarea
21205      * into the iframe editor.
21206      */
21207     pushValue : function(){
21208         if(this.initialized){
21209             var v = this.el.dom.value.trim();
21210             
21211 //            if(v.length < 1){
21212 //                v = '&#160;';
21213 //            }
21214             
21215             if(this.owner.fireEvent('beforepush', this, v) !== false){
21216                 var d = (this.doc.body || this.doc.documentElement);
21217                 d.innerHTML = v;
21218                 this.cleanUpPaste();
21219                 this.el.dom.value = d.innerHTML;
21220                 this.owner.fireEvent('push', this, v);
21221             }
21222         }
21223     },
21224
21225     // private
21226     deferFocus : function(){
21227         this.focus.defer(10, this);
21228     },
21229
21230     // doc'ed in Field
21231     focus : function(){
21232         if(this.win && !this.sourceEditMode){
21233             this.win.focus();
21234         }else{
21235             this.el.focus();
21236         }
21237     },
21238     
21239     assignDocWin: function()
21240     {
21241         var iframe = this.iframe;
21242         
21243          if(Roo.isIE){
21244             this.doc = iframe.contentWindow.document;
21245             this.win = iframe.contentWindow;
21246         } else {
21247 //            if (!Roo.get(this.frameId)) {
21248 //                return;
21249 //            }
21250 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21251 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21252             
21253             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21254                 return;
21255             }
21256             
21257             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21258             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21259         }
21260     },
21261     
21262     // private
21263     initEditor : function(){
21264         //console.log("INIT EDITOR");
21265         this.assignDocWin();
21266         
21267         
21268         
21269         this.doc.designMode="on";
21270         this.doc.open();
21271         this.doc.write(this.getDocMarkup());
21272         this.doc.close();
21273         
21274         var dbody = (this.doc.body || this.doc.documentElement);
21275         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21276         // this copies styles from the containing element into thsi one..
21277         // not sure why we need all of this..
21278         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21279         
21280         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21281         //ss['background-attachment'] = 'fixed'; // w3c
21282         dbody.bgProperties = 'fixed'; // ie
21283         //Roo.DomHelper.applyStyles(dbody, ss);
21284         Roo.EventManager.on(this.doc, {
21285             //'mousedown': this.onEditorEvent,
21286             'mouseup': this.onEditorEvent,
21287             'dblclick': this.onEditorEvent,
21288             'click': this.onEditorEvent,
21289             'keyup': this.onEditorEvent,
21290             buffer:100,
21291             scope: this
21292         });
21293         if(Roo.isGecko){
21294             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21295         }
21296         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21297             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21298         }
21299         this.initialized = true;
21300
21301         this.owner.fireEvent('initialize', this);
21302         this.pushValue();
21303     },
21304
21305     // private
21306     onDestroy : function(){
21307         
21308         
21309         
21310         if(this.rendered){
21311             
21312             //for (var i =0; i < this.toolbars.length;i++) {
21313             //    // fixme - ask toolbars for heights?
21314             //    this.toolbars[i].onDestroy();
21315            // }
21316             
21317             //this.wrap.dom.innerHTML = '';
21318             //this.wrap.remove();
21319         }
21320     },
21321
21322     // private
21323     onFirstFocus : function(){
21324         
21325         this.assignDocWin();
21326         
21327         
21328         this.activated = true;
21329          
21330     
21331         if(Roo.isGecko){ // prevent silly gecko errors
21332             this.win.focus();
21333             var s = this.win.getSelection();
21334             if(!s.focusNode || s.focusNode.nodeType != 3){
21335                 var r = s.getRangeAt(0);
21336                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21337                 r.collapse(true);
21338                 this.deferFocus();
21339             }
21340             try{
21341                 this.execCmd('useCSS', true);
21342                 this.execCmd('styleWithCSS', false);
21343             }catch(e){}
21344         }
21345         this.owner.fireEvent('activate', this);
21346     },
21347
21348     // private
21349     adjustFont: function(btn){
21350         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21351         //if(Roo.isSafari){ // safari
21352         //    adjust *= 2;
21353        // }
21354         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21355         if(Roo.isSafari){ // safari
21356             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21357             v =  (v < 10) ? 10 : v;
21358             v =  (v > 48) ? 48 : v;
21359             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21360             
21361         }
21362         
21363         
21364         v = Math.max(1, v+adjust);
21365         
21366         this.execCmd('FontSize', v  );
21367     },
21368
21369     onEditorEvent : function(e)
21370     {
21371         this.owner.fireEvent('editorevent', this, e);
21372       //  this.updateToolbar();
21373         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21374     },
21375
21376     insertTag : function(tg)
21377     {
21378         // could be a bit smarter... -> wrap the current selected tRoo..
21379         if (tg.toLowerCase() == 'span' ||
21380             tg.toLowerCase() == 'code' ||
21381             tg.toLowerCase() == 'sup' ||
21382             tg.toLowerCase() == 'sub' 
21383             ) {
21384             
21385             range = this.createRange(this.getSelection());
21386             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21387             wrappingNode.appendChild(range.extractContents());
21388             range.insertNode(wrappingNode);
21389
21390             return;
21391             
21392             
21393             
21394         }
21395         this.execCmd("formatblock",   tg);
21396         
21397     },
21398     
21399     insertText : function(txt)
21400     {
21401         
21402         
21403         var range = this.createRange();
21404         range.deleteContents();
21405                //alert(Sender.getAttribute('label'));
21406                
21407         range.insertNode(this.doc.createTextNode(txt));
21408     } ,
21409     
21410      
21411
21412     /**
21413      * Executes a Midas editor command on the editor document and performs necessary focus and
21414      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21415      * @param {String} cmd The Midas command
21416      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21417      */
21418     relayCmd : function(cmd, value){
21419         this.win.focus();
21420         this.execCmd(cmd, value);
21421         this.owner.fireEvent('editorevent', this);
21422         //this.updateToolbar();
21423         this.owner.deferFocus();
21424     },
21425
21426     /**
21427      * Executes a Midas editor command directly on the editor document.
21428      * For visual commands, you should use {@link #relayCmd} instead.
21429      * <b>This should only be called after the editor is initialized.</b>
21430      * @param {String} cmd The Midas command
21431      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21432      */
21433     execCmd : function(cmd, value){
21434         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21435         this.syncValue();
21436     },
21437  
21438  
21439    
21440     /**
21441      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21442      * to insert tRoo.
21443      * @param {String} text | dom node.. 
21444      */
21445     insertAtCursor : function(text)
21446     {
21447         
21448         if(!this.activated){
21449             return;
21450         }
21451         /*
21452         if(Roo.isIE){
21453             this.win.focus();
21454             var r = this.doc.selection.createRange();
21455             if(r){
21456                 r.collapse(true);
21457                 r.pasteHTML(text);
21458                 this.syncValue();
21459                 this.deferFocus();
21460             
21461             }
21462             return;
21463         }
21464         */
21465         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21466             this.win.focus();
21467             
21468             
21469             // from jquery ui (MIT licenced)
21470             var range, node;
21471             var win = this.win;
21472             
21473             if (win.getSelection && win.getSelection().getRangeAt) {
21474                 range = win.getSelection().getRangeAt(0);
21475                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21476                 range.insertNode(node);
21477             } else if (win.document.selection && win.document.selection.createRange) {
21478                 // no firefox support
21479                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21480                 win.document.selection.createRange().pasteHTML(txt);
21481             } else {
21482                 // no firefox support
21483                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21484                 this.execCmd('InsertHTML', txt);
21485             } 
21486             
21487             this.syncValue();
21488             
21489             this.deferFocus();
21490         }
21491     },
21492  // private
21493     mozKeyPress : function(e){
21494         if(e.ctrlKey){
21495             var c = e.getCharCode(), cmd;
21496           
21497             if(c > 0){
21498                 c = String.fromCharCode(c).toLowerCase();
21499                 switch(c){
21500                     case 'b':
21501                         cmd = 'bold';
21502                         break;
21503                     case 'i':
21504                         cmd = 'italic';
21505                         break;
21506                     
21507                     case 'u':
21508                         cmd = 'underline';
21509                         break;
21510                     
21511                     case 'v':
21512                         this.cleanUpPaste.defer(100, this);
21513                         return;
21514                         
21515                 }
21516                 if(cmd){
21517                     this.win.focus();
21518                     this.execCmd(cmd);
21519                     this.deferFocus();
21520                     e.preventDefault();
21521                 }
21522                 
21523             }
21524         }
21525     },
21526
21527     // private
21528     fixKeys : function(){ // load time branching for fastest keydown performance
21529         if(Roo.isIE){
21530             return function(e){
21531                 var k = e.getKey(), r;
21532                 if(k == e.TAB){
21533                     e.stopEvent();
21534                     r = this.doc.selection.createRange();
21535                     if(r){
21536                         r.collapse(true);
21537                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21538                         this.deferFocus();
21539                     }
21540                     return;
21541                 }
21542                 
21543                 if(k == e.ENTER){
21544                     r = this.doc.selection.createRange();
21545                     if(r){
21546                         var target = r.parentElement();
21547                         if(!target || target.tagName.toLowerCase() != 'li'){
21548                             e.stopEvent();
21549                             r.pasteHTML('<br />');
21550                             r.collapse(false);
21551                             r.select();
21552                         }
21553                     }
21554                 }
21555                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21556                     this.cleanUpPaste.defer(100, this);
21557                     return;
21558                 }
21559                 
21560                 
21561             };
21562         }else if(Roo.isOpera){
21563             return function(e){
21564                 var k = e.getKey();
21565                 if(k == e.TAB){
21566                     e.stopEvent();
21567                     this.win.focus();
21568                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21569                     this.deferFocus();
21570                 }
21571                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21572                     this.cleanUpPaste.defer(100, this);
21573                     return;
21574                 }
21575                 
21576             };
21577         }else if(Roo.isSafari){
21578             return function(e){
21579                 var k = e.getKey();
21580                 
21581                 if(k == e.TAB){
21582                     e.stopEvent();
21583                     this.execCmd('InsertText','\t');
21584                     this.deferFocus();
21585                     return;
21586                 }
21587                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21588                     this.cleanUpPaste.defer(100, this);
21589                     return;
21590                 }
21591                 
21592              };
21593         }
21594     }(),
21595     
21596     getAllAncestors: function()
21597     {
21598         var p = this.getSelectedNode();
21599         var a = [];
21600         if (!p) {
21601             a.push(p); // push blank onto stack..
21602             p = this.getParentElement();
21603         }
21604         
21605         
21606         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21607             a.push(p);
21608             p = p.parentNode;
21609         }
21610         a.push(this.doc.body);
21611         return a;
21612     },
21613     lastSel : false,
21614     lastSelNode : false,
21615     
21616     
21617     getSelection : function() 
21618     {
21619         this.assignDocWin();
21620         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21621     },
21622     
21623     getSelectedNode: function() 
21624     {
21625         // this may only work on Gecko!!!
21626         
21627         // should we cache this!!!!
21628         
21629         
21630         
21631          
21632         var range = this.createRange(this.getSelection()).cloneRange();
21633         
21634         if (Roo.isIE) {
21635             var parent = range.parentElement();
21636             while (true) {
21637                 var testRange = range.duplicate();
21638                 testRange.moveToElementText(parent);
21639                 if (testRange.inRange(range)) {
21640                     break;
21641                 }
21642                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21643                     break;
21644                 }
21645                 parent = parent.parentElement;
21646             }
21647             return parent;
21648         }
21649         
21650         // is ancestor a text element.
21651         var ac =  range.commonAncestorContainer;
21652         if (ac.nodeType == 3) {
21653             ac = ac.parentNode;
21654         }
21655         
21656         var ar = ac.childNodes;
21657          
21658         var nodes = [];
21659         var other_nodes = [];
21660         var has_other_nodes = false;
21661         for (var i=0;i<ar.length;i++) {
21662             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21663                 continue;
21664             }
21665             // fullly contained node.
21666             
21667             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21668                 nodes.push(ar[i]);
21669                 continue;
21670             }
21671             
21672             // probably selected..
21673             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21674                 other_nodes.push(ar[i]);
21675                 continue;
21676             }
21677             // outer..
21678             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21679                 continue;
21680             }
21681             
21682             
21683             has_other_nodes = true;
21684         }
21685         if (!nodes.length && other_nodes.length) {
21686             nodes= other_nodes;
21687         }
21688         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21689             return false;
21690         }
21691         
21692         return nodes[0];
21693     },
21694     createRange: function(sel)
21695     {
21696         // this has strange effects when using with 
21697         // top toolbar - not sure if it's a great idea.
21698         //this.editor.contentWindow.focus();
21699         if (typeof sel != "undefined") {
21700             try {
21701                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21702             } catch(e) {
21703                 return this.doc.createRange();
21704             }
21705         } else {
21706             return this.doc.createRange();
21707         }
21708     },
21709     getParentElement: function()
21710     {
21711         
21712         this.assignDocWin();
21713         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21714         
21715         var range = this.createRange(sel);
21716          
21717         try {
21718             var p = range.commonAncestorContainer;
21719             while (p.nodeType == 3) { // text node
21720                 p = p.parentNode;
21721             }
21722             return p;
21723         } catch (e) {
21724             return null;
21725         }
21726     
21727     },
21728     /***
21729      *
21730      * Range intersection.. the hard stuff...
21731      *  '-1' = before
21732      *  '0' = hits..
21733      *  '1' = after.
21734      *         [ -- selected range --- ]
21735      *   [fail]                        [fail]
21736      *
21737      *    basically..
21738      *      if end is before start or  hits it. fail.
21739      *      if start is after end or hits it fail.
21740      *
21741      *   if either hits (but other is outside. - then it's not 
21742      *   
21743      *    
21744      **/
21745     
21746     
21747     // @see http://www.thismuchiknow.co.uk/?p=64.
21748     rangeIntersectsNode : function(range, node)
21749     {
21750         var nodeRange = node.ownerDocument.createRange();
21751         try {
21752             nodeRange.selectNode(node);
21753         } catch (e) {
21754             nodeRange.selectNodeContents(node);
21755         }
21756     
21757         var rangeStartRange = range.cloneRange();
21758         rangeStartRange.collapse(true);
21759     
21760         var rangeEndRange = range.cloneRange();
21761         rangeEndRange.collapse(false);
21762     
21763         var nodeStartRange = nodeRange.cloneRange();
21764         nodeStartRange.collapse(true);
21765     
21766         var nodeEndRange = nodeRange.cloneRange();
21767         nodeEndRange.collapse(false);
21768     
21769         return rangeStartRange.compareBoundaryPoints(
21770                  Range.START_TO_START, nodeEndRange) == -1 &&
21771                rangeEndRange.compareBoundaryPoints(
21772                  Range.START_TO_START, nodeStartRange) == 1;
21773         
21774          
21775     },
21776     rangeCompareNode : function(range, node)
21777     {
21778         var nodeRange = node.ownerDocument.createRange();
21779         try {
21780             nodeRange.selectNode(node);
21781         } catch (e) {
21782             nodeRange.selectNodeContents(node);
21783         }
21784         
21785         
21786         range.collapse(true);
21787     
21788         nodeRange.collapse(true);
21789      
21790         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21791         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21792          
21793         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21794         
21795         var nodeIsBefore   =  ss == 1;
21796         var nodeIsAfter    = ee == -1;
21797         
21798         if (nodeIsBefore && nodeIsAfter) {
21799             return 0; // outer
21800         }
21801         if (!nodeIsBefore && nodeIsAfter) {
21802             return 1; //right trailed.
21803         }
21804         
21805         if (nodeIsBefore && !nodeIsAfter) {
21806             return 2;  // left trailed.
21807         }
21808         // fully contined.
21809         return 3;
21810     },
21811
21812     // private? - in a new class?
21813     cleanUpPaste :  function()
21814     {
21815         // cleans up the whole document..
21816         Roo.log('cleanuppaste');
21817         
21818         this.cleanUpChildren(this.doc.body);
21819         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21820         if (clean != this.doc.body.innerHTML) {
21821             this.doc.body.innerHTML = clean;
21822         }
21823         
21824     },
21825     
21826     cleanWordChars : function(input) {// change the chars to hex code
21827         var he = Roo.HtmlEditorCore;
21828         
21829         var output = input;
21830         Roo.each(he.swapCodes, function(sw) { 
21831             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21832             
21833             output = output.replace(swapper, sw[1]);
21834         });
21835         
21836         return output;
21837     },
21838     
21839     
21840     cleanUpChildren : function (n)
21841     {
21842         if (!n.childNodes.length) {
21843             return;
21844         }
21845         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21846            this.cleanUpChild(n.childNodes[i]);
21847         }
21848     },
21849     
21850     
21851         
21852     
21853     cleanUpChild : function (node)
21854     {
21855         var ed = this;
21856         //console.log(node);
21857         if (node.nodeName == "#text") {
21858             // clean up silly Windows -- stuff?
21859             return; 
21860         }
21861         if (node.nodeName == "#comment") {
21862             node.parentNode.removeChild(node);
21863             // clean up silly Windows -- stuff?
21864             return; 
21865         }
21866         var lcname = node.tagName.toLowerCase();
21867         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21868         // whitelist of tags..
21869         
21870         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21871             // remove node.
21872             node.parentNode.removeChild(node);
21873             return;
21874             
21875         }
21876         
21877         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21878         
21879         // spans with no attributes - just remove them..
21880         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21881             remove_keep_children = true;
21882         }
21883         
21884         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21885         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21886         
21887         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21888         //    remove_keep_children = true;
21889         //}
21890         
21891         if (remove_keep_children) {
21892             this.cleanUpChildren(node);
21893             // inserts everything just before this node...
21894             while (node.childNodes.length) {
21895                 var cn = node.childNodes[0];
21896                 node.removeChild(cn);
21897                 node.parentNode.insertBefore(cn, node);
21898             }
21899             node.parentNode.removeChild(node);
21900             return;
21901         }
21902         
21903         if (!node.attributes || !node.attributes.length) {
21904             
21905           
21906             
21907             
21908             this.cleanUpChildren(node);
21909             return;
21910         }
21911         
21912         function cleanAttr(n,v)
21913         {
21914             
21915             if (v.match(/^\./) || v.match(/^\//)) {
21916                 return;
21917             }
21918             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21919                 return;
21920             }
21921             if (v.match(/^#/)) {
21922                 return;
21923             }
21924 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21925             node.removeAttribute(n);
21926             
21927         }
21928         
21929         var cwhite = this.cwhite;
21930         var cblack = this.cblack;
21931             
21932         function cleanStyle(n,v)
21933         {
21934             if (v.match(/expression/)) { //XSS?? should we even bother..
21935                 node.removeAttribute(n);
21936                 return;
21937             }
21938             
21939             var parts = v.split(/;/);
21940             var clean = [];
21941             
21942             Roo.each(parts, function(p) {
21943                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21944                 if (!p.length) {
21945                     return true;
21946                 }
21947                 var l = p.split(':').shift().replace(/\s+/g,'');
21948                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21949                 
21950                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21951 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21952                     //node.removeAttribute(n);
21953                     return true;
21954                 }
21955                 //Roo.log()
21956                 // only allow 'c whitelisted system attributes'
21957                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21958 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21959                     //node.removeAttribute(n);
21960                     return true;
21961                 }
21962                 
21963                 
21964                  
21965                 
21966                 clean.push(p);
21967                 return true;
21968             });
21969             if (clean.length) { 
21970                 node.setAttribute(n, clean.join(';'));
21971             } else {
21972                 node.removeAttribute(n);
21973             }
21974             
21975         }
21976         
21977         
21978         for (var i = node.attributes.length-1; i > -1 ; i--) {
21979             var a = node.attributes[i];
21980             //console.log(a);
21981             
21982             if (a.name.toLowerCase().substr(0,2)=='on')  {
21983                 node.removeAttribute(a.name);
21984                 continue;
21985             }
21986             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21987                 node.removeAttribute(a.name);
21988                 continue;
21989             }
21990             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21991                 cleanAttr(a.name,a.value); // fixme..
21992                 continue;
21993             }
21994             if (a.name == 'style') {
21995                 cleanStyle(a.name,a.value);
21996                 continue;
21997             }
21998             /// clean up MS crap..
21999             // tecnically this should be a list of valid class'es..
22000             
22001             
22002             if (a.name == 'class') {
22003                 if (a.value.match(/^Mso/)) {
22004                     node.removeAttribute('class');
22005                 }
22006                 
22007                 if (a.value.match(/^body$/)) {
22008                     node.removeAttribute('class');
22009                 }
22010                 continue;
22011             }
22012             
22013             // style cleanup!?
22014             // class cleanup?
22015             
22016         }
22017         
22018         
22019         this.cleanUpChildren(node);
22020         
22021         
22022     },
22023     
22024     /**
22025      * Clean up MS wordisms...
22026      */
22027     cleanWord : function(node)
22028     {
22029         if (!node) {
22030             this.cleanWord(this.doc.body);
22031             return;
22032         }
22033         
22034         if(
22035                 node.nodeName == 'SPAN' &&
22036                 !node.hasAttributes() &&
22037                 node.childNodes.length == 1 &&
22038                 node.firstChild.nodeName == "#text"  
22039         ) {
22040             var textNode = node.firstChild;
22041             node.removeChild(textNode);
22042             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22043                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22044             }
22045             node.parentNode.insertBefore(textNode, node);
22046             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22047                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22048             }
22049             node.parentNode.removeChild(node);
22050         }
22051         
22052         if (node.nodeName == "#text") {
22053             // clean up silly Windows -- stuff?
22054             return; 
22055         }
22056         if (node.nodeName == "#comment") {
22057             node.parentNode.removeChild(node);
22058             // clean up silly Windows -- stuff?
22059             return; 
22060         }
22061         
22062         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22063             node.parentNode.removeChild(node);
22064             return;
22065         }
22066         //Roo.log(node.tagName);
22067         // remove - but keep children..
22068         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22069             //Roo.log('-- removed');
22070             while (node.childNodes.length) {
22071                 var cn = node.childNodes[0];
22072                 node.removeChild(cn);
22073                 node.parentNode.insertBefore(cn, node);
22074                 // move node to parent - and clean it..
22075                 this.cleanWord(cn);
22076             }
22077             node.parentNode.removeChild(node);
22078             /// no need to iterate chidlren = it's got none..
22079             //this.iterateChildren(node, this.cleanWord);
22080             return;
22081         }
22082         // clean styles
22083         if (node.className.length) {
22084             
22085             var cn = node.className.split(/\W+/);
22086             var cna = [];
22087             Roo.each(cn, function(cls) {
22088                 if (cls.match(/Mso[a-zA-Z]+/)) {
22089                     return;
22090                 }
22091                 cna.push(cls);
22092             });
22093             node.className = cna.length ? cna.join(' ') : '';
22094             if (!cna.length) {
22095                 node.removeAttribute("class");
22096             }
22097         }
22098         
22099         if (node.hasAttribute("lang")) {
22100             node.removeAttribute("lang");
22101         }
22102         
22103         if (node.hasAttribute("style")) {
22104             
22105             var styles = node.getAttribute("style").split(";");
22106             var nstyle = [];
22107             Roo.each(styles, function(s) {
22108                 if (!s.match(/:/)) {
22109                     return;
22110                 }
22111                 var kv = s.split(":");
22112                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22113                     return;
22114                 }
22115                 // what ever is left... we allow.
22116                 nstyle.push(s);
22117             });
22118             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22119             if (!nstyle.length) {
22120                 node.removeAttribute('style');
22121             }
22122         }
22123         this.iterateChildren(node, this.cleanWord);
22124         
22125         
22126         
22127     },
22128     /**
22129      * iterateChildren of a Node, calling fn each time, using this as the scole..
22130      * @param {DomNode} node node to iterate children of.
22131      * @param {Function} fn method of this class to call on each item.
22132      */
22133     iterateChildren : function(node, fn)
22134     {
22135         if (!node.childNodes.length) {
22136                 return;
22137         }
22138         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22139            fn.call(this, node.childNodes[i])
22140         }
22141     },
22142     
22143     
22144     /**
22145      * cleanTableWidths.
22146      *
22147      * Quite often pasting from word etc.. results in tables with column and widths.
22148      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22149      *
22150      */
22151     cleanTableWidths : function(node)
22152     {
22153          
22154          
22155         if (!node) {
22156             this.cleanTableWidths(this.doc.body);
22157             return;
22158         }
22159         
22160         // ignore list...
22161         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22162             return; 
22163         }
22164         Roo.log(node.tagName);
22165         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22166             this.iterateChildren(node, this.cleanTableWidths);
22167             return;
22168         }
22169         if (node.hasAttribute('width')) {
22170             node.removeAttribute('width');
22171         }
22172         
22173          
22174         if (node.hasAttribute("style")) {
22175             // pretty basic...
22176             
22177             var styles = node.getAttribute("style").split(";");
22178             var nstyle = [];
22179             Roo.each(styles, function(s) {
22180                 if (!s.match(/:/)) {
22181                     return;
22182                 }
22183                 var kv = s.split(":");
22184                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22185                     return;
22186                 }
22187                 // what ever is left... we allow.
22188                 nstyle.push(s);
22189             });
22190             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22191             if (!nstyle.length) {
22192                 node.removeAttribute('style');
22193             }
22194         }
22195         
22196         this.iterateChildren(node, this.cleanTableWidths);
22197         
22198         
22199     },
22200     
22201     
22202     
22203     
22204     domToHTML : function(currentElement, depth, nopadtext) {
22205         
22206         depth = depth || 0;
22207         nopadtext = nopadtext || false;
22208     
22209         if (!currentElement) {
22210             return this.domToHTML(this.doc.body);
22211         }
22212         
22213         //Roo.log(currentElement);
22214         var j;
22215         var allText = false;
22216         var nodeName = currentElement.nodeName;
22217         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22218         
22219         if  (nodeName == '#text') {
22220             
22221             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22222         }
22223         
22224         
22225         var ret = '';
22226         if (nodeName != 'BODY') {
22227              
22228             var i = 0;
22229             // Prints the node tagName, such as <A>, <IMG>, etc
22230             if (tagName) {
22231                 var attr = [];
22232                 for(i = 0; i < currentElement.attributes.length;i++) {
22233                     // quoting?
22234                     var aname = currentElement.attributes.item(i).name;
22235                     if (!currentElement.attributes.item(i).value.length) {
22236                         continue;
22237                     }
22238                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22239                 }
22240                 
22241                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22242             } 
22243             else {
22244                 
22245                 // eack
22246             }
22247         } else {
22248             tagName = false;
22249         }
22250         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22251             return ret;
22252         }
22253         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22254             nopadtext = true;
22255         }
22256         
22257         
22258         // Traverse the tree
22259         i = 0;
22260         var currentElementChild = currentElement.childNodes.item(i);
22261         var allText = true;
22262         var innerHTML  = '';
22263         lastnode = '';
22264         while (currentElementChild) {
22265             // Formatting code (indent the tree so it looks nice on the screen)
22266             var nopad = nopadtext;
22267             if (lastnode == 'SPAN') {
22268                 nopad  = true;
22269             }
22270             // text
22271             if  (currentElementChild.nodeName == '#text') {
22272                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22273                 toadd = nopadtext ? toadd : toadd.trim();
22274                 if (!nopad && toadd.length > 80) {
22275                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22276                 }
22277                 innerHTML  += toadd;
22278                 
22279                 i++;
22280                 currentElementChild = currentElement.childNodes.item(i);
22281                 lastNode = '';
22282                 continue;
22283             }
22284             allText = false;
22285             
22286             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22287                 
22288             // Recursively traverse the tree structure of the child node
22289             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22290             lastnode = currentElementChild.nodeName;
22291             i++;
22292             currentElementChild=currentElement.childNodes.item(i);
22293         }
22294         
22295         ret += innerHTML;
22296         
22297         if (!allText) {
22298                 // The remaining code is mostly for formatting the tree
22299             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22300         }
22301         
22302         
22303         if (tagName) {
22304             ret+= "</"+tagName+">";
22305         }
22306         return ret;
22307         
22308     },
22309         
22310     applyBlacklists : function()
22311     {
22312         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22313         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22314         
22315         this.white = [];
22316         this.black = [];
22317         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22318             if (b.indexOf(tag) > -1) {
22319                 return;
22320             }
22321             this.white.push(tag);
22322             
22323         }, this);
22324         
22325         Roo.each(w, function(tag) {
22326             if (b.indexOf(tag) > -1) {
22327                 return;
22328             }
22329             if (this.white.indexOf(tag) > -1) {
22330                 return;
22331             }
22332             this.white.push(tag);
22333             
22334         }, this);
22335         
22336         
22337         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22338             if (w.indexOf(tag) > -1) {
22339                 return;
22340             }
22341             this.black.push(tag);
22342             
22343         }, this);
22344         
22345         Roo.each(b, function(tag) {
22346             if (w.indexOf(tag) > -1) {
22347                 return;
22348             }
22349             if (this.black.indexOf(tag) > -1) {
22350                 return;
22351             }
22352             this.black.push(tag);
22353             
22354         }, this);
22355         
22356         
22357         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22358         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22359         
22360         this.cwhite = [];
22361         this.cblack = [];
22362         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22363             if (b.indexOf(tag) > -1) {
22364                 return;
22365             }
22366             this.cwhite.push(tag);
22367             
22368         }, this);
22369         
22370         Roo.each(w, function(tag) {
22371             if (b.indexOf(tag) > -1) {
22372                 return;
22373             }
22374             if (this.cwhite.indexOf(tag) > -1) {
22375                 return;
22376             }
22377             this.cwhite.push(tag);
22378             
22379         }, this);
22380         
22381         
22382         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22383             if (w.indexOf(tag) > -1) {
22384                 return;
22385             }
22386             this.cblack.push(tag);
22387             
22388         }, this);
22389         
22390         Roo.each(b, function(tag) {
22391             if (w.indexOf(tag) > -1) {
22392                 return;
22393             }
22394             if (this.cblack.indexOf(tag) > -1) {
22395                 return;
22396             }
22397             this.cblack.push(tag);
22398             
22399         }, this);
22400     },
22401     
22402     setStylesheets : function(stylesheets)
22403     {
22404         if(typeof(stylesheets) == 'string'){
22405             Roo.get(this.iframe.contentDocument.head).createChild({
22406                 tag : 'link',
22407                 rel : 'stylesheet',
22408                 type : 'text/css',
22409                 href : stylesheets
22410             });
22411             
22412             return;
22413         }
22414         var _this = this;
22415      
22416         Roo.each(stylesheets, function(s) {
22417             if(!s.length){
22418                 return;
22419             }
22420             
22421             Roo.get(_this.iframe.contentDocument.head).createChild({
22422                 tag : 'link',
22423                 rel : 'stylesheet',
22424                 type : 'text/css',
22425                 href : s
22426             });
22427         });
22428
22429         
22430     },
22431     
22432     removeStylesheets : function()
22433     {
22434         var _this = this;
22435         
22436         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22437             s.remove();
22438         });
22439     },
22440     
22441     setStyle : function(style)
22442     {
22443         Roo.get(this.iframe.contentDocument.head).createChild({
22444             tag : 'style',
22445             type : 'text/css',
22446             html : style
22447         });
22448
22449         return;
22450     }
22451     
22452     // hide stuff that is not compatible
22453     /**
22454      * @event blur
22455      * @hide
22456      */
22457     /**
22458      * @event change
22459      * @hide
22460      */
22461     /**
22462      * @event focus
22463      * @hide
22464      */
22465     /**
22466      * @event specialkey
22467      * @hide
22468      */
22469     /**
22470      * @cfg {String} fieldClass @hide
22471      */
22472     /**
22473      * @cfg {String} focusClass @hide
22474      */
22475     /**
22476      * @cfg {String} autoCreate @hide
22477      */
22478     /**
22479      * @cfg {String} inputType @hide
22480      */
22481     /**
22482      * @cfg {String} invalidClass @hide
22483      */
22484     /**
22485      * @cfg {String} invalidText @hide
22486      */
22487     /**
22488      * @cfg {String} msgFx @hide
22489      */
22490     /**
22491      * @cfg {String} validateOnBlur @hide
22492      */
22493 });
22494
22495 Roo.HtmlEditorCore.white = [
22496         'area', 'br', 'img', 'input', 'hr', 'wbr',
22497         
22498        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22499        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22500        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22501        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22502        'table',   'ul',         'xmp', 
22503        
22504        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22505       'thead',   'tr', 
22506      
22507       'dir', 'menu', 'ol', 'ul', 'dl',
22508        
22509       'embed',  'object'
22510 ];
22511
22512
22513 Roo.HtmlEditorCore.black = [
22514     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22515         'applet', // 
22516         'base',   'basefont', 'bgsound', 'blink',  'body', 
22517         'frame',  'frameset', 'head',    'html',   'ilayer', 
22518         'iframe', 'layer',  'link',     'meta',    'object',   
22519         'script', 'style' ,'title',  'xml' // clean later..
22520 ];
22521 Roo.HtmlEditorCore.clean = [
22522     'script', 'style', 'title', 'xml'
22523 ];
22524 Roo.HtmlEditorCore.remove = [
22525     'font'
22526 ];
22527 // attributes..
22528
22529 Roo.HtmlEditorCore.ablack = [
22530     'on'
22531 ];
22532     
22533 Roo.HtmlEditorCore.aclean = [ 
22534     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22535 ];
22536
22537 // protocols..
22538 Roo.HtmlEditorCore.pwhite= [
22539         'http',  'https',  'mailto'
22540 ];
22541
22542 // white listed style attributes.
22543 Roo.HtmlEditorCore.cwhite= [
22544       //  'text-align', /// default is to allow most things..
22545       
22546          
22547 //        'font-size'//??
22548 ];
22549
22550 // black listed style attributes.
22551 Roo.HtmlEditorCore.cblack= [
22552       //  'font-size' -- this can be set by the project 
22553 ];
22554
22555
22556 Roo.HtmlEditorCore.swapCodes   =[ 
22557     [    8211, "--" ], 
22558     [    8212, "--" ], 
22559     [    8216,  "'" ],  
22560     [    8217, "'" ],  
22561     [    8220, '"' ],  
22562     [    8221, '"' ],  
22563     [    8226, "*" ],  
22564     [    8230, "..." ]
22565 ]; 
22566
22567     //<script type="text/javascript">
22568
22569 /*
22570  * Ext JS Library 1.1.1
22571  * Copyright(c) 2006-2007, Ext JS, LLC.
22572  * Licence LGPL
22573  * 
22574  */
22575  
22576  
22577 Roo.form.HtmlEditor = function(config){
22578     
22579     
22580     
22581     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22582     
22583     if (!this.toolbars) {
22584         this.toolbars = [];
22585     }
22586     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22587     
22588     
22589 };
22590
22591 /**
22592  * @class Roo.form.HtmlEditor
22593  * @extends Roo.form.Field
22594  * Provides a lightweight HTML Editor component.
22595  *
22596  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22597  * 
22598  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22599  * supported by this editor.</b><br/><br/>
22600  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22601  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22602  */
22603 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22604     /**
22605      * @cfg {Boolean} clearUp
22606      */
22607     clearUp : true,
22608       /**
22609      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22610      */
22611     toolbars : false,
22612    
22613      /**
22614      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22615      *                        Roo.resizable.
22616      */
22617     resizable : false,
22618      /**
22619      * @cfg {Number} height (in pixels)
22620      */   
22621     height: 300,
22622    /**
22623      * @cfg {Number} width (in pixels)
22624      */   
22625     width: 500,
22626     
22627     /**
22628      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22629      * 
22630      */
22631     stylesheets: false,
22632     
22633     
22634      /**
22635      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22636      * 
22637      */
22638     cblack: false,
22639     /**
22640      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22641      * 
22642      */
22643     cwhite: false,
22644     
22645      /**
22646      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22647      * 
22648      */
22649     black: false,
22650     /**
22651      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22652      * 
22653      */
22654     white: false,
22655     
22656     // id of frame..
22657     frameId: false,
22658     
22659     // private properties
22660     validationEvent : false,
22661     deferHeight: true,
22662     initialized : false,
22663     activated : false,
22664     
22665     onFocus : Roo.emptyFn,
22666     iframePad:3,
22667     hideMode:'offsets',
22668     
22669     actionMode : 'container', // defaults to hiding it...
22670     
22671     defaultAutoCreate : { // modified by initCompnoent..
22672         tag: "textarea",
22673         style:"width:500px;height:300px;",
22674         autocomplete: "new-password"
22675     },
22676
22677     // private
22678     initComponent : function(){
22679         this.addEvents({
22680             /**
22681              * @event initialize
22682              * Fires when the editor is fully initialized (including the iframe)
22683              * @param {HtmlEditor} this
22684              */
22685             initialize: true,
22686             /**
22687              * @event activate
22688              * Fires when the editor is first receives the focus. Any insertion must wait
22689              * until after this event.
22690              * @param {HtmlEditor} this
22691              */
22692             activate: true,
22693              /**
22694              * @event beforesync
22695              * Fires before the textarea is updated with content from the editor iframe. Return false
22696              * to cancel the sync.
22697              * @param {HtmlEditor} this
22698              * @param {String} html
22699              */
22700             beforesync: true,
22701              /**
22702              * @event beforepush
22703              * Fires before the iframe editor is updated with content from the textarea. Return false
22704              * to cancel the push.
22705              * @param {HtmlEditor} this
22706              * @param {String} html
22707              */
22708             beforepush: true,
22709              /**
22710              * @event sync
22711              * Fires when the textarea is updated with content from the editor iframe.
22712              * @param {HtmlEditor} this
22713              * @param {String} html
22714              */
22715             sync: true,
22716              /**
22717              * @event push
22718              * Fires when the iframe editor is updated with content from the textarea.
22719              * @param {HtmlEditor} this
22720              * @param {String} html
22721              */
22722             push: true,
22723              /**
22724              * @event editmodechange
22725              * Fires when the editor switches edit modes
22726              * @param {HtmlEditor} this
22727              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22728              */
22729             editmodechange: true,
22730             /**
22731              * @event editorevent
22732              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22733              * @param {HtmlEditor} this
22734              */
22735             editorevent: true,
22736             /**
22737              * @event firstfocus
22738              * Fires when on first focus - needed by toolbars..
22739              * @param {HtmlEditor} this
22740              */
22741             firstfocus: true,
22742             /**
22743              * @event autosave
22744              * Auto save the htmlEditor value as a file into Events
22745              * @param {HtmlEditor} this
22746              */
22747             autosave: true,
22748             /**
22749              * @event savedpreview
22750              * preview the saved version of htmlEditor
22751              * @param {HtmlEditor} this
22752              */
22753             savedpreview: true,
22754             
22755             /**
22756             * @event stylesheetsclick
22757             * Fires when press the Sytlesheets button
22758             * @param {Roo.HtmlEditorCore} this
22759             */
22760             stylesheetsclick: true
22761         });
22762         this.defaultAutoCreate =  {
22763             tag: "textarea",
22764             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22765             autocomplete: "new-password"
22766         };
22767     },
22768
22769     /**
22770      * Protected method that will not generally be called directly. It
22771      * is called when the editor creates its toolbar. Override this method if you need to
22772      * add custom toolbar buttons.
22773      * @param {HtmlEditor} editor
22774      */
22775     createToolbar : function(editor){
22776         Roo.log("create toolbars");
22777         if (!editor.toolbars || !editor.toolbars.length) {
22778             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22779         }
22780         
22781         for (var i =0 ; i < editor.toolbars.length;i++) {
22782             editor.toolbars[i] = Roo.factory(
22783                     typeof(editor.toolbars[i]) == 'string' ?
22784                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22785                 Roo.form.HtmlEditor);
22786             editor.toolbars[i].init(editor);
22787         }
22788          
22789         
22790     },
22791
22792      
22793     // private
22794     onRender : function(ct, position)
22795     {
22796         var _t = this;
22797         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22798         
22799         this.wrap = this.el.wrap({
22800             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22801         });
22802         
22803         this.editorcore.onRender(ct, position);
22804          
22805         if (this.resizable) {
22806             this.resizeEl = new Roo.Resizable(this.wrap, {
22807                 pinned : true,
22808                 wrap: true,
22809                 dynamic : true,
22810                 minHeight : this.height,
22811                 height: this.height,
22812                 handles : this.resizable,
22813                 width: this.width,
22814                 listeners : {
22815                     resize : function(r, w, h) {
22816                         _t.onResize(w,h); // -something
22817                     }
22818                 }
22819             });
22820             
22821         }
22822         this.createToolbar(this);
22823        
22824         
22825         if(!this.width){
22826             this.setSize(this.wrap.getSize());
22827         }
22828         if (this.resizeEl) {
22829             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22830             // should trigger onReize..
22831         }
22832         
22833         this.keyNav = new Roo.KeyNav(this.el, {
22834             
22835             "tab" : function(e){
22836                 e.preventDefault();
22837                 
22838                 var value = this.getValue();
22839                 
22840                 var start = this.el.dom.selectionStart;
22841                 var end = this.el.dom.selectionEnd;
22842                 
22843                 if(!e.shiftKey){
22844                     
22845                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22846                     this.el.dom.setSelectionRange(end + 1, end + 1);
22847                     return;
22848                 }
22849                 
22850                 var f = value.substring(0, start).split("\t");
22851                 
22852                 if(f.pop().length != 0){
22853                     return;
22854                 }
22855                 
22856                 this.setValue(f.join("\t") + value.substring(end));
22857                 this.el.dom.setSelectionRange(start - 1, start - 1);
22858                 
22859             },
22860             
22861             "home" : function(e){
22862                 e.preventDefault();
22863                 
22864                 var curr = this.el.dom.selectionStart;
22865                 var lines = this.getValue().split("\n");
22866                 
22867                 if(!lines.length){
22868                     return;
22869                 }
22870                 
22871                 if(e.ctrlKey){
22872                     this.el.dom.setSelectionRange(0, 0);
22873                     return;
22874                 }
22875                 
22876                 var pos = 0;
22877                 
22878                 for (var i = 0; i < lines.length;i++) {
22879                     pos += lines[i].length;
22880                     
22881                     if(i != 0){
22882                         pos += 1;
22883                     }
22884                     
22885                     if(pos < curr){
22886                         continue;
22887                     }
22888                     
22889                     pos -= lines[i].length;
22890                     
22891                     break;
22892                 }
22893                 
22894                 if(!e.shiftKey){
22895                     this.el.dom.setSelectionRange(pos, pos);
22896                     return;
22897                 }
22898                 
22899                 this.el.dom.selectionStart = pos;
22900                 this.el.dom.selectionEnd = curr;
22901             },
22902             
22903             "end" : function(e){
22904                 e.preventDefault();
22905                 
22906                 var curr = this.el.dom.selectionStart;
22907                 var lines = this.getValue().split("\n");
22908                 
22909                 if(!lines.length){
22910                     return;
22911                 }
22912                 
22913                 if(e.ctrlKey){
22914                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22915                     return;
22916                 }
22917                 
22918                 var pos = 0;
22919                 
22920                 for (var i = 0; i < lines.length;i++) {
22921                     
22922                     pos += lines[i].length;
22923                     
22924                     if(i != 0){
22925                         pos += 1;
22926                     }
22927                     
22928                     if(pos < curr){
22929                         continue;
22930                     }
22931                     
22932                     break;
22933                 }
22934                 
22935                 if(!e.shiftKey){
22936                     this.el.dom.setSelectionRange(pos, pos);
22937                     return;
22938                 }
22939                 
22940                 this.el.dom.selectionStart = curr;
22941                 this.el.dom.selectionEnd = pos;
22942             },
22943
22944             scope : this,
22945
22946             doRelay : function(foo, bar, hname){
22947                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22948             },
22949
22950             forceKeyDown: true
22951         });
22952         
22953 //        if(this.autosave && this.w){
22954 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22955 //        }
22956     },
22957
22958     // private
22959     onResize : function(w, h)
22960     {
22961         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22962         var ew = false;
22963         var eh = false;
22964         
22965         if(this.el ){
22966             if(typeof w == 'number'){
22967                 var aw = w - this.wrap.getFrameWidth('lr');
22968                 this.el.setWidth(this.adjustWidth('textarea', aw));
22969                 ew = aw;
22970             }
22971             if(typeof h == 'number'){
22972                 var tbh = 0;
22973                 for (var i =0; i < this.toolbars.length;i++) {
22974                     // fixme - ask toolbars for heights?
22975                     tbh += this.toolbars[i].tb.el.getHeight();
22976                     if (this.toolbars[i].footer) {
22977                         tbh += this.toolbars[i].footer.el.getHeight();
22978                     }
22979                 }
22980                 
22981                 
22982                 
22983                 
22984                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22985                 ah -= 5; // knock a few pixes off for look..
22986 //                Roo.log(ah);
22987                 this.el.setHeight(this.adjustWidth('textarea', ah));
22988                 var eh = ah;
22989             }
22990         }
22991         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22992         this.editorcore.onResize(ew,eh);
22993         
22994     },
22995
22996     /**
22997      * Toggles the editor between standard and source edit mode.
22998      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22999      */
23000     toggleSourceEdit : function(sourceEditMode)
23001     {
23002         this.editorcore.toggleSourceEdit(sourceEditMode);
23003         
23004         if(this.editorcore.sourceEditMode){
23005             Roo.log('editor - showing textarea');
23006             
23007 //            Roo.log('in');
23008 //            Roo.log(this.syncValue());
23009             this.editorcore.syncValue();
23010             this.el.removeClass('x-hidden');
23011             this.el.dom.removeAttribute('tabIndex');
23012             this.el.focus();
23013             
23014             for (var i = 0; i < this.toolbars.length; i++) {
23015                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23016                     this.toolbars[i].tb.hide();
23017                     this.toolbars[i].footer.hide();
23018                 }
23019             }
23020             
23021         }else{
23022             Roo.log('editor - hiding textarea');
23023 //            Roo.log('out')
23024 //            Roo.log(this.pushValue()); 
23025             this.editorcore.pushValue();
23026             
23027             this.el.addClass('x-hidden');
23028             this.el.dom.setAttribute('tabIndex', -1);
23029             
23030             for (var i = 0; i < this.toolbars.length; i++) {
23031                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23032                     this.toolbars[i].tb.show();
23033                     this.toolbars[i].footer.show();
23034                 }
23035             }
23036             
23037             //this.deferFocus();
23038         }
23039         
23040         this.setSize(this.wrap.getSize());
23041         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23042         
23043         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23044     },
23045  
23046     // private (for BoxComponent)
23047     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23048
23049     // private (for BoxComponent)
23050     getResizeEl : function(){
23051         return this.wrap;
23052     },
23053
23054     // private (for BoxComponent)
23055     getPositionEl : function(){
23056         return this.wrap;
23057     },
23058
23059     // private
23060     initEvents : function(){
23061         this.originalValue = this.getValue();
23062     },
23063
23064     /**
23065      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23066      * @method
23067      */
23068     markInvalid : Roo.emptyFn,
23069     /**
23070      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23071      * @method
23072      */
23073     clearInvalid : Roo.emptyFn,
23074
23075     setValue : function(v){
23076         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23077         this.editorcore.pushValue();
23078     },
23079
23080      
23081     // private
23082     deferFocus : function(){
23083         this.focus.defer(10, this);
23084     },
23085
23086     // doc'ed in Field
23087     focus : function(){
23088         this.editorcore.focus();
23089         
23090     },
23091       
23092
23093     // private
23094     onDestroy : function(){
23095         
23096         
23097         
23098         if(this.rendered){
23099             
23100             for (var i =0; i < this.toolbars.length;i++) {
23101                 // fixme - ask toolbars for heights?
23102                 this.toolbars[i].onDestroy();
23103             }
23104             
23105             this.wrap.dom.innerHTML = '';
23106             this.wrap.remove();
23107         }
23108     },
23109
23110     // private
23111     onFirstFocus : function(){
23112         //Roo.log("onFirstFocus");
23113         this.editorcore.onFirstFocus();
23114          for (var i =0; i < this.toolbars.length;i++) {
23115             this.toolbars[i].onFirstFocus();
23116         }
23117         
23118     },
23119     
23120     // private
23121     syncValue : function()
23122     {
23123         this.editorcore.syncValue();
23124     },
23125     
23126     pushValue : function()
23127     {
23128         this.editorcore.pushValue();
23129     },
23130     
23131     setStylesheets : function(stylesheets)
23132     {
23133         this.editorcore.setStylesheets(stylesheets);
23134     },
23135     
23136     removeStylesheets : function()
23137     {
23138         this.editorcore.removeStylesheets();
23139     }
23140      
23141     
23142     // hide stuff that is not compatible
23143     /**
23144      * @event blur
23145      * @hide
23146      */
23147     /**
23148      * @event change
23149      * @hide
23150      */
23151     /**
23152      * @event focus
23153      * @hide
23154      */
23155     /**
23156      * @event specialkey
23157      * @hide
23158      */
23159     /**
23160      * @cfg {String} fieldClass @hide
23161      */
23162     /**
23163      * @cfg {String} focusClass @hide
23164      */
23165     /**
23166      * @cfg {String} autoCreate @hide
23167      */
23168     /**
23169      * @cfg {String} inputType @hide
23170      */
23171     /**
23172      * @cfg {String} invalidClass @hide
23173      */
23174     /**
23175      * @cfg {String} invalidText @hide
23176      */
23177     /**
23178      * @cfg {String} msgFx @hide
23179      */
23180     /**
23181      * @cfg {String} validateOnBlur @hide
23182      */
23183 });
23184  
23185     // <script type="text/javascript">
23186 /*
23187  * Based on
23188  * Ext JS Library 1.1.1
23189  * Copyright(c) 2006-2007, Ext JS, LLC.
23190  *  
23191  
23192  */
23193
23194 /**
23195  * @class Roo.form.HtmlEditorToolbar1
23196  * Basic Toolbar
23197  * 
23198  * Usage:
23199  *
23200  new Roo.form.HtmlEditor({
23201     ....
23202     toolbars : [
23203         new Roo.form.HtmlEditorToolbar1({
23204             disable : { fonts: 1 , format: 1, ..., ... , ...],
23205             btns : [ .... ]
23206         })
23207     }
23208      
23209  * 
23210  * @cfg {Object} disable List of elements to disable..
23211  * @cfg {Array} btns List of additional buttons.
23212  * 
23213  * 
23214  * NEEDS Extra CSS? 
23215  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23216  */
23217  
23218 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23219 {
23220     
23221     Roo.apply(this, config);
23222     
23223     // default disabled, based on 'good practice'..
23224     this.disable = this.disable || {};
23225     Roo.applyIf(this.disable, {
23226         fontSize : true,
23227         colors : true,
23228         specialElements : true
23229     });
23230     
23231     
23232     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23233     // dont call parent... till later.
23234 }
23235
23236 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23237     
23238     tb: false,
23239     
23240     rendered: false,
23241     
23242     editor : false,
23243     editorcore : false,
23244     /**
23245      * @cfg {Object} disable  List of toolbar elements to disable
23246          
23247      */
23248     disable : false,
23249     
23250     
23251      /**
23252      * @cfg {String} createLinkText The default text for the create link prompt
23253      */
23254     createLinkText : 'Please enter the URL for the link:',
23255     /**
23256      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23257      */
23258     defaultLinkValue : 'http:/'+'/',
23259    
23260     
23261       /**
23262      * @cfg {Array} fontFamilies An array of available font families
23263      */
23264     fontFamilies : [
23265         'Arial',
23266         'Courier New',
23267         'Tahoma',
23268         'Times New Roman',
23269         'Verdana'
23270     ],
23271     
23272     specialChars : [
23273            "&#169;",
23274           "&#174;",     
23275           "&#8482;",    
23276           "&#163;" ,    
23277          // "&#8212;",    
23278           "&#8230;",    
23279           "&#247;" ,    
23280         //  "&#225;" ,     ?? a acute?
23281            "&#8364;"    , //Euro
23282        //   "&#8220;"    ,
23283         //  "&#8221;"    ,
23284         //  "&#8226;"    ,
23285           "&#176;"  //   , // degrees
23286
23287          // "&#233;"     , // e ecute
23288          // "&#250;"     , // u ecute?
23289     ],
23290     
23291     specialElements : [
23292         {
23293             text: "Insert Table",
23294             xtype: 'MenuItem',
23295             xns : Roo.Menu,
23296             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23297                 
23298         },
23299         {    
23300             text: "Insert Image",
23301             xtype: 'MenuItem',
23302             xns : Roo.Menu,
23303             ihtml : '<img src="about:blank"/>'
23304             
23305         }
23306         
23307          
23308     ],
23309     
23310     
23311     inputElements : [ 
23312             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23313             "input:submit", "input:button", "select", "textarea", "label" ],
23314     formats : [
23315         ["p"] ,  
23316         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23317         ["pre"],[ "code"], 
23318         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23319         ['div'],['span'],
23320         ['sup'],['sub']
23321     ],
23322     
23323     cleanStyles : [
23324         "font-size"
23325     ],
23326      /**
23327      * @cfg {String} defaultFont default font to use.
23328      */
23329     defaultFont: 'tahoma',
23330    
23331     fontSelect : false,
23332     
23333     
23334     formatCombo : false,
23335     
23336     init : function(editor)
23337     {
23338         this.editor = editor;
23339         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23340         var editorcore = this.editorcore;
23341         
23342         var _t = this;
23343         
23344         var fid = editorcore.frameId;
23345         var etb = this;
23346         function btn(id, toggle, handler){
23347             var xid = fid + '-'+ id ;
23348             return {
23349                 id : xid,
23350                 cmd : id,
23351                 cls : 'x-btn-icon x-edit-'+id,
23352                 enableToggle:toggle !== false,
23353                 scope: _t, // was editor...
23354                 handler:handler||_t.relayBtnCmd,
23355                 clickEvent:'mousedown',
23356                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23357                 tabIndex:-1
23358             };
23359         }
23360         
23361         
23362         
23363         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23364         this.tb = tb;
23365          // stop form submits
23366         tb.el.on('click', function(e){
23367             e.preventDefault(); // what does this do?
23368         });
23369
23370         if(!this.disable.font) { // && !Roo.isSafari){
23371             /* why no safari for fonts 
23372             editor.fontSelect = tb.el.createChild({
23373                 tag:'select',
23374                 tabIndex: -1,
23375                 cls:'x-font-select',
23376                 html: this.createFontOptions()
23377             });
23378             
23379             editor.fontSelect.on('change', function(){
23380                 var font = editor.fontSelect.dom.value;
23381                 editor.relayCmd('fontname', font);
23382                 editor.deferFocus();
23383             }, editor);
23384             
23385             tb.add(
23386                 editor.fontSelect.dom,
23387                 '-'
23388             );
23389             */
23390             
23391         };
23392         if(!this.disable.formats){
23393             this.formatCombo = new Roo.form.ComboBox({
23394                 store: new Roo.data.SimpleStore({
23395                     id : 'tag',
23396                     fields: ['tag'],
23397                     data : this.formats // from states.js
23398                 }),
23399                 blockFocus : true,
23400                 name : '',
23401                 //autoCreate : {tag: "div",  size: "20"},
23402                 displayField:'tag',
23403                 typeAhead: false,
23404                 mode: 'local',
23405                 editable : false,
23406                 triggerAction: 'all',
23407                 emptyText:'Add tag',
23408                 selectOnFocus:true,
23409                 width:135,
23410                 listeners : {
23411                     'select': function(c, r, i) {
23412                         editorcore.insertTag(r.get('tag'));
23413                         editor.focus();
23414                     }
23415                 }
23416
23417             });
23418             tb.addField(this.formatCombo);
23419             
23420         }
23421         
23422         if(!this.disable.format){
23423             tb.add(
23424                 btn('bold'),
23425                 btn('italic'),
23426                 btn('underline'),
23427                 btn('strikethrough')
23428             );
23429         };
23430         if(!this.disable.fontSize){
23431             tb.add(
23432                 '-',
23433                 
23434                 
23435                 btn('increasefontsize', false, editorcore.adjustFont),
23436                 btn('decreasefontsize', false, editorcore.adjustFont)
23437             );
23438         };
23439         
23440         
23441         if(!this.disable.colors){
23442             tb.add(
23443                 '-', {
23444                     id:editorcore.frameId +'-forecolor',
23445                     cls:'x-btn-icon x-edit-forecolor',
23446                     clickEvent:'mousedown',
23447                     tooltip: this.buttonTips['forecolor'] || undefined,
23448                     tabIndex:-1,
23449                     menu : new Roo.menu.ColorMenu({
23450                         allowReselect: true,
23451                         focus: Roo.emptyFn,
23452                         value:'000000',
23453                         plain:true,
23454                         selectHandler: function(cp, color){
23455                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23456                             editor.deferFocus();
23457                         },
23458                         scope: editorcore,
23459                         clickEvent:'mousedown'
23460                     })
23461                 }, {
23462                     id:editorcore.frameId +'backcolor',
23463                     cls:'x-btn-icon x-edit-backcolor',
23464                     clickEvent:'mousedown',
23465                     tooltip: this.buttonTips['backcolor'] || undefined,
23466                     tabIndex:-1,
23467                     menu : new Roo.menu.ColorMenu({
23468                         focus: Roo.emptyFn,
23469                         value:'FFFFFF',
23470                         plain:true,
23471                         allowReselect: true,
23472                         selectHandler: function(cp, color){
23473                             if(Roo.isGecko){
23474                                 editorcore.execCmd('useCSS', false);
23475                                 editorcore.execCmd('hilitecolor', color);
23476                                 editorcore.execCmd('useCSS', true);
23477                                 editor.deferFocus();
23478                             }else{
23479                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23480                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23481                                 editor.deferFocus();
23482                             }
23483                         },
23484                         scope:editorcore,
23485                         clickEvent:'mousedown'
23486                     })
23487                 }
23488             );
23489         };
23490         // now add all the items...
23491         
23492
23493         if(!this.disable.alignments){
23494             tb.add(
23495                 '-',
23496                 btn('justifyleft'),
23497                 btn('justifycenter'),
23498                 btn('justifyright')
23499             );
23500         };
23501
23502         //if(!Roo.isSafari){
23503             if(!this.disable.links){
23504                 tb.add(
23505                     '-',
23506                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23507                 );
23508             };
23509
23510             if(!this.disable.lists){
23511                 tb.add(
23512                     '-',
23513                     btn('insertorderedlist'),
23514                     btn('insertunorderedlist')
23515                 );
23516             }
23517             if(!this.disable.sourceEdit){
23518                 tb.add(
23519                     '-',
23520                     btn('sourceedit', true, function(btn){
23521                         this.toggleSourceEdit(btn.pressed);
23522                     })
23523                 );
23524             }
23525         //}
23526         
23527         var smenu = { };
23528         // special menu.. - needs to be tidied up..
23529         if (!this.disable.special) {
23530             smenu = {
23531                 text: "&#169;",
23532                 cls: 'x-edit-none',
23533                 
23534                 menu : {
23535                     items : []
23536                 }
23537             };
23538             for (var i =0; i < this.specialChars.length; i++) {
23539                 smenu.menu.items.push({
23540                     
23541                     html: this.specialChars[i],
23542                     handler: function(a,b) {
23543                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23544                         //editor.insertAtCursor(a.html);
23545                         
23546                     },
23547                     tabIndex:-1
23548                 });
23549             }
23550             
23551             
23552             tb.add(smenu);
23553             
23554             
23555         }
23556         
23557         var cmenu = { };
23558         if (!this.disable.cleanStyles) {
23559             cmenu = {
23560                 cls: 'x-btn-icon x-btn-clear',
23561                 
23562                 menu : {
23563                     items : []
23564                 }
23565             };
23566             for (var i =0; i < this.cleanStyles.length; i++) {
23567                 cmenu.menu.items.push({
23568                     actiontype : this.cleanStyles[i],
23569                     html: 'Remove ' + this.cleanStyles[i],
23570                     handler: function(a,b) {
23571 //                        Roo.log(a);
23572 //                        Roo.log(b);
23573                         var c = Roo.get(editorcore.doc.body);
23574                         c.select('[style]').each(function(s) {
23575                             s.dom.style.removeProperty(a.actiontype);
23576                         });
23577                         editorcore.syncValue();
23578                     },
23579                     tabIndex:-1
23580                 });
23581             }
23582              cmenu.menu.items.push({
23583                 actiontype : 'tablewidths',
23584                 html: 'Remove Table Widths',
23585                 handler: function(a,b) {
23586                     editorcore.cleanTableWidths();
23587                     editorcore.syncValue();
23588                 },
23589                 tabIndex:-1
23590             });
23591             cmenu.menu.items.push({
23592                 actiontype : 'word',
23593                 html: 'Remove MS Word Formating',
23594                 handler: function(a,b) {
23595                     editorcore.cleanWord();
23596                     editorcore.syncValue();
23597                 },
23598                 tabIndex:-1
23599             });
23600             
23601             cmenu.menu.items.push({
23602                 actiontype : 'all',
23603                 html: 'Remove All Styles',
23604                 handler: function(a,b) {
23605                     
23606                     var c = Roo.get(editorcore.doc.body);
23607                     c.select('[style]').each(function(s) {
23608                         s.dom.removeAttribute('style');
23609                     });
23610                     editorcore.syncValue();
23611                 },
23612                 tabIndex:-1
23613             });
23614             
23615             cmenu.menu.items.push({
23616                 actiontype : 'all',
23617                 html: 'Remove All CSS Classes',
23618                 handler: function(a,b) {
23619                     
23620                     var c = Roo.get(editorcore.doc.body);
23621                     c.select('[class]').each(function(s) {
23622                         s.dom.removeAttribute('class');
23623                     });
23624                     editorcore.cleanWord();
23625                     editorcore.syncValue();
23626                 },
23627                 tabIndex:-1
23628             });
23629             
23630              cmenu.menu.items.push({
23631                 actiontype : 'tidy',
23632                 html: 'Tidy HTML Source',
23633                 handler: function(a,b) {
23634                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23635                     editorcore.syncValue();
23636                 },
23637                 tabIndex:-1
23638             });
23639             
23640             
23641             tb.add(cmenu);
23642         }
23643          
23644         if (!this.disable.specialElements) {
23645             var semenu = {
23646                 text: "Other;",
23647                 cls: 'x-edit-none',
23648                 menu : {
23649                     items : []
23650                 }
23651             };
23652             for (var i =0; i < this.specialElements.length; i++) {
23653                 semenu.menu.items.push(
23654                     Roo.apply({ 
23655                         handler: function(a,b) {
23656                             editor.insertAtCursor(this.ihtml);
23657                         }
23658                     }, this.specialElements[i])
23659                 );
23660                     
23661             }
23662             
23663             tb.add(semenu);
23664             
23665             
23666         }
23667          
23668         
23669         if (this.btns) {
23670             for(var i =0; i< this.btns.length;i++) {
23671                 var b = Roo.factory(this.btns[i],Roo.form);
23672                 b.cls =  'x-edit-none';
23673                 
23674                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23675                     b.cls += ' x-init-enable';
23676                 }
23677                 
23678                 b.scope = editorcore;
23679                 tb.add(b);
23680             }
23681         
23682         }
23683         
23684         
23685         
23686         // disable everything...
23687         
23688         this.tb.items.each(function(item){
23689             
23690            if(
23691                 item.id != editorcore.frameId+ '-sourceedit' && 
23692                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23693             ){
23694                 
23695                 item.disable();
23696             }
23697         });
23698         this.rendered = true;
23699         
23700         // the all the btns;
23701         editor.on('editorevent', this.updateToolbar, this);
23702         // other toolbars need to implement this..
23703         //editor.on('editmodechange', this.updateToolbar, this);
23704     },
23705     
23706     
23707     relayBtnCmd : function(btn) {
23708         this.editorcore.relayCmd(btn.cmd);
23709     },
23710     // private used internally
23711     createLink : function(){
23712         Roo.log("create link?");
23713         var url = prompt(this.createLinkText, this.defaultLinkValue);
23714         if(url && url != 'http:/'+'/'){
23715             this.editorcore.relayCmd('createlink', url);
23716         }
23717     },
23718
23719     
23720     /**
23721      * Protected method that will not generally be called directly. It triggers
23722      * a toolbar update by reading the markup state of the current selection in the editor.
23723      */
23724     updateToolbar: function(){
23725
23726         if(!this.editorcore.activated){
23727             this.editor.onFirstFocus();
23728             return;
23729         }
23730
23731         var btns = this.tb.items.map, 
23732             doc = this.editorcore.doc,
23733             frameId = this.editorcore.frameId;
23734
23735         if(!this.disable.font && !Roo.isSafari){
23736             /*
23737             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23738             if(name != this.fontSelect.dom.value){
23739                 this.fontSelect.dom.value = name;
23740             }
23741             */
23742         }
23743         if(!this.disable.format){
23744             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23745             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23746             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23747             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23748         }
23749         if(!this.disable.alignments){
23750             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23751             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23752             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23753         }
23754         if(!Roo.isSafari && !this.disable.lists){
23755             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23756             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23757         }
23758         
23759         var ans = this.editorcore.getAllAncestors();
23760         if (this.formatCombo) {
23761             
23762             
23763             var store = this.formatCombo.store;
23764             this.formatCombo.setValue("");
23765             for (var i =0; i < ans.length;i++) {
23766                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23767                     // select it..
23768                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23769                     break;
23770                 }
23771             }
23772         }
23773         
23774         
23775         
23776         // hides menus... - so this cant be on a menu...
23777         Roo.menu.MenuMgr.hideAll();
23778
23779         //this.editorsyncValue();
23780     },
23781    
23782     
23783     createFontOptions : function(){
23784         var buf = [], fs = this.fontFamilies, ff, lc;
23785         
23786         
23787         
23788         for(var i = 0, len = fs.length; i< len; i++){
23789             ff = fs[i];
23790             lc = ff.toLowerCase();
23791             buf.push(
23792                 '<option value="',lc,'" style="font-family:',ff,';"',
23793                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23794                     ff,
23795                 '</option>'
23796             );
23797         }
23798         return buf.join('');
23799     },
23800     
23801     toggleSourceEdit : function(sourceEditMode){
23802         
23803         Roo.log("toolbar toogle");
23804         if(sourceEditMode === undefined){
23805             sourceEditMode = !this.sourceEditMode;
23806         }
23807         this.sourceEditMode = sourceEditMode === true;
23808         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23809         // just toggle the button?
23810         if(btn.pressed !== this.sourceEditMode){
23811             btn.toggle(this.sourceEditMode);
23812             return;
23813         }
23814         
23815         if(sourceEditMode){
23816             Roo.log("disabling buttons");
23817             this.tb.items.each(function(item){
23818                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23819                     item.disable();
23820                 }
23821             });
23822           
23823         }else{
23824             Roo.log("enabling buttons");
23825             if(this.editorcore.initialized){
23826                 this.tb.items.each(function(item){
23827                     item.enable();
23828                 });
23829             }
23830             
23831         }
23832         Roo.log("calling toggole on editor");
23833         // tell the editor that it's been pressed..
23834         this.editor.toggleSourceEdit(sourceEditMode);
23835        
23836     },
23837      /**
23838      * Object collection of toolbar tooltips for the buttons in the editor. The key
23839      * is the command id associated with that button and the value is a valid QuickTips object.
23840      * For example:
23841 <pre><code>
23842 {
23843     bold : {
23844         title: 'Bold (Ctrl+B)',
23845         text: 'Make the selected text bold.',
23846         cls: 'x-html-editor-tip'
23847     },
23848     italic : {
23849         title: 'Italic (Ctrl+I)',
23850         text: 'Make the selected text italic.',
23851         cls: 'x-html-editor-tip'
23852     },
23853     ...
23854 </code></pre>
23855     * @type Object
23856      */
23857     buttonTips : {
23858         bold : {
23859             title: 'Bold (Ctrl+B)',
23860             text: 'Make the selected text bold.',
23861             cls: 'x-html-editor-tip'
23862         },
23863         italic : {
23864             title: 'Italic (Ctrl+I)',
23865             text: 'Make the selected text italic.',
23866             cls: 'x-html-editor-tip'
23867         },
23868         underline : {
23869             title: 'Underline (Ctrl+U)',
23870             text: 'Underline the selected text.',
23871             cls: 'x-html-editor-tip'
23872         },
23873         strikethrough : {
23874             title: 'Strikethrough',
23875             text: 'Strikethrough the selected text.',
23876             cls: 'x-html-editor-tip'
23877         },
23878         increasefontsize : {
23879             title: 'Grow Text',
23880             text: 'Increase the font size.',
23881             cls: 'x-html-editor-tip'
23882         },
23883         decreasefontsize : {
23884             title: 'Shrink Text',
23885             text: 'Decrease the font size.',
23886             cls: 'x-html-editor-tip'
23887         },
23888         backcolor : {
23889             title: 'Text Highlight Color',
23890             text: 'Change the background color of the selected text.',
23891             cls: 'x-html-editor-tip'
23892         },
23893         forecolor : {
23894             title: 'Font Color',
23895             text: 'Change the color of the selected text.',
23896             cls: 'x-html-editor-tip'
23897         },
23898         justifyleft : {
23899             title: 'Align Text Left',
23900             text: 'Align text to the left.',
23901             cls: 'x-html-editor-tip'
23902         },
23903         justifycenter : {
23904             title: 'Center Text',
23905             text: 'Center text in the editor.',
23906             cls: 'x-html-editor-tip'
23907         },
23908         justifyright : {
23909             title: 'Align Text Right',
23910             text: 'Align text to the right.',
23911             cls: 'x-html-editor-tip'
23912         },
23913         insertunorderedlist : {
23914             title: 'Bullet List',
23915             text: 'Start a bulleted list.',
23916             cls: 'x-html-editor-tip'
23917         },
23918         insertorderedlist : {
23919             title: 'Numbered List',
23920             text: 'Start a numbered list.',
23921             cls: 'x-html-editor-tip'
23922         },
23923         createlink : {
23924             title: 'Hyperlink',
23925             text: 'Make the selected text a hyperlink.',
23926             cls: 'x-html-editor-tip'
23927         },
23928         sourceedit : {
23929             title: 'Source Edit',
23930             text: 'Switch to source editing mode.',
23931             cls: 'x-html-editor-tip'
23932         }
23933     },
23934     // private
23935     onDestroy : function(){
23936         if(this.rendered){
23937             
23938             this.tb.items.each(function(item){
23939                 if(item.menu){
23940                     item.menu.removeAll();
23941                     if(item.menu.el){
23942                         item.menu.el.destroy();
23943                     }
23944                 }
23945                 item.destroy();
23946             });
23947              
23948         }
23949     },
23950     onFirstFocus: function() {
23951         this.tb.items.each(function(item){
23952            item.enable();
23953         });
23954     }
23955 });
23956
23957
23958
23959
23960 // <script type="text/javascript">
23961 /*
23962  * Based on
23963  * Ext JS Library 1.1.1
23964  * Copyright(c) 2006-2007, Ext JS, LLC.
23965  *  
23966  
23967  */
23968
23969  
23970 /**
23971  * @class Roo.form.HtmlEditor.ToolbarContext
23972  * Context Toolbar
23973  * 
23974  * Usage:
23975  *
23976  new Roo.form.HtmlEditor({
23977     ....
23978     toolbars : [
23979         { xtype: 'ToolbarStandard', styles : {} }
23980         { xtype: 'ToolbarContext', disable : {} }
23981     ]
23982 })
23983
23984      
23985  * 
23986  * @config : {Object} disable List of elements to disable.. (not done yet.)
23987  * @config : {Object} styles  Map of styles available.
23988  * 
23989  */
23990
23991 Roo.form.HtmlEditor.ToolbarContext = function(config)
23992 {
23993     
23994     Roo.apply(this, config);
23995     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23996     // dont call parent... till later.
23997     this.styles = this.styles || {};
23998 }
23999
24000  
24001
24002 Roo.form.HtmlEditor.ToolbarContext.types = {
24003     'IMG' : {
24004         width : {
24005             title: "Width",
24006             width: 40
24007         },
24008         height:  {
24009             title: "Height",
24010             width: 40
24011         },
24012         align: {
24013             title: "Align",
24014             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24015             width : 80
24016             
24017         },
24018         border: {
24019             title: "Border",
24020             width: 40
24021         },
24022         alt: {
24023             title: "Alt",
24024             width: 120
24025         },
24026         src : {
24027             title: "Src",
24028             width: 220
24029         }
24030         
24031     },
24032     'A' : {
24033         name : {
24034             title: "Name",
24035             width: 50
24036         },
24037         target:  {
24038             title: "Target",
24039             width: 120
24040         },
24041         href:  {
24042             title: "Href",
24043             width: 220
24044         } // border?
24045         
24046     },
24047     'TABLE' : {
24048         rows : {
24049             title: "Rows",
24050             width: 20
24051         },
24052         cols : {
24053             title: "Cols",
24054             width: 20
24055         },
24056         width : {
24057             title: "Width",
24058             width: 40
24059         },
24060         height : {
24061             title: "Height",
24062             width: 40
24063         },
24064         border : {
24065             title: "Border",
24066             width: 20
24067         }
24068     },
24069     'TD' : {
24070         width : {
24071             title: "Width",
24072             width: 40
24073         },
24074         height : {
24075             title: "Height",
24076             width: 40
24077         },   
24078         align: {
24079             title: "Align",
24080             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24081             width: 80
24082         },
24083         valign: {
24084             title: "Valign",
24085             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24086             width: 80
24087         },
24088         colspan: {
24089             title: "Colspan",
24090             width: 20
24091             
24092         },
24093          'font-family'  : {
24094             title : "Font",
24095             style : 'fontFamily',
24096             displayField: 'display',
24097             optname : 'font-family',
24098             width: 140
24099         }
24100     },
24101     'INPUT' : {
24102         name : {
24103             title: "name",
24104             width: 120
24105         },
24106         value : {
24107             title: "Value",
24108             width: 120
24109         },
24110         width : {
24111             title: "Width",
24112             width: 40
24113         }
24114     },
24115     'LABEL' : {
24116         'for' : {
24117             title: "For",
24118             width: 120
24119         }
24120     },
24121     'TEXTAREA' : {
24122           name : {
24123             title: "name",
24124             width: 120
24125         },
24126         rows : {
24127             title: "Rows",
24128             width: 20
24129         },
24130         cols : {
24131             title: "Cols",
24132             width: 20
24133         }
24134     },
24135     'SELECT' : {
24136         name : {
24137             title: "name",
24138             width: 120
24139         },
24140         selectoptions : {
24141             title: "Options",
24142             width: 200
24143         }
24144     },
24145     
24146     // should we really allow this??
24147     // should this just be 
24148     'BODY' : {
24149         title : {
24150             title: "Title",
24151             width: 200,
24152             disabled : true
24153         }
24154     },
24155     'SPAN' : {
24156         'font-family'  : {
24157             title : "Font",
24158             style : 'fontFamily',
24159             displayField: 'display',
24160             optname : 'font-family',
24161             width: 140
24162         }
24163     },
24164     'DIV' : {
24165         'font-family'  : {
24166             title : "Font",
24167             style : 'fontFamily',
24168             displayField: 'display',
24169             optname : 'font-family',
24170             width: 140
24171         }
24172     },
24173      'P' : {
24174         'font-family'  : {
24175             title : "Font",
24176             style : 'fontFamily',
24177             displayField: 'display',
24178             optname : 'font-family',
24179             width: 140
24180         }
24181     },
24182     
24183     '*' : {
24184         // empty..
24185     }
24186
24187 };
24188
24189 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24190 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24191
24192 Roo.form.HtmlEditor.ToolbarContext.options = {
24193         'font-family'  : [ 
24194                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24195                 [ 'Courier New', 'Courier New'],
24196                 [ 'Tahoma', 'Tahoma'],
24197                 [ 'Times New Roman,serif', 'Times'],
24198                 [ 'Verdana','Verdana' ]
24199         ]
24200 };
24201
24202 // fixme - these need to be configurable..
24203  
24204
24205 //Roo.form.HtmlEditor.ToolbarContext.types
24206
24207
24208 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24209     
24210     tb: false,
24211     
24212     rendered: false,
24213     
24214     editor : false,
24215     editorcore : false,
24216     /**
24217      * @cfg {Object} disable  List of toolbar elements to disable
24218          
24219      */
24220     disable : false,
24221     /**
24222      * @cfg {Object} styles List of styles 
24223      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24224      *
24225      * These must be defined in the page, so they get rendered correctly..
24226      * .headline { }
24227      * TD.underline { }
24228      * 
24229      */
24230     styles : false,
24231     
24232     options: false,
24233     
24234     toolbars : false,
24235     
24236     init : function(editor)
24237     {
24238         this.editor = editor;
24239         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24240         var editorcore = this.editorcore;
24241         
24242         var fid = editorcore.frameId;
24243         var etb = this;
24244         function btn(id, toggle, handler){
24245             var xid = fid + '-'+ id ;
24246             return {
24247                 id : xid,
24248                 cmd : id,
24249                 cls : 'x-btn-icon x-edit-'+id,
24250                 enableToggle:toggle !== false,
24251                 scope: editorcore, // was editor...
24252                 handler:handler||editorcore.relayBtnCmd,
24253                 clickEvent:'mousedown',
24254                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24255                 tabIndex:-1
24256             };
24257         }
24258         // create a new element.
24259         var wdiv = editor.wrap.createChild({
24260                 tag: 'div'
24261             }, editor.wrap.dom.firstChild.nextSibling, true);
24262         
24263         // can we do this more than once??
24264         
24265          // stop form submits
24266       
24267  
24268         // disable everything...
24269         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24270         this.toolbars = {};
24271            
24272         for (var i in  ty) {
24273           
24274             this.toolbars[i] = this.buildToolbar(ty[i],i);
24275         }
24276         this.tb = this.toolbars.BODY;
24277         this.tb.el.show();
24278         this.buildFooter();
24279         this.footer.show();
24280         editor.on('hide', function( ) { this.footer.hide() }, this);
24281         editor.on('show', function( ) { this.footer.show() }, this);
24282         
24283          
24284         this.rendered = true;
24285         
24286         // the all the btns;
24287         editor.on('editorevent', this.updateToolbar, this);
24288         // other toolbars need to implement this..
24289         //editor.on('editmodechange', this.updateToolbar, this);
24290     },
24291     
24292     
24293     
24294     /**
24295      * Protected method that will not generally be called directly. It triggers
24296      * a toolbar update by reading the markup state of the current selection in the editor.
24297      *
24298      * Note you can force an update by calling on('editorevent', scope, false)
24299      */
24300     updateToolbar: function(editor,ev,sel){
24301
24302         //Roo.log(ev);
24303         // capture mouse up - this is handy for selecting images..
24304         // perhaps should go somewhere else...
24305         if(!this.editorcore.activated){
24306              this.editor.onFirstFocus();
24307             return;
24308         }
24309         
24310         
24311         
24312         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24313         // selectNode - might want to handle IE?
24314         if (ev &&
24315             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24316             ev.target && ev.target.tagName == 'IMG') {
24317             // they have click on an image...
24318             // let's see if we can change the selection...
24319             sel = ev.target;
24320          
24321               var nodeRange = sel.ownerDocument.createRange();
24322             try {
24323                 nodeRange.selectNode(sel);
24324             } catch (e) {
24325                 nodeRange.selectNodeContents(sel);
24326             }
24327             //nodeRange.collapse(true);
24328             var s = this.editorcore.win.getSelection();
24329             s.removeAllRanges();
24330             s.addRange(nodeRange);
24331         }  
24332         
24333       
24334         var updateFooter = sel ? false : true;
24335         
24336         
24337         var ans = this.editorcore.getAllAncestors();
24338         
24339         // pick
24340         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24341         
24342         if (!sel) { 
24343             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24344             sel = sel ? sel : this.editorcore.doc.body;
24345             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24346             
24347         }
24348         // pick a menu that exists..
24349         var tn = sel.tagName.toUpperCase();
24350         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24351         
24352         tn = sel.tagName.toUpperCase();
24353         
24354         var lastSel = this.tb.selectedNode;
24355         
24356         this.tb.selectedNode = sel;
24357         
24358         // if current menu does not match..
24359         
24360         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24361                 
24362             this.tb.el.hide();
24363             ///console.log("show: " + tn);
24364             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24365             this.tb.el.show();
24366             // update name
24367             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24368             
24369             
24370             // update attributes
24371             if (this.tb.fields) {
24372                 this.tb.fields.each(function(e) {
24373                     if (e.stylename) {
24374                         e.setValue(sel.style[e.stylename]);
24375                         return;
24376                     } 
24377                    e.setValue(sel.getAttribute(e.attrname));
24378                 });
24379             }
24380             
24381             var hasStyles = false;
24382             for(var i in this.styles) {
24383                 hasStyles = true;
24384                 break;
24385             }
24386             
24387             // update styles
24388             if (hasStyles) { 
24389                 var st = this.tb.fields.item(0);
24390                 
24391                 st.store.removeAll();
24392                
24393                 
24394                 var cn = sel.className.split(/\s+/);
24395                 
24396                 var avs = [];
24397                 if (this.styles['*']) {
24398                     
24399                     Roo.each(this.styles['*'], function(v) {
24400                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24401                     });
24402                 }
24403                 if (this.styles[tn]) { 
24404                     Roo.each(this.styles[tn], function(v) {
24405                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24406                     });
24407                 }
24408                 
24409                 st.store.loadData(avs);
24410                 st.collapse();
24411                 st.setValue(cn);
24412             }
24413             // flag our selected Node.
24414             this.tb.selectedNode = sel;
24415            
24416            
24417             Roo.menu.MenuMgr.hideAll();
24418
24419         }
24420         
24421         if (!updateFooter) {
24422             //this.footDisp.dom.innerHTML = ''; 
24423             return;
24424         }
24425         // update the footer
24426         //
24427         var html = '';
24428         
24429         this.footerEls = ans.reverse();
24430         Roo.each(this.footerEls, function(a,i) {
24431             if (!a) { return; }
24432             html += html.length ? ' &gt; '  :  '';
24433             
24434             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24435             
24436         });
24437        
24438         // 
24439         var sz = this.footDisp.up('td').getSize();
24440         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24441         this.footDisp.dom.style.marginLeft = '5px';
24442         
24443         this.footDisp.dom.style.overflow = 'hidden';
24444         
24445         this.footDisp.dom.innerHTML = html;
24446             
24447         //this.editorsyncValue();
24448     },
24449      
24450     
24451    
24452        
24453     // private
24454     onDestroy : function(){
24455         if(this.rendered){
24456             
24457             this.tb.items.each(function(item){
24458                 if(item.menu){
24459                     item.menu.removeAll();
24460                     if(item.menu.el){
24461                         item.menu.el.destroy();
24462                     }
24463                 }
24464                 item.destroy();
24465             });
24466              
24467         }
24468     },
24469     onFirstFocus: function() {
24470         // need to do this for all the toolbars..
24471         this.tb.items.each(function(item){
24472            item.enable();
24473         });
24474     },
24475     buildToolbar: function(tlist, nm)
24476     {
24477         var editor = this.editor;
24478         var editorcore = this.editorcore;
24479          // create a new element.
24480         var wdiv = editor.wrap.createChild({
24481                 tag: 'div'
24482             }, editor.wrap.dom.firstChild.nextSibling, true);
24483         
24484        
24485         var tb = new Roo.Toolbar(wdiv);
24486         // add the name..
24487         
24488         tb.add(nm+ ":&nbsp;");
24489         
24490         var styles = [];
24491         for(var i in this.styles) {
24492             styles.push(i);
24493         }
24494         
24495         // styles...
24496         if (styles && styles.length) {
24497             
24498             // this needs a multi-select checkbox...
24499             tb.addField( new Roo.form.ComboBox({
24500                 store: new Roo.data.SimpleStore({
24501                     id : 'val',
24502                     fields: ['val', 'selected'],
24503                     data : [] 
24504                 }),
24505                 name : '-roo-edit-className',
24506                 attrname : 'className',
24507                 displayField: 'val',
24508                 typeAhead: false,
24509                 mode: 'local',
24510                 editable : false,
24511                 triggerAction: 'all',
24512                 emptyText:'Select Style',
24513                 selectOnFocus:true,
24514                 width: 130,
24515                 listeners : {
24516                     'select': function(c, r, i) {
24517                         // initial support only for on class per el..
24518                         tb.selectedNode.className =  r ? r.get('val') : '';
24519                         editorcore.syncValue();
24520                     }
24521                 }
24522     
24523             }));
24524         }
24525         
24526         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24527         var tbops = tbc.options;
24528         
24529         for (var i in tlist) {
24530             
24531             var item = tlist[i];
24532             tb.add(item.title + ":&nbsp;");
24533             
24534             
24535             //optname == used so you can configure the options available..
24536             var opts = item.opts ? item.opts : false;
24537             if (item.optname) {
24538                 opts = tbops[item.optname];
24539            
24540             }
24541             
24542             if (opts) {
24543                 // opts == pulldown..
24544                 tb.addField( new Roo.form.ComboBox({
24545                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24546                         id : 'val',
24547                         fields: ['val', 'display'],
24548                         data : opts  
24549                     }),
24550                     name : '-roo-edit-' + i,
24551                     attrname : i,
24552                     stylename : item.style ? item.style : false,
24553                     displayField: item.displayField ? item.displayField : 'val',
24554                     valueField :  'val',
24555                     typeAhead: false,
24556                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24557                     editable : false,
24558                     triggerAction: 'all',
24559                     emptyText:'Select',
24560                     selectOnFocus:true,
24561                     width: item.width ? item.width  : 130,
24562                     listeners : {
24563                         'select': function(c, r, i) {
24564                             if (c.stylename) {
24565                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24566                                 return;
24567                             }
24568                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24569                         }
24570                     }
24571
24572                 }));
24573                 continue;
24574                     
24575                  
24576                 
24577                 tb.addField( new Roo.form.TextField({
24578                     name: i,
24579                     width: 100,
24580                     //allowBlank:false,
24581                     value: ''
24582                 }));
24583                 continue;
24584             }
24585             tb.addField( new Roo.form.TextField({
24586                 name: '-roo-edit-' + i,
24587                 attrname : i,
24588                 
24589                 width: item.width,
24590                 //allowBlank:true,
24591                 value: '',
24592                 listeners: {
24593                     'change' : function(f, nv, ov) {
24594                         tb.selectedNode.setAttribute(f.attrname, nv);
24595                         editorcore.syncValue();
24596                     }
24597                 }
24598             }));
24599              
24600         }
24601         
24602         var _this = this;
24603         
24604         if(nm == 'BODY'){
24605             tb.addSeparator();
24606         
24607             tb.addButton( {
24608                 text: 'Stylesheets',
24609
24610                 listeners : {
24611                     click : function ()
24612                     {
24613                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24614                     }
24615                 }
24616             });
24617         }
24618         
24619         tb.addFill();
24620         tb.addButton( {
24621             text: 'Remove Tag',
24622     
24623             listeners : {
24624                 click : function ()
24625                 {
24626                     // remove
24627                     // undo does not work.
24628                      
24629                     var sn = tb.selectedNode;
24630                     
24631                     var pn = sn.parentNode;
24632                     
24633                     var stn =  sn.childNodes[0];
24634                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24635                     while (sn.childNodes.length) {
24636                         var node = sn.childNodes[0];
24637                         sn.removeChild(node);
24638                         //Roo.log(node);
24639                         pn.insertBefore(node, sn);
24640                         
24641                     }
24642                     pn.removeChild(sn);
24643                     var range = editorcore.createRange();
24644         
24645                     range.setStart(stn,0);
24646                     range.setEnd(en,0); //????
24647                     //range.selectNode(sel);
24648                     
24649                     
24650                     var selection = editorcore.getSelection();
24651                     selection.removeAllRanges();
24652                     selection.addRange(range);
24653                     
24654                     
24655                     
24656                     //_this.updateToolbar(null, null, pn);
24657                     _this.updateToolbar(null, null, null);
24658                     _this.footDisp.dom.innerHTML = ''; 
24659                 }
24660             }
24661             
24662                     
24663                 
24664             
24665         });
24666         
24667         
24668         tb.el.on('click', function(e){
24669             e.preventDefault(); // what does this do?
24670         });
24671         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24672         tb.el.hide();
24673         tb.name = nm;
24674         // dont need to disable them... as they will get hidden
24675         return tb;
24676          
24677         
24678     },
24679     buildFooter : function()
24680     {
24681         
24682         var fel = this.editor.wrap.createChild();
24683         this.footer = new Roo.Toolbar(fel);
24684         // toolbar has scrolly on left / right?
24685         var footDisp= new Roo.Toolbar.Fill();
24686         var _t = this;
24687         this.footer.add(
24688             {
24689                 text : '&lt;',
24690                 xtype: 'Button',
24691                 handler : function() {
24692                     _t.footDisp.scrollTo('left',0,true)
24693                 }
24694             }
24695         );
24696         this.footer.add( footDisp );
24697         this.footer.add( 
24698             {
24699                 text : '&gt;',
24700                 xtype: 'Button',
24701                 handler : function() {
24702                     // no animation..
24703                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24704                 }
24705             }
24706         );
24707         var fel = Roo.get(footDisp.el);
24708         fel.addClass('x-editor-context');
24709         this.footDispWrap = fel; 
24710         this.footDispWrap.overflow  = 'hidden';
24711         
24712         this.footDisp = fel.createChild();
24713         this.footDispWrap.on('click', this.onContextClick, this)
24714         
24715         
24716     },
24717     onContextClick : function (ev,dom)
24718     {
24719         ev.preventDefault();
24720         var  cn = dom.className;
24721         //Roo.log(cn);
24722         if (!cn.match(/x-ed-loc-/)) {
24723             return;
24724         }
24725         var n = cn.split('-').pop();
24726         var ans = this.footerEls;
24727         var sel = ans[n];
24728         
24729          // pick
24730         var range = this.editorcore.createRange();
24731         
24732         range.selectNodeContents(sel);
24733         //range.selectNode(sel);
24734         
24735         
24736         var selection = this.editorcore.getSelection();
24737         selection.removeAllRanges();
24738         selection.addRange(range);
24739         
24740         
24741         
24742         this.updateToolbar(null, null, sel);
24743         
24744         
24745     }
24746     
24747     
24748     
24749     
24750     
24751 });
24752
24753
24754
24755
24756
24757 /*
24758  * Based on:
24759  * Ext JS Library 1.1.1
24760  * Copyright(c) 2006-2007, Ext JS, LLC.
24761  *
24762  * Originally Released Under LGPL - original licence link has changed is not relivant.
24763  *
24764  * Fork - LGPL
24765  * <script type="text/javascript">
24766  */
24767  
24768 /**
24769  * @class Roo.form.BasicForm
24770  * @extends Roo.util.Observable
24771  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24772  * @constructor
24773  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24774  * @param {Object} config Configuration options
24775  */
24776 Roo.form.BasicForm = function(el, config){
24777     this.allItems = [];
24778     this.childForms = [];
24779     Roo.apply(this, config);
24780     /*
24781      * The Roo.form.Field items in this form.
24782      * @type MixedCollection
24783      */
24784      
24785      
24786     this.items = new Roo.util.MixedCollection(false, function(o){
24787         return o.id || (o.id = Roo.id());
24788     });
24789     this.addEvents({
24790         /**
24791          * @event beforeaction
24792          * Fires before any action is performed. Return false to cancel the action.
24793          * @param {Form} this
24794          * @param {Action} action The action to be performed
24795          */
24796         beforeaction: true,
24797         /**
24798          * @event actionfailed
24799          * Fires when an action fails.
24800          * @param {Form} this
24801          * @param {Action} action The action that failed
24802          */
24803         actionfailed : true,
24804         /**
24805          * @event actioncomplete
24806          * Fires when an action is completed.
24807          * @param {Form} this
24808          * @param {Action} action The action that completed
24809          */
24810         actioncomplete : true
24811     });
24812     if(el){
24813         this.initEl(el);
24814     }
24815     Roo.form.BasicForm.superclass.constructor.call(this);
24816     
24817     Roo.form.BasicForm.popover.apply();
24818 };
24819
24820 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24821     /**
24822      * @cfg {String} method
24823      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24824      */
24825     /**
24826      * @cfg {DataReader} reader
24827      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24828      * This is optional as there is built-in support for processing JSON.
24829      */
24830     /**
24831      * @cfg {DataReader} errorReader
24832      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24833      * This is completely optional as there is built-in support for processing JSON.
24834      */
24835     /**
24836      * @cfg {String} url
24837      * The URL to use for form actions if one isn't supplied in the action options.
24838      */
24839     /**
24840      * @cfg {Boolean} fileUpload
24841      * Set to true if this form is a file upload.
24842      */
24843      
24844     /**
24845      * @cfg {Object} baseParams
24846      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24847      */
24848      /**
24849      
24850     /**
24851      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24852      */
24853     timeout: 30,
24854
24855     // private
24856     activeAction : null,
24857
24858     /**
24859      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24860      * or setValues() data instead of when the form was first created.
24861      */
24862     trackResetOnLoad : false,
24863     
24864     
24865     /**
24866      * childForms - used for multi-tab forms
24867      * @type {Array}
24868      */
24869     childForms : false,
24870     
24871     /**
24872      * allItems - full list of fields.
24873      * @type {Array}
24874      */
24875     allItems : false,
24876     
24877     /**
24878      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24879      * element by passing it or its id or mask the form itself by passing in true.
24880      * @type Mixed
24881      */
24882     waitMsgTarget : false,
24883     
24884     /**
24885      * @type Boolean
24886      */
24887     disableMask : false,
24888     
24889     /**
24890      * @cfg {Boolean} errorMask (true|false) default false
24891      */
24892     errorMask : false,
24893     
24894     /**
24895      * @cfg {Number} maskOffset Default 100
24896      */
24897     maskOffset : 100,
24898
24899     // private
24900     initEl : function(el){
24901         this.el = Roo.get(el);
24902         this.id = this.el.id || Roo.id();
24903         this.el.on('submit', this.onSubmit, this);
24904         this.el.addClass('x-form');
24905     },
24906
24907     // private
24908     onSubmit : function(e){
24909         e.stopEvent();
24910     },
24911
24912     /**
24913      * Returns true if client-side validation on the form is successful.
24914      * @return Boolean
24915      */
24916     isValid : function(){
24917         var valid = true;
24918         var target = false;
24919         this.items.each(function(f){
24920             if(f.validate()){
24921                 return;
24922             }
24923             
24924             valid = false;
24925                 
24926             if(!target && f.el.isVisible(true)){
24927                 target = f;
24928             }
24929         });
24930         
24931         if(this.errorMask && !valid){
24932             Roo.form.BasicForm.popover.mask(this, target);
24933         }
24934         
24935         return valid;
24936     },
24937
24938     /**
24939      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24940      * @return Boolean
24941      */
24942     isDirty : function(){
24943         var dirty = false;
24944         this.items.each(function(f){
24945            if(f.isDirty()){
24946                dirty = true;
24947                return false;
24948            }
24949         });
24950         return dirty;
24951     },
24952     
24953     /**
24954      * Returns true if any fields in this form have changed since their original load. (New version)
24955      * @return Boolean
24956      */
24957     
24958     hasChanged : function()
24959     {
24960         var dirty = false;
24961         this.items.each(function(f){
24962            if(f.hasChanged()){
24963                dirty = true;
24964                return false;
24965            }
24966         });
24967         return dirty;
24968         
24969     },
24970     /**
24971      * Resets all hasChanged to 'false' -
24972      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24973      * So hasChanged storage is only to be used for this purpose
24974      * @return Boolean
24975      */
24976     resetHasChanged : function()
24977     {
24978         this.items.each(function(f){
24979            f.resetHasChanged();
24980         });
24981         
24982     },
24983     
24984     
24985     /**
24986      * Performs a predefined action (submit or load) or custom actions you define on this form.
24987      * @param {String} actionName The name of the action type
24988      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24989      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24990      * accept other config options):
24991      * <pre>
24992 Property          Type             Description
24993 ----------------  ---------------  ----------------------------------------------------------------------------------
24994 url               String           The url for the action (defaults to the form's url)
24995 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24996 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24997 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24998                                    validate the form on the client (defaults to false)
24999      * </pre>
25000      * @return {BasicForm} this
25001      */
25002     doAction : function(action, options){
25003         if(typeof action == 'string'){
25004             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25005         }
25006         if(this.fireEvent('beforeaction', this, action) !== false){
25007             this.beforeAction(action);
25008             action.run.defer(100, action);
25009         }
25010         return this;
25011     },
25012
25013     /**
25014      * Shortcut to do a submit action.
25015      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25016      * @return {BasicForm} this
25017      */
25018     submit : function(options){
25019         this.doAction('submit', options);
25020         return this;
25021     },
25022
25023     /**
25024      * Shortcut to do a load action.
25025      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25026      * @return {BasicForm} this
25027      */
25028     load : function(options){
25029         this.doAction('load', options);
25030         return this;
25031     },
25032
25033     /**
25034      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25035      * @param {Record} record The record to edit
25036      * @return {BasicForm} this
25037      */
25038     updateRecord : function(record){
25039         record.beginEdit();
25040         var fs = record.fields;
25041         fs.each(function(f){
25042             var field = this.findField(f.name);
25043             if(field){
25044                 record.set(f.name, field.getValue());
25045             }
25046         }, this);
25047         record.endEdit();
25048         return this;
25049     },
25050
25051     /**
25052      * Loads an Roo.data.Record into this form.
25053      * @param {Record} record The record to load
25054      * @return {BasicForm} this
25055      */
25056     loadRecord : function(record){
25057         this.setValues(record.data);
25058         return this;
25059     },
25060
25061     // private
25062     beforeAction : function(action){
25063         var o = action.options;
25064         
25065         if(!this.disableMask) {
25066             if(this.waitMsgTarget === true){
25067                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25068             }else if(this.waitMsgTarget){
25069                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25070                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25071             }else {
25072                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25073             }
25074         }
25075         
25076          
25077     },
25078
25079     // private
25080     afterAction : function(action, success){
25081         this.activeAction = null;
25082         var o = action.options;
25083         
25084         if(!this.disableMask) {
25085             if(this.waitMsgTarget === true){
25086                 this.el.unmask();
25087             }else if(this.waitMsgTarget){
25088                 this.waitMsgTarget.unmask();
25089             }else{
25090                 Roo.MessageBox.updateProgress(1);
25091                 Roo.MessageBox.hide();
25092             }
25093         }
25094         
25095         if(success){
25096             if(o.reset){
25097                 this.reset();
25098             }
25099             Roo.callback(o.success, o.scope, [this, action]);
25100             this.fireEvent('actioncomplete', this, action);
25101             
25102         }else{
25103             
25104             // failure condition..
25105             // we have a scenario where updates need confirming.
25106             // eg. if a locking scenario exists..
25107             // we look for { errors : { needs_confirm : true }} in the response.
25108             if (
25109                 (typeof(action.result) != 'undefined')  &&
25110                 (typeof(action.result.errors) != 'undefined')  &&
25111                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25112            ){
25113                 var _t = this;
25114                 Roo.MessageBox.confirm(
25115                     "Change requires confirmation",
25116                     action.result.errorMsg,
25117                     function(r) {
25118                         if (r != 'yes') {
25119                             return;
25120                         }
25121                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25122                     }
25123                     
25124                 );
25125                 
25126                 
25127                 
25128                 return;
25129             }
25130             
25131             Roo.callback(o.failure, o.scope, [this, action]);
25132             // show an error message if no failed handler is set..
25133             if (!this.hasListener('actionfailed')) {
25134                 Roo.MessageBox.alert("Error",
25135                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25136                         action.result.errorMsg :
25137                         "Saving Failed, please check your entries or try again"
25138                 );
25139             }
25140             
25141             this.fireEvent('actionfailed', this, action);
25142         }
25143         
25144     },
25145
25146     /**
25147      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25148      * @param {String} id The value to search for
25149      * @return Field
25150      */
25151     findField : function(id){
25152         var field = this.items.get(id);
25153         if(!field){
25154             this.items.each(function(f){
25155                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25156                     field = f;
25157                     return false;
25158                 }
25159             });
25160         }
25161         return field || null;
25162     },
25163
25164     /**
25165      * Add a secondary form to this one, 
25166      * Used to provide tabbed forms. One form is primary, with hidden values 
25167      * which mirror the elements from the other forms.
25168      * 
25169      * @param {Roo.form.Form} form to add.
25170      * 
25171      */
25172     addForm : function(form)
25173     {
25174        
25175         if (this.childForms.indexOf(form) > -1) {
25176             // already added..
25177             return;
25178         }
25179         this.childForms.push(form);
25180         var n = '';
25181         Roo.each(form.allItems, function (fe) {
25182             
25183             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25184             if (this.findField(n)) { // already added..
25185                 return;
25186             }
25187             var add = new Roo.form.Hidden({
25188                 name : n
25189             });
25190             add.render(this.el);
25191             
25192             this.add( add );
25193         }, this);
25194         
25195     },
25196     /**
25197      * Mark fields in this form invalid in bulk.
25198      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25199      * @return {BasicForm} this
25200      */
25201     markInvalid : function(errors){
25202         if(errors instanceof Array){
25203             for(var i = 0, len = errors.length; i < len; i++){
25204                 var fieldError = errors[i];
25205                 var f = this.findField(fieldError.id);
25206                 if(f){
25207                     f.markInvalid(fieldError.msg);
25208                 }
25209             }
25210         }else{
25211             var field, id;
25212             for(id in errors){
25213                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25214                     field.markInvalid(errors[id]);
25215                 }
25216             }
25217         }
25218         Roo.each(this.childForms || [], function (f) {
25219             f.markInvalid(errors);
25220         });
25221         
25222         return this;
25223     },
25224
25225     /**
25226      * Set values for fields in this form in bulk.
25227      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25228      * @return {BasicForm} this
25229      */
25230     setValues : function(values){
25231         if(values instanceof Array){ // array of objects
25232             for(var i = 0, len = values.length; i < len; i++){
25233                 var v = values[i];
25234                 var f = this.findField(v.id);
25235                 if(f){
25236                     f.setValue(v.value);
25237                     if(this.trackResetOnLoad){
25238                         f.originalValue = f.getValue();
25239                     }
25240                 }
25241             }
25242         }else{ // object hash
25243             var field, id;
25244             for(id in values){
25245                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25246                     
25247                     if (field.setFromData && 
25248                         field.valueField && 
25249                         field.displayField &&
25250                         // combos' with local stores can 
25251                         // be queried via setValue()
25252                         // to set their value..
25253                         (field.store && !field.store.isLocal)
25254                         ) {
25255                         // it's a combo
25256                         var sd = { };
25257                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25258                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25259                         field.setFromData(sd);
25260                         
25261                     } else {
25262                         field.setValue(values[id]);
25263                     }
25264                     
25265                     
25266                     if(this.trackResetOnLoad){
25267                         field.originalValue = field.getValue();
25268                     }
25269                 }
25270             }
25271         }
25272         this.resetHasChanged();
25273         
25274         
25275         Roo.each(this.childForms || [], function (f) {
25276             f.setValues(values);
25277             f.resetHasChanged();
25278         });
25279                 
25280         return this;
25281     },
25282  
25283     /**
25284      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25285      * they are returned as an array.
25286      * @param {Boolean} asString
25287      * @return {Object}
25288      */
25289     getValues : function(asString){
25290         if (this.childForms) {
25291             // copy values from the child forms
25292             Roo.each(this.childForms, function (f) {
25293                 this.setValues(f.getValues());
25294             }, this);
25295         }
25296         
25297         // use formdata
25298         if (typeof(FormData) != 'undefined' && asString !== true) {
25299             var fd = (new FormData(this.el.dom)).entries();
25300             var ret = {};
25301             var ent = fd.next();
25302             while (!ent.done) {
25303                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25304                 ent = fd.next();
25305             };
25306             return ret;
25307         }
25308         
25309         
25310         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25311         if(asString === true){
25312             return fs;
25313         }
25314         return Roo.urlDecode(fs);
25315     },
25316     
25317     /**
25318      * Returns the fields in this form as an object with key/value pairs. 
25319      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25320      * @return {Object}
25321      */
25322     getFieldValues : function(with_hidden)
25323     {
25324         if (this.childForms) {
25325             // copy values from the child forms
25326             // should this call getFieldValues - probably not as we do not currently copy
25327             // hidden fields when we generate..
25328             Roo.each(this.childForms, function (f) {
25329                 this.setValues(f.getValues());
25330             }, this);
25331         }
25332         
25333         var ret = {};
25334         this.items.each(function(f){
25335             if (!f.getName()) {
25336                 return;
25337             }
25338             var v = f.getValue();
25339             if (f.inputType =='radio') {
25340                 if (typeof(ret[f.getName()]) == 'undefined') {
25341                     ret[f.getName()] = ''; // empty..
25342                 }
25343                 
25344                 if (!f.el.dom.checked) {
25345                     return;
25346                     
25347                 }
25348                 v = f.el.dom.value;
25349                 
25350             }
25351             
25352             // not sure if this supported any more..
25353             if ((typeof(v) == 'object') && f.getRawValue) {
25354                 v = f.getRawValue() ; // dates..
25355             }
25356             // combo boxes where name != hiddenName...
25357             if (f.name != f.getName()) {
25358                 ret[f.name] = f.getRawValue();
25359             }
25360             ret[f.getName()] = v;
25361         });
25362         
25363         return ret;
25364     },
25365
25366     /**
25367      * Clears all invalid messages in this form.
25368      * @return {BasicForm} this
25369      */
25370     clearInvalid : function(){
25371         this.items.each(function(f){
25372            f.clearInvalid();
25373         });
25374         
25375         Roo.each(this.childForms || [], function (f) {
25376             f.clearInvalid();
25377         });
25378         
25379         
25380         return this;
25381     },
25382
25383     /**
25384      * Resets this form.
25385      * @return {BasicForm} this
25386      */
25387     reset : function(){
25388         this.items.each(function(f){
25389             f.reset();
25390         });
25391         
25392         Roo.each(this.childForms || [], function (f) {
25393             f.reset();
25394         });
25395         this.resetHasChanged();
25396         
25397         return this;
25398     },
25399
25400     /**
25401      * Add Roo.form components to this form.
25402      * @param {Field} field1
25403      * @param {Field} field2 (optional)
25404      * @param {Field} etc (optional)
25405      * @return {BasicForm} this
25406      */
25407     add : function(){
25408         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25409         return this;
25410     },
25411
25412
25413     /**
25414      * Removes a field from the items collection (does NOT remove its markup).
25415      * @param {Field} field
25416      * @return {BasicForm} this
25417      */
25418     remove : function(field){
25419         this.items.remove(field);
25420         return this;
25421     },
25422
25423     /**
25424      * Looks at the fields in this form, checks them for an id attribute,
25425      * and calls applyTo on the existing dom element with that id.
25426      * @return {BasicForm} this
25427      */
25428     render : function(){
25429         this.items.each(function(f){
25430             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25431                 f.applyTo(f.id);
25432             }
25433         });
25434         return this;
25435     },
25436
25437     /**
25438      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25439      * @param {Object} values
25440      * @return {BasicForm} this
25441      */
25442     applyToFields : function(o){
25443         this.items.each(function(f){
25444            Roo.apply(f, o);
25445         });
25446         return this;
25447     },
25448
25449     /**
25450      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25451      * @param {Object} values
25452      * @return {BasicForm} this
25453      */
25454     applyIfToFields : function(o){
25455         this.items.each(function(f){
25456            Roo.applyIf(f, o);
25457         });
25458         return this;
25459     }
25460 });
25461
25462 // back compat
25463 Roo.BasicForm = Roo.form.BasicForm;
25464
25465 Roo.apply(Roo.form.BasicForm, {
25466     
25467     popover : {
25468         
25469         padding : 5,
25470         
25471         isApplied : false,
25472         
25473         isMasked : false,
25474         
25475         form : false,
25476         
25477         target : false,
25478         
25479         intervalID : false,
25480         
25481         maskEl : false,
25482         
25483         apply : function()
25484         {
25485             if(this.isApplied){
25486                 return;
25487             }
25488             
25489             this.maskEl = {
25490                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25491                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25492                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25493                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25494             };
25495             
25496             this.maskEl.top.enableDisplayMode("block");
25497             this.maskEl.left.enableDisplayMode("block");
25498             this.maskEl.bottom.enableDisplayMode("block");
25499             this.maskEl.right.enableDisplayMode("block");
25500             
25501             Roo.get(document.body).on('click', function(){
25502                 this.unmask();
25503             }, this);
25504             
25505             Roo.get(document.body).on('touchstart', function(){
25506                 this.unmask();
25507             }, this);
25508             
25509             this.isApplied = true
25510         },
25511         
25512         mask : function(form, target)
25513         {
25514             this.form = form;
25515             
25516             this.target = target;
25517             
25518             if(!this.form.errorMask || !target.el){
25519                 return;
25520             }
25521             
25522             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25523             
25524             var ot = this.target.el.calcOffsetsTo(scrollable);
25525             
25526             var scrollTo = ot[1] - this.form.maskOffset;
25527             
25528             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25529             
25530             scrollable.scrollTo('top', scrollTo);
25531             
25532             var el = this.target.wrap || this.target.el;
25533             
25534             var box = el.getBox();
25535             
25536             this.maskEl.top.setStyle('position', 'absolute');
25537             this.maskEl.top.setStyle('z-index', 10000);
25538             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25539             this.maskEl.top.setLeft(0);
25540             this.maskEl.top.setTop(0);
25541             this.maskEl.top.show();
25542             
25543             this.maskEl.left.setStyle('position', 'absolute');
25544             this.maskEl.left.setStyle('z-index', 10000);
25545             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25546             this.maskEl.left.setLeft(0);
25547             this.maskEl.left.setTop(box.y - this.padding);
25548             this.maskEl.left.show();
25549
25550             this.maskEl.bottom.setStyle('position', 'absolute');
25551             this.maskEl.bottom.setStyle('z-index', 10000);
25552             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25553             this.maskEl.bottom.setLeft(0);
25554             this.maskEl.bottom.setTop(box.bottom + this.padding);
25555             this.maskEl.bottom.show();
25556
25557             this.maskEl.right.setStyle('position', 'absolute');
25558             this.maskEl.right.setStyle('z-index', 10000);
25559             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25560             this.maskEl.right.setLeft(box.right + this.padding);
25561             this.maskEl.right.setTop(box.y - this.padding);
25562             this.maskEl.right.show();
25563
25564             this.intervalID = window.setInterval(function() {
25565                 Roo.form.BasicForm.popover.unmask();
25566             }, 10000);
25567
25568             window.onwheel = function(){ return false;};
25569             
25570             (function(){ this.isMasked = true; }).defer(500, this);
25571             
25572         },
25573         
25574         unmask : function()
25575         {
25576             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25577                 return;
25578             }
25579             
25580             this.maskEl.top.setStyle('position', 'absolute');
25581             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25582             this.maskEl.top.hide();
25583
25584             this.maskEl.left.setStyle('position', 'absolute');
25585             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25586             this.maskEl.left.hide();
25587
25588             this.maskEl.bottom.setStyle('position', 'absolute');
25589             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25590             this.maskEl.bottom.hide();
25591
25592             this.maskEl.right.setStyle('position', 'absolute');
25593             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25594             this.maskEl.right.hide();
25595             
25596             window.onwheel = function(){ return true;};
25597             
25598             if(this.intervalID){
25599                 window.clearInterval(this.intervalID);
25600                 this.intervalID = false;
25601             }
25602             
25603             this.isMasked = false;
25604             
25605         }
25606         
25607     }
25608     
25609 });/*
25610  * Based on:
25611  * Ext JS Library 1.1.1
25612  * Copyright(c) 2006-2007, Ext JS, LLC.
25613  *
25614  * Originally Released Under LGPL - original licence link has changed is not relivant.
25615  *
25616  * Fork - LGPL
25617  * <script type="text/javascript">
25618  */
25619
25620 /**
25621  * @class Roo.form.Form
25622  * @extends Roo.form.BasicForm
25623  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25624  * @constructor
25625  * @param {Object} config Configuration options
25626  */
25627 Roo.form.Form = function(config){
25628     var xitems =  [];
25629     if (config.items) {
25630         xitems = config.items;
25631         delete config.items;
25632     }
25633    
25634     
25635     Roo.form.Form.superclass.constructor.call(this, null, config);
25636     this.url = this.url || this.action;
25637     if(!this.root){
25638         this.root = new Roo.form.Layout(Roo.applyIf({
25639             id: Roo.id()
25640         }, config));
25641     }
25642     this.active = this.root;
25643     /**
25644      * Array of all the buttons that have been added to this form via {@link addButton}
25645      * @type Array
25646      */
25647     this.buttons = [];
25648     this.allItems = [];
25649     this.addEvents({
25650         /**
25651          * @event clientvalidation
25652          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25653          * @param {Form} this
25654          * @param {Boolean} valid true if the form has passed client-side validation
25655          */
25656         clientvalidation: true,
25657         /**
25658          * @event rendered
25659          * Fires when the form is rendered
25660          * @param {Roo.form.Form} form
25661          */
25662         rendered : true
25663     });
25664     
25665     if (this.progressUrl) {
25666             // push a hidden field onto the list of fields..
25667             this.addxtype( {
25668                     xns: Roo.form, 
25669                     xtype : 'Hidden', 
25670                     name : 'UPLOAD_IDENTIFIER' 
25671             });
25672         }
25673         
25674     
25675     Roo.each(xitems, this.addxtype, this);
25676     
25677 };
25678
25679 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25680     /**
25681      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25682      */
25683     /**
25684      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25685      */
25686     /**
25687      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25688      */
25689     buttonAlign:'center',
25690
25691     /**
25692      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25693      */
25694     minButtonWidth:75,
25695
25696     /**
25697      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25698      * This property cascades to child containers if not set.
25699      */
25700     labelAlign:'left',
25701
25702     /**
25703      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25704      * fires a looping event with that state. This is required to bind buttons to the valid
25705      * state using the config value formBind:true on the button.
25706      */
25707     monitorValid : false,
25708
25709     /**
25710      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25711      */
25712     monitorPoll : 200,
25713     
25714     /**
25715      * @cfg {String} progressUrl - Url to return progress data 
25716      */
25717     
25718     progressUrl : false,
25719     /**
25720      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25721      * sending a formdata with extra parameters - eg uploaded elements.
25722      */
25723     
25724     formData : false,
25725     
25726     /**
25727      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25728      * fields are added and the column is closed. If no fields are passed the column remains open
25729      * until end() is called.
25730      * @param {Object} config The config to pass to the column
25731      * @param {Field} field1 (optional)
25732      * @param {Field} field2 (optional)
25733      * @param {Field} etc (optional)
25734      * @return Column The column container object
25735      */
25736     column : function(c){
25737         var col = new Roo.form.Column(c);
25738         this.start(col);
25739         if(arguments.length > 1){ // duplicate code required because of Opera
25740             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25741             this.end();
25742         }
25743         return col;
25744     },
25745
25746     /**
25747      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25748      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25749      * until end() is called.
25750      * @param {Object} config The config to pass to the fieldset
25751      * @param {Field} field1 (optional)
25752      * @param {Field} field2 (optional)
25753      * @param {Field} etc (optional)
25754      * @return FieldSet The fieldset container object
25755      */
25756     fieldset : function(c){
25757         var fs = new Roo.form.FieldSet(c);
25758         this.start(fs);
25759         if(arguments.length > 1){ // duplicate code required because of Opera
25760             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25761             this.end();
25762         }
25763         return fs;
25764     },
25765
25766     /**
25767      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25768      * fields are added and the container is closed. If no fields are passed the container remains open
25769      * until end() is called.
25770      * @param {Object} config The config to pass to the Layout
25771      * @param {Field} field1 (optional)
25772      * @param {Field} field2 (optional)
25773      * @param {Field} etc (optional)
25774      * @return Layout The container object
25775      */
25776     container : function(c){
25777         var l = new Roo.form.Layout(c);
25778         this.start(l);
25779         if(arguments.length > 1){ // duplicate code required because of Opera
25780             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25781             this.end();
25782         }
25783         return l;
25784     },
25785
25786     /**
25787      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25788      * @param {Object} container A Roo.form.Layout or subclass of Layout
25789      * @return {Form} this
25790      */
25791     start : function(c){
25792         // cascade label info
25793         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25794         this.active.stack.push(c);
25795         c.ownerCt = this.active;
25796         this.active = c;
25797         return this;
25798     },
25799
25800     /**
25801      * Closes the current open container
25802      * @return {Form} this
25803      */
25804     end : function(){
25805         if(this.active == this.root){
25806             return this;
25807         }
25808         this.active = this.active.ownerCt;
25809         return this;
25810     },
25811
25812     /**
25813      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25814      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25815      * as the label of the field.
25816      * @param {Field} field1
25817      * @param {Field} field2 (optional)
25818      * @param {Field} etc. (optional)
25819      * @return {Form} this
25820      */
25821     add : function(){
25822         this.active.stack.push.apply(this.active.stack, arguments);
25823         this.allItems.push.apply(this.allItems,arguments);
25824         var r = [];
25825         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25826             if(a[i].isFormField){
25827                 r.push(a[i]);
25828             }
25829         }
25830         if(r.length > 0){
25831             Roo.form.Form.superclass.add.apply(this, r);
25832         }
25833         return this;
25834     },
25835     
25836
25837     
25838     
25839     
25840      /**
25841      * Find any element that has been added to a form, using it's ID or name
25842      * This can include framesets, columns etc. along with regular fields..
25843      * @param {String} id - id or name to find.
25844      
25845      * @return {Element} e - or false if nothing found.
25846      */
25847     findbyId : function(id)
25848     {
25849         var ret = false;
25850         if (!id) {
25851             return ret;
25852         }
25853         Roo.each(this.allItems, function(f){
25854             if (f.id == id || f.name == id ){
25855                 ret = f;
25856                 return false;
25857             }
25858         });
25859         return ret;
25860     },
25861
25862     
25863     
25864     /**
25865      * Render this form into the passed container. This should only be called once!
25866      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25867      * @return {Form} this
25868      */
25869     render : function(ct)
25870     {
25871         
25872         
25873         
25874         ct = Roo.get(ct);
25875         var o = this.autoCreate || {
25876             tag: 'form',
25877             method : this.method || 'POST',
25878             id : this.id || Roo.id()
25879         };
25880         this.initEl(ct.createChild(o));
25881
25882         this.root.render(this.el);
25883         
25884        
25885              
25886         this.items.each(function(f){
25887             f.render('x-form-el-'+f.id);
25888         });
25889
25890         if(this.buttons.length > 0){
25891             // tables are required to maintain order and for correct IE layout
25892             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25893                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25894                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25895             }}, null, true);
25896             var tr = tb.getElementsByTagName('tr')[0];
25897             for(var i = 0, len = this.buttons.length; i < len; i++) {
25898                 var b = this.buttons[i];
25899                 var td = document.createElement('td');
25900                 td.className = 'x-form-btn-td';
25901                 b.render(tr.appendChild(td));
25902             }
25903         }
25904         if(this.monitorValid){ // initialize after render
25905             this.startMonitoring();
25906         }
25907         this.fireEvent('rendered', this);
25908         return this;
25909     },
25910
25911     /**
25912      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25913      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25914      * object or a valid Roo.DomHelper element config
25915      * @param {Function} handler The function called when the button is clicked
25916      * @param {Object} scope (optional) The scope of the handler function
25917      * @return {Roo.Button}
25918      */
25919     addButton : function(config, handler, scope){
25920         var bc = {
25921             handler: handler,
25922             scope: scope,
25923             minWidth: this.minButtonWidth,
25924             hideParent:true
25925         };
25926         if(typeof config == "string"){
25927             bc.text = config;
25928         }else{
25929             Roo.apply(bc, config);
25930         }
25931         var btn = new Roo.Button(null, bc);
25932         this.buttons.push(btn);
25933         return btn;
25934     },
25935
25936      /**
25937      * Adds a series of form elements (using the xtype property as the factory method.
25938      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25939      * @param {Object} config 
25940      */
25941     
25942     addxtype : function()
25943     {
25944         var ar = Array.prototype.slice.call(arguments, 0);
25945         var ret = false;
25946         for(var i = 0; i < ar.length; i++) {
25947             if (!ar[i]) {
25948                 continue; // skip -- if this happends something invalid got sent, we 
25949                 // should ignore it, as basically that interface element will not show up
25950                 // and that should be pretty obvious!!
25951             }
25952             
25953             if (Roo.form[ar[i].xtype]) {
25954                 ar[i].form = this;
25955                 var fe = Roo.factory(ar[i], Roo.form);
25956                 if (!ret) {
25957                     ret = fe;
25958                 }
25959                 fe.form = this;
25960                 if (fe.store) {
25961                     fe.store.form = this;
25962                 }
25963                 if (fe.isLayout) {  
25964                          
25965                     this.start(fe);
25966                     this.allItems.push(fe);
25967                     if (fe.items && fe.addxtype) {
25968                         fe.addxtype.apply(fe, fe.items);
25969                         delete fe.items;
25970                     }
25971                      this.end();
25972                     continue;
25973                 }
25974                 
25975                 
25976                  
25977                 this.add(fe);
25978               //  console.log('adding ' + ar[i].xtype);
25979             }
25980             if (ar[i].xtype == 'Button') {  
25981                 //console.log('adding button');
25982                 //console.log(ar[i]);
25983                 this.addButton(ar[i]);
25984                 this.allItems.push(fe);
25985                 continue;
25986             }
25987             
25988             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25989                 alert('end is not supported on xtype any more, use items');
25990             //    this.end();
25991             //    //console.log('adding end');
25992             }
25993             
25994         }
25995         return ret;
25996     },
25997     
25998     /**
25999      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26000      * option "monitorValid"
26001      */
26002     startMonitoring : function(){
26003         if(!this.bound){
26004             this.bound = true;
26005             Roo.TaskMgr.start({
26006                 run : this.bindHandler,
26007                 interval : this.monitorPoll || 200,
26008                 scope: this
26009             });
26010         }
26011     },
26012
26013     /**
26014      * Stops monitoring of the valid state of this form
26015      */
26016     stopMonitoring : function(){
26017         this.bound = false;
26018     },
26019
26020     // private
26021     bindHandler : function(){
26022         if(!this.bound){
26023             return false; // stops binding
26024         }
26025         var valid = true;
26026         this.items.each(function(f){
26027             if(!f.isValid(true)){
26028                 valid = false;
26029                 return false;
26030             }
26031         });
26032         for(var i = 0, len = this.buttons.length; i < len; i++){
26033             var btn = this.buttons[i];
26034             if(btn.formBind === true && btn.disabled === valid){
26035                 btn.setDisabled(!valid);
26036             }
26037         }
26038         this.fireEvent('clientvalidation', this, valid);
26039     }
26040     
26041     
26042     
26043     
26044     
26045     
26046     
26047     
26048 });
26049
26050
26051 // back compat
26052 Roo.Form = Roo.form.Form;
26053 /*
26054  * Based on:
26055  * Ext JS Library 1.1.1
26056  * Copyright(c) 2006-2007, Ext JS, LLC.
26057  *
26058  * Originally Released Under LGPL - original licence link has changed is not relivant.
26059  *
26060  * Fork - LGPL
26061  * <script type="text/javascript">
26062  */
26063
26064 // as we use this in bootstrap.
26065 Roo.namespace('Roo.form');
26066  /**
26067  * @class Roo.form.Action
26068  * Internal Class used to handle form actions
26069  * @constructor
26070  * @param {Roo.form.BasicForm} el The form element or its id
26071  * @param {Object} config Configuration options
26072  */
26073
26074  
26075  
26076 // define the action interface
26077 Roo.form.Action = function(form, options){
26078     this.form = form;
26079     this.options = options || {};
26080 };
26081 /**
26082  * Client Validation Failed
26083  * @const 
26084  */
26085 Roo.form.Action.CLIENT_INVALID = 'client';
26086 /**
26087  * Server Validation Failed
26088  * @const 
26089  */
26090 Roo.form.Action.SERVER_INVALID = 'server';
26091  /**
26092  * Connect to Server Failed
26093  * @const 
26094  */
26095 Roo.form.Action.CONNECT_FAILURE = 'connect';
26096 /**
26097  * Reading Data from Server Failed
26098  * @const 
26099  */
26100 Roo.form.Action.LOAD_FAILURE = 'load';
26101
26102 Roo.form.Action.prototype = {
26103     type : 'default',
26104     failureType : undefined,
26105     response : undefined,
26106     result : undefined,
26107
26108     // interface method
26109     run : function(options){
26110
26111     },
26112
26113     // interface method
26114     success : function(response){
26115
26116     },
26117
26118     // interface method
26119     handleResponse : function(response){
26120
26121     },
26122
26123     // default connection failure
26124     failure : function(response){
26125         
26126         this.response = response;
26127         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26128         this.form.afterAction(this, false);
26129     },
26130
26131     processResponse : function(response){
26132         this.response = response;
26133         if(!response.responseText){
26134             return true;
26135         }
26136         this.result = this.handleResponse(response);
26137         return this.result;
26138     },
26139
26140     // utility functions used internally
26141     getUrl : function(appendParams){
26142         var url = this.options.url || this.form.url || this.form.el.dom.action;
26143         if(appendParams){
26144             var p = this.getParams();
26145             if(p){
26146                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26147             }
26148         }
26149         return url;
26150     },
26151
26152     getMethod : function(){
26153         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26154     },
26155
26156     getParams : function(){
26157         var bp = this.form.baseParams;
26158         var p = this.options.params;
26159         if(p){
26160             if(typeof p == "object"){
26161                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26162             }else if(typeof p == 'string' && bp){
26163                 p += '&' + Roo.urlEncode(bp);
26164             }
26165         }else if(bp){
26166             p = Roo.urlEncode(bp);
26167         }
26168         return p;
26169     },
26170
26171     createCallback : function(){
26172         return {
26173             success: this.success,
26174             failure: this.failure,
26175             scope: this,
26176             timeout: (this.form.timeout*1000),
26177             upload: this.form.fileUpload ? this.success : undefined
26178         };
26179     }
26180 };
26181
26182 Roo.form.Action.Submit = function(form, options){
26183     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26184 };
26185
26186 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26187     type : 'submit',
26188
26189     haveProgress : false,
26190     uploadComplete : false,
26191     
26192     // uploadProgress indicator.
26193     uploadProgress : function()
26194     {
26195         if (!this.form.progressUrl) {
26196             return;
26197         }
26198         
26199         if (!this.haveProgress) {
26200             Roo.MessageBox.progress("Uploading", "Uploading");
26201         }
26202         if (this.uploadComplete) {
26203            Roo.MessageBox.hide();
26204            return;
26205         }
26206         
26207         this.haveProgress = true;
26208    
26209         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26210         
26211         var c = new Roo.data.Connection();
26212         c.request({
26213             url : this.form.progressUrl,
26214             params: {
26215                 id : uid
26216             },
26217             method: 'GET',
26218             success : function(req){
26219                //console.log(data);
26220                 var rdata = false;
26221                 var edata;
26222                 try  {
26223                    rdata = Roo.decode(req.responseText)
26224                 } catch (e) {
26225                     Roo.log("Invalid data from server..");
26226                     Roo.log(edata);
26227                     return;
26228                 }
26229                 if (!rdata || !rdata.success) {
26230                     Roo.log(rdata);
26231                     Roo.MessageBox.alert(Roo.encode(rdata));
26232                     return;
26233                 }
26234                 var data = rdata.data;
26235                 
26236                 if (this.uploadComplete) {
26237                    Roo.MessageBox.hide();
26238                    return;
26239                 }
26240                    
26241                 if (data){
26242                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26243                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26244                     );
26245                 }
26246                 this.uploadProgress.defer(2000,this);
26247             },
26248        
26249             failure: function(data) {
26250                 Roo.log('progress url failed ');
26251                 Roo.log(data);
26252             },
26253             scope : this
26254         });
26255            
26256     },
26257     
26258     
26259     run : function()
26260     {
26261         // run get Values on the form, so it syncs any secondary forms.
26262         this.form.getValues();
26263         
26264         var o = this.options;
26265         var method = this.getMethod();
26266         var isPost = method == 'POST';
26267         if(o.clientValidation === false || this.form.isValid()){
26268             
26269             if (this.form.progressUrl) {
26270                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26271                     (new Date() * 1) + '' + Math.random());
26272                     
26273             } 
26274             
26275             
26276             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26277                 form:this.form.el.dom,
26278                 url:this.getUrl(!isPost),
26279                 method: method,
26280                 params:isPost ? this.getParams() : null,
26281                 isUpload: this.form.fileUpload,
26282                 formData : this.form.formData
26283             }));
26284             
26285             this.uploadProgress();
26286
26287         }else if (o.clientValidation !== false){ // client validation failed
26288             this.failureType = Roo.form.Action.CLIENT_INVALID;
26289             this.form.afterAction(this, false);
26290         }
26291     },
26292
26293     success : function(response)
26294     {
26295         this.uploadComplete= true;
26296         if (this.haveProgress) {
26297             Roo.MessageBox.hide();
26298         }
26299         
26300         
26301         var result = this.processResponse(response);
26302         if(result === true || result.success){
26303             this.form.afterAction(this, true);
26304             return;
26305         }
26306         if(result.errors){
26307             this.form.markInvalid(result.errors);
26308             this.failureType = Roo.form.Action.SERVER_INVALID;
26309         }
26310         this.form.afterAction(this, false);
26311     },
26312     failure : function(response)
26313     {
26314         this.uploadComplete= true;
26315         if (this.haveProgress) {
26316             Roo.MessageBox.hide();
26317         }
26318         
26319         this.response = response;
26320         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26321         this.form.afterAction(this, false);
26322     },
26323     
26324     handleResponse : function(response){
26325         if(this.form.errorReader){
26326             var rs = this.form.errorReader.read(response);
26327             var errors = [];
26328             if(rs.records){
26329                 for(var i = 0, len = rs.records.length; i < len; i++) {
26330                     var r = rs.records[i];
26331                     errors[i] = r.data;
26332                 }
26333             }
26334             if(errors.length < 1){
26335                 errors = null;
26336             }
26337             return {
26338                 success : rs.success,
26339                 errors : errors
26340             };
26341         }
26342         var ret = false;
26343         try {
26344             ret = Roo.decode(response.responseText);
26345         } catch (e) {
26346             ret = {
26347                 success: false,
26348                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26349                 errors : []
26350             };
26351         }
26352         return ret;
26353         
26354     }
26355 });
26356
26357
26358 Roo.form.Action.Load = function(form, options){
26359     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26360     this.reader = this.form.reader;
26361 };
26362
26363 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26364     type : 'load',
26365
26366     run : function(){
26367         
26368         Roo.Ajax.request(Roo.apply(
26369                 this.createCallback(), {
26370                     method:this.getMethod(),
26371                     url:this.getUrl(false),
26372                     params:this.getParams()
26373         }));
26374     },
26375
26376     success : function(response){
26377         
26378         var result = this.processResponse(response);
26379         if(result === true || !result.success || !result.data){
26380             this.failureType = Roo.form.Action.LOAD_FAILURE;
26381             this.form.afterAction(this, false);
26382             return;
26383         }
26384         this.form.clearInvalid();
26385         this.form.setValues(result.data);
26386         this.form.afterAction(this, true);
26387     },
26388
26389     handleResponse : function(response){
26390         if(this.form.reader){
26391             var rs = this.form.reader.read(response);
26392             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26393             return {
26394                 success : rs.success,
26395                 data : data
26396             };
26397         }
26398         return Roo.decode(response.responseText);
26399     }
26400 });
26401
26402 Roo.form.Action.ACTION_TYPES = {
26403     'load' : Roo.form.Action.Load,
26404     'submit' : Roo.form.Action.Submit
26405 };/*
26406  * Based on:
26407  * Ext JS Library 1.1.1
26408  * Copyright(c) 2006-2007, Ext JS, LLC.
26409  *
26410  * Originally Released Under LGPL - original licence link has changed is not relivant.
26411  *
26412  * Fork - LGPL
26413  * <script type="text/javascript">
26414  */
26415  
26416 /**
26417  * @class Roo.form.Layout
26418  * @extends Roo.Component
26419  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26420  * @constructor
26421  * @param {Object} config Configuration options
26422  */
26423 Roo.form.Layout = function(config){
26424     var xitems = [];
26425     if (config.items) {
26426         xitems = config.items;
26427         delete config.items;
26428     }
26429     Roo.form.Layout.superclass.constructor.call(this, config);
26430     this.stack = [];
26431     Roo.each(xitems, this.addxtype, this);
26432      
26433 };
26434
26435 Roo.extend(Roo.form.Layout, Roo.Component, {
26436     /**
26437      * @cfg {String/Object} autoCreate
26438      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26439      */
26440     /**
26441      * @cfg {String/Object/Function} style
26442      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26443      * a function which returns such a specification.
26444      */
26445     /**
26446      * @cfg {String} labelAlign
26447      * Valid values are "left," "top" and "right" (defaults to "left")
26448      */
26449     /**
26450      * @cfg {Number} labelWidth
26451      * Fixed width in pixels of all field labels (defaults to undefined)
26452      */
26453     /**
26454      * @cfg {Boolean} clear
26455      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26456      */
26457     clear : true,
26458     /**
26459      * @cfg {String} labelSeparator
26460      * The separator to use after field labels (defaults to ':')
26461      */
26462     labelSeparator : ':',
26463     /**
26464      * @cfg {Boolean} hideLabels
26465      * True to suppress the display of field labels in this layout (defaults to false)
26466      */
26467     hideLabels : false,
26468
26469     // private
26470     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26471     
26472     isLayout : true,
26473     
26474     // private
26475     onRender : function(ct, position){
26476         if(this.el){ // from markup
26477             this.el = Roo.get(this.el);
26478         }else {  // generate
26479             var cfg = this.getAutoCreate();
26480             this.el = ct.createChild(cfg, position);
26481         }
26482         if(this.style){
26483             this.el.applyStyles(this.style);
26484         }
26485         if(this.labelAlign){
26486             this.el.addClass('x-form-label-'+this.labelAlign);
26487         }
26488         if(this.hideLabels){
26489             this.labelStyle = "display:none";
26490             this.elementStyle = "padding-left:0;";
26491         }else{
26492             if(typeof this.labelWidth == 'number'){
26493                 this.labelStyle = "width:"+this.labelWidth+"px;";
26494                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26495             }
26496             if(this.labelAlign == 'top'){
26497                 this.labelStyle = "width:auto;";
26498                 this.elementStyle = "padding-left:0;";
26499             }
26500         }
26501         var stack = this.stack;
26502         var slen = stack.length;
26503         if(slen > 0){
26504             if(!this.fieldTpl){
26505                 var t = new Roo.Template(
26506                     '<div class="x-form-item {5}">',
26507                         '<label for="{0}" style="{2}">{1}{4}</label>',
26508                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26509                         '</div>',
26510                     '</div><div class="x-form-clear-left"></div>'
26511                 );
26512                 t.disableFormats = true;
26513                 t.compile();
26514                 Roo.form.Layout.prototype.fieldTpl = t;
26515             }
26516             for(var i = 0; i < slen; i++) {
26517                 if(stack[i].isFormField){
26518                     this.renderField(stack[i]);
26519                 }else{
26520                     this.renderComponent(stack[i]);
26521                 }
26522             }
26523         }
26524         if(this.clear){
26525             this.el.createChild({cls:'x-form-clear'});
26526         }
26527     },
26528
26529     // private
26530     renderField : function(f){
26531         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26532                f.id, //0
26533                f.fieldLabel, //1
26534                f.labelStyle||this.labelStyle||'', //2
26535                this.elementStyle||'', //3
26536                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26537                f.itemCls||this.itemCls||''  //5
26538        ], true).getPrevSibling());
26539     },
26540
26541     // private
26542     renderComponent : function(c){
26543         c.render(c.isLayout ? this.el : this.el.createChild());    
26544     },
26545     /**
26546      * Adds a object form elements (using the xtype property as the factory method.)
26547      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26548      * @param {Object} config 
26549      */
26550     addxtype : function(o)
26551     {
26552         // create the lement.
26553         o.form = this.form;
26554         var fe = Roo.factory(o, Roo.form);
26555         this.form.allItems.push(fe);
26556         this.stack.push(fe);
26557         
26558         if (fe.isFormField) {
26559             this.form.items.add(fe);
26560         }
26561          
26562         return fe;
26563     }
26564 });
26565
26566 /**
26567  * @class Roo.form.Column
26568  * @extends Roo.form.Layout
26569  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26570  * @constructor
26571  * @param {Object} config Configuration options
26572  */
26573 Roo.form.Column = function(config){
26574     Roo.form.Column.superclass.constructor.call(this, config);
26575 };
26576
26577 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26578     /**
26579      * @cfg {Number/String} width
26580      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26581      */
26582     /**
26583      * @cfg {String/Object} autoCreate
26584      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26585      */
26586
26587     // private
26588     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26589
26590     // private
26591     onRender : function(ct, position){
26592         Roo.form.Column.superclass.onRender.call(this, ct, position);
26593         if(this.width){
26594             this.el.setWidth(this.width);
26595         }
26596     }
26597 });
26598
26599
26600 /**
26601  * @class Roo.form.Row
26602  * @extends Roo.form.Layout
26603  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26604  * @constructor
26605  * @param {Object} config Configuration options
26606  */
26607
26608  
26609 Roo.form.Row = function(config){
26610     Roo.form.Row.superclass.constructor.call(this, config);
26611 };
26612  
26613 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26614       /**
26615      * @cfg {Number/String} width
26616      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26617      */
26618     /**
26619      * @cfg {Number/String} height
26620      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26621      */
26622     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26623     
26624     padWidth : 20,
26625     // private
26626     onRender : function(ct, position){
26627         //console.log('row render');
26628         if(!this.rowTpl){
26629             var t = new Roo.Template(
26630                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26631                     '<label for="{0}" style="{2}">{1}{4}</label>',
26632                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26633                     '</div>',
26634                 '</div>'
26635             );
26636             t.disableFormats = true;
26637             t.compile();
26638             Roo.form.Layout.prototype.rowTpl = t;
26639         }
26640         this.fieldTpl = this.rowTpl;
26641         
26642         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26643         var labelWidth = 100;
26644         
26645         if ((this.labelAlign != 'top')) {
26646             if (typeof this.labelWidth == 'number') {
26647                 labelWidth = this.labelWidth
26648             }
26649             this.padWidth =  20 + labelWidth;
26650             
26651         }
26652         
26653         Roo.form.Column.superclass.onRender.call(this, ct, position);
26654         if(this.width){
26655             this.el.setWidth(this.width);
26656         }
26657         if(this.height){
26658             this.el.setHeight(this.height);
26659         }
26660     },
26661     
26662     // private
26663     renderField : function(f){
26664         f.fieldEl = this.fieldTpl.append(this.el, [
26665                f.id, f.fieldLabel,
26666                f.labelStyle||this.labelStyle||'',
26667                this.elementStyle||'',
26668                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26669                f.itemCls||this.itemCls||'',
26670                f.width ? f.width + this.padWidth : 160 + this.padWidth
26671        ],true);
26672     }
26673 });
26674  
26675
26676 /**
26677  * @class Roo.form.FieldSet
26678  * @extends Roo.form.Layout
26679  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26680  * @constructor
26681  * @param {Object} config Configuration options
26682  */
26683 Roo.form.FieldSet = function(config){
26684     Roo.form.FieldSet.superclass.constructor.call(this, config);
26685 };
26686
26687 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26688     /**
26689      * @cfg {String} legend
26690      * The text to display as the legend for the FieldSet (defaults to '')
26691      */
26692     /**
26693      * @cfg {String/Object} autoCreate
26694      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26695      */
26696
26697     // private
26698     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26699
26700     // private
26701     onRender : function(ct, position){
26702         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26703         if(this.legend){
26704             this.setLegend(this.legend);
26705         }
26706     },
26707
26708     // private
26709     setLegend : function(text){
26710         if(this.rendered){
26711             this.el.child('legend').update(text);
26712         }
26713     }
26714 });/*
26715  * Based on:
26716  * Ext JS Library 1.1.1
26717  * Copyright(c) 2006-2007, Ext JS, LLC.
26718  *
26719  * Originally Released Under LGPL - original licence link has changed is not relivant.
26720  *
26721  * Fork - LGPL
26722  * <script type="text/javascript">
26723  */
26724 /**
26725  * @class Roo.form.VTypes
26726  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26727  * @singleton
26728  */
26729 Roo.form.VTypes = function(){
26730     // closure these in so they are only created once.
26731     var alpha = /^[a-zA-Z_]+$/;
26732     var alphanum = /^[a-zA-Z0-9_]+$/;
26733     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26734     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26735
26736     // All these messages and functions are configurable
26737     return {
26738         /**
26739          * The function used to validate email addresses
26740          * @param {String} value The email address
26741          */
26742         'email' : function(v){
26743             return email.test(v);
26744         },
26745         /**
26746          * The error text to display when the email validation function returns false
26747          * @type String
26748          */
26749         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26750         /**
26751          * The keystroke filter mask to be applied on email input
26752          * @type RegExp
26753          */
26754         'emailMask' : /[a-z0-9_\.\-@]/i,
26755
26756         /**
26757          * The function used to validate URLs
26758          * @param {String} value The URL
26759          */
26760         'url' : function(v){
26761             return url.test(v);
26762         },
26763         /**
26764          * The error text to display when the url validation function returns false
26765          * @type String
26766          */
26767         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26768         
26769         /**
26770          * The function used to validate alpha values
26771          * @param {String} value The value
26772          */
26773         'alpha' : function(v){
26774             return alpha.test(v);
26775         },
26776         /**
26777          * The error text to display when the alpha validation function returns false
26778          * @type String
26779          */
26780         'alphaText' : 'This field should only contain letters and _',
26781         /**
26782          * The keystroke filter mask to be applied on alpha input
26783          * @type RegExp
26784          */
26785         'alphaMask' : /[a-z_]/i,
26786
26787         /**
26788          * The function used to validate alphanumeric values
26789          * @param {String} value The value
26790          */
26791         'alphanum' : function(v){
26792             return alphanum.test(v);
26793         },
26794         /**
26795          * The error text to display when the alphanumeric validation function returns false
26796          * @type String
26797          */
26798         'alphanumText' : 'This field should only contain letters, numbers and _',
26799         /**
26800          * The keystroke filter mask to be applied on alphanumeric input
26801          * @type RegExp
26802          */
26803         'alphanumMask' : /[a-z0-9_]/i
26804     };
26805 }();//<script type="text/javascript">
26806
26807 /**
26808  * @class Roo.form.FCKeditor
26809  * @extends Roo.form.TextArea
26810  * Wrapper around the FCKEditor http://www.fckeditor.net
26811  * @constructor
26812  * Creates a new FCKeditor
26813  * @param {Object} config Configuration options
26814  */
26815 Roo.form.FCKeditor = function(config){
26816     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26817     this.addEvents({
26818          /**
26819          * @event editorinit
26820          * Fired when the editor is initialized - you can add extra handlers here..
26821          * @param {FCKeditor} this
26822          * @param {Object} the FCK object.
26823          */
26824         editorinit : true
26825     });
26826     
26827     
26828 };
26829 Roo.form.FCKeditor.editors = { };
26830 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26831 {
26832     //defaultAutoCreate : {
26833     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26834     //},
26835     // private
26836     /**
26837      * @cfg {Object} fck options - see fck manual for details.
26838      */
26839     fckconfig : false,
26840     
26841     /**
26842      * @cfg {Object} fck toolbar set (Basic or Default)
26843      */
26844     toolbarSet : 'Basic',
26845     /**
26846      * @cfg {Object} fck BasePath
26847      */ 
26848     basePath : '/fckeditor/',
26849     
26850     
26851     frame : false,
26852     
26853     value : '',
26854     
26855    
26856     onRender : function(ct, position)
26857     {
26858         if(!this.el){
26859             this.defaultAutoCreate = {
26860                 tag: "textarea",
26861                 style:"width:300px;height:60px;",
26862                 autocomplete: "new-password"
26863             };
26864         }
26865         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26866         /*
26867         if(this.grow){
26868             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26869             if(this.preventScrollbars){
26870                 this.el.setStyle("overflow", "hidden");
26871             }
26872             this.el.setHeight(this.growMin);
26873         }
26874         */
26875         //console.log('onrender' + this.getId() );
26876         Roo.form.FCKeditor.editors[this.getId()] = this;
26877          
26878
26879         this.replaceTextarea() ;
26880         
26881     },
26882     
26883     getEditor : function() {
26884         return this.fckEditor;
26885     },
26886     /**
26887      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26888      * @param {Mixed} value The value to set
26889      */
26890     
26891     
26892     setValue : function(value)
26893     {
26894         //console.log('setValue: ' + value);
26895         
26896         if(typeof(value) == 'undefined') { // not sure why this is happending...
26897             return;
26898         }
26899         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26900         
26901         //if(!this.el || !this.getEditor()) {
26902         //    this.value = value;
26903             //this.setValue.defer(100,this,[value]);    
26904         //    return;
26905         //} 
26906         
26907         if(!this.getEditor()) {
26908             return;
26909         }
26910         
26911         this.getEditor().SetData(value);
26912         
26913         //
26914
26915     },
26916
26917     /**
26918      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26919      * @return {Mixed} value The field value
26920      */
26921     getValue : function()
26922     {
26923         
26924         if (this.frame && this.frame.dom.style.display == 'none') {
26925             return Roo.form.FCKeditor.superclass.getValue.call(this);
26926         }
26927         
26928         if(!this.el || !this.getEditor()) {
26929            
26930            // this.getValue.defer(100,this); 
26931             return this.value;
26932         }
26933        
26934         
26935         var value=this.getEditor().GetData();
26936         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26937         return Roo.form.FCKeditor.superclass.getValue.call(this);
26938         
26939
26940     },
26941
26942     /**
26943      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26944      * @return {Mixed} value The field value
26945      */
26946     getRawValue : function()
26947     {
26948         if (this.frame && this.frame.dom.style.display == 'none') {
26949             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26950         }
26951         
26952         if(!this.el || !this.getEditor()) {
26953             //this.getRawValue.defer(100,this); 
26954             return this.value;
26955             return;
26956         }
26957         
26958         
26959         
26960         var value=this.getEditor().GetData();
26961         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26962         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26963          
26964     },
26965     
26966     setSize : function(w,h) {
26967         
26968         
26969         
26970         //if (this.frame && this.frame.dom.style.display == 'none') {
26971         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26972         //    return;
26973         //}
26974         //if(!this.el || !this.getEditor()) {
26975         //    this.setSize.defer(100,this, [w,h]); 
26976         //    return;
26977         //}
26978         
26979         
26980         
26981         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26982         
26983         this.frame.dom.setAttribute('width', w);
26984         this.frame.dom.setAttribute('height', h);
26985         this.frame.setSize(w,h);
26986         
26987     },
26988     
26989     toggleSourceEdit : function(value) {
26990         
26991       
26992          
26993         this.el.dom.style.display = value ? '' : 'none';
26994         this.frame.dom.style.display = value ?  'none' : '';
26995         
26996     },
26997     
26998     
26999     focus: function(tag)
27000     {
27001         if (this.frame.dom.style.display == 'none') {
27002             return Roo.form.FCKeditor.superclass.focus.call(this);
27003         }
27004         if(!this.el || !this.getEditor()) {
27005             this.focus.defer(100,this, [tag]); 
27006             return;
27007         }
27008         
27009         
27010         
27011         
27012         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27013         this.getEditor().Focus();
27014         if (tgs.length) {
27015             if (!this.getEditor().Selection.GetSelection()) {
27016                 this.focus.defer(100,this, [tag]); 
27017                 return;
27018             }
27019             
27020             
27021             var r = this.getEditor().EditorDocument.createRange();
27022             r.setStart(tgs[0],0);
27023             r.setEnd(tgs[0],0);
27024             this.getEditor().Selection.GetSelection().removeAllRanges();
27025             this.getEditor().Selection.GetSelection().addRange(r);
27026             this.getEditor().Focus();
27027         }
27028         
27029     },
27030     
27031     
27032     
27033     replaceTextarea : function()
27034     {
27035         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27036             return ;
27037         }
27038         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27039         //{
27040             // We must check the elements firstly using the Id and then the name.
27041         var oTextarea = document.getElementById( this.getId() );
27042         
27043         var colElementsByName = document.getElementsByName( this.getId() ) ;
27044          
27045         oTextarea.style.display = 'none' ;
27046
27047         if ( oTextarea.tabIndex ) {            
27048             this.TabIndex = oTextarea.tabIndex ;
27049         }
27050         
27051         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27052         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27053         this.frame = Roo.get(this.getId() + '___Frame')
27054     },
27055     
27056     _getConfigHtml : function()
27057     {
27058         var sConfig = '' ;
27059
27060         for ( var o in this.fckconfig ) {
27061             sConfig += sConfig.length > 0  ? '&amp;' : '';
27062             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27063         }
27064
27065         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27066     },
27067     
27068     
27069     _getIFrameHtml : function()
27070     {
27071         var sFile = 'fckeditor.html' ;
27072         /* no idea what this is about..
27073         try
27074         {
27075             if ( (/fcksource=true/i).test( window.top.location.search ) )
27076                 sFile = 'fckeditor.original.html' ;
27077         }
27078         catch (e) { 
27079         */
27080
27081         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27082         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27083         
27084         
27085         var html = '<iframe id="' + this.getId() +
27086             '___Frame" src="' + sLink +
27087             '" width="' + this.width +
27088             '" height="' + this.height + '"' +
27089             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27090             ' frameborder="0" scrolling="no"></iframe>' ;
27091
27092         return html ;
27093     },
27094     
27095     _insertHtmlBefore : function( html, element )
27096     {
27097         if ( element.insertAdjacentHTML )       {
27098             // IE
27099             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27100         } else { // Gecko
27101             var oRange = document.createRange() ;
27102             oRange.setStartBefore( element ) ;
27103             var oFragment = oRange.createContextualFragment( html );
27104             element.parentNode.insertBefore( oFragment, element ) ;
27105         }
27106     }
27107     
27108     
27109   
27110     
27111     
27112     
27113     
27114
27115 });
27116
27117 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27118
27119 function FCKeditor_OnComplete(editorInstance){
27120     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27121     f.fckEditor = editorInstance;
27122     //console.log("loaded");
27123     f.fireEvent('editorinit', f, editorInstance);
27124
27125   
27126
27127  
27128
27129
27130
27131
27132
27133
27134
27135
27136
27137
27138
27139
27140
27141
27142
27143 //<script type="text/javascript">
27144 /**
27145  * @class Roo.form.GridField
27146  * @extends Roo.form.Field
27147  * Embed a grid (or editable grid into a form)
27148  * STATUS ALPHA
27149  * 
27150  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27151  * it needs 
27152  * xgrid.store = Roo.data.Store
27153  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27154  * xgrid.store.reader = Roo.data.JsonReader 
27155  * 
27156  * 
27157  * @constructor
27158  * Creates a new GridField
27159  * @param {Object} config Configuration options
27160  */
27161 Roo.form.GridField = function(config){
27162     Roo.form.GridField.superclass.constructor.call(this, config);
27163      
27164 };
27165
27166 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27167     /**
27168      * @cfg {Number} width  - used to restrict width of grid..
27169      */
27170     width : 100,
27171     /**
27172      * @cfg {Number} height - used to restrict height of grid..
27173      */
27174     height : 50,
27175      /**
27176      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27177          * 
27178          *}
27179      */
27180     xgrid : false, 
27181     /**
27182      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27183      * {tag: "input", type: "checkbox", autocomplete: "off"})
27184      */
27185    // defaultAutoCreate : { tag: 'div' },
27186     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27187     /**
27188      * @cfg {String} addTitle Text to include for adding a title.
27189      */
27190     addTitle : false,
27191     //
27192     onResize : function(){
27193         Roo.form.Field.superclass.onResize.apply(this, arguments);
27194     },
27195
27196     initEvents : function(){
27197         // Roo.form.Checkbox.superclass.initEvents.call(this);
27198         // has no events...
27199        
27200     },
27201
27202
27203     getResizeEl : function(){
27204         return this.wrap;
27205     },
27206
27207     getPositionEl : function(){
27208         return this.wrap;
27209     },
27210
27211     // private
27212     onRender : function(ct, position){
27213         
27214         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27215         var style = this.style;
27216         delete this.style;
27217         
27218         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27219         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27220         this.viewEl = this.wrap.createChild({ tag: 'div' });
27221         if (style) {
27222             this.viewEl.applyStyles(style);
27223         }
27224         if (this.width) {
27225             this.viewEl.setWidth(this.width);
27226         }
27227         if (this.height) {
27228             this.viewEl.setHeight(this.height);
27229         }
27230         //if(this.inputValue !== undefined){
27231         //this.setValue(this.value);
27232         
27233         
27234         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27235         
27236         
27237         this.grid.render();
27238         this.grid.getDataSource().on('remove', this.refreshValue, this);
27239         this.grid.getDataSource().on('update', this.refreshValue, this);
27240         this.grid.on('afteredit', this.refreshValue, this);
27241  
27242     },
27243      
27244     
27245     /**
27246      * Sets the value of the item. 
27247      * @param {String} either an object  or a string..
27248      */
27249     setValue : function(v){
27250         //this.value = v;
27251         v = v || []; // empty set..
27252         // this does not seem smart - it really only affects memoryproxy grids..
27253         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27254             var ds = this.grid.getDataSource();
27255             // assumes a json reader..
27256             var data = {}
27257             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27258             ds.loadData( data);
27259         }
27260         // clear selection so it does not get stale.
27261         if (this.grid.sm) { 
27262             this.grid.sm.clearSelections();
27263         }
27264         
27265         Roo.form.GridField.superclass.setValue.call(this, v);
27266         this.refreshValue();
27267         // should load data in the grid really....
27268     },
27269     
27270     // private
27271     refreshValue: function() {
27272          var val = [];
27273         this.grid.getDataSource().each(function(r) {
27274             val.push(r.data);
27275         });
27276         this.el.dom.value = Roo.encode(val);
27277     }
27278     
27279      
27280     
27281     
27282 });/*
27283  * Based on:
27284  * Ext JS Library 1.1.1
27285  * Copyright(c) 2006-2007, Ext JS, LLC.
27286  *
27287  * Originally Released Under LGPL - original licence link has changed is not relivant.
27288  *
27289  * Fork - LGPL
27290  * <script type="text/javascript">
27291  */
27292 /**
27293  * @class Roo.form.DisplayField
27294  * @extends Roo.form.Field
27295  * A generic Field to display non-editable data.
27296  * @cfg {Boolean} closable (true|false) default false
27297  * @constructor
27298  * Creates a new Display Field item.
27299  * @param {Object} config Configuration options
27300  */
27301 Roo.form.DisplayField = function(config){
27302     Roo.form.DisplayField.superclass.constructor.call(this, config);
27303     
27304     this.addEvents({
27305         /**
27306          * @event close
27307          * Fires after the click the close btn
27308              * @param {Roo.form.DisplayField} this
27309              */
27310         close : true
27311     });
27312 };
27313
27314 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27315     inputType:      'hidden',
27316     allowBlank:     true,
27317     readOnly:         true,
27318     
27319  
27320     /**
27321      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27322      */
27323     focusClass : undefined,
27324     /**
27325      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27326      */
27327     fieldClass: 'x-form-field',
27328     
27329      /**
27330      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27331      */
27332     valueRenderer: undefined,
27333     
27334     width: 100,
27335     /**
27336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27337      * {tag: "input", type: "checkbox", autocomplete: "off"})
27338      */
27339      
27340  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27341  
27342     closable : false,
27343     
27344     onResize : function(){
27345         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27346         
27347     },
27348
27349     initEvents : function(){
27350         // Roo.form.Checkbox.superclass.initEvents.call(this);
27351         // has no events...
27352         
27353         if(this.closable){
27354             this.closeEl.on('click', this.onClose, this);
27355         }
27356        
27357     },
27358
27359
27360     getResizeEl : function(){
27361         return this.wrap;
27362     },
27363
27364     getPositionEl : function(){
27365         return this.wrap;
27366     },
27367
27368     // private
27369     onRender : function(ct, position){
27370         
27371         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27372         //if(this.inputValue !== undefined){
27373         this.wrap = this.el.wrap();
27374         
27375         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27376         
27377         if(this.closable){
27378             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27379         }
27380         
27381         if (this.bodyStyle) {
27382             this.viewEl.applyStyles(this.bodyStyle);
27383         }
27384         //this.viewEl.setStyle('padding', '2px');
27385         
27386         this.setValue(this.value);
27387         
27388     },
27389 /*
27390     // private
27391     initValue : Roo.emptyFn,
27392
27393   */
27394
27395         // private
27396     onClick : function(){
27397         
27398     },
27399
27400     /**
27401      * Sets the checked state of the checkbox.
27402      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27403      */
27404     setValue : function(v){
27405         this.value = v;
27406         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27407         // this might be called before we have a dom element..
27408         if (!this.viewEl) {
27409             return;
27410         }
27411         this.viewEl.dom.innerHTML = html;
27412         Roo.form.DisplayField.superclass.setValue.call(this, v);
27413
27414     },
27415     
27416     onClose : function(e)
27417     {
27418         e.preventDefault();
27419         
27420         this.fireEvent('close', this);
27421     }
27422 });/*
27423  * 
27424  * Licence- LGPL
27425  * 
27426  */
27427
27428 /**
27429  * @class Roo.form.DayPicker
27430  * @extends Roo.form.Field
27431  * A Day picker show [M] [T] [W] ....
27432  * @constructor
27433  * Creates a new Day Picker
27434  * @param {Object} config Configuration options
27435  */
27436 Roo.form.DayPicker= function(config){
27437     Roo.form.DayPicker.superclass.constructor.call(this, config);
27438      
27439 };
27440
27441 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27442     /**
27443      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27444      */
27445     focusClass : undefined,
27446     /**
27447      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27448      */
27449     fieldClass: "x-form-field",
27450    
27451     /**
27452      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27453      * {tag: "input", type: "checkbox", autocomplete: "off"})
27454      */
27455     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27456     
27457    
27458     actionMode : 'viewEl', 
27459     //
27460     // private
27461  
27462     inputType : 'hidden',
27463     
27464      
27465     inputElement: false, // real input element?
27466     basedOn: false, // ????
27467     
27468     isFormField: true, // not sure where this is needed!!!!
27469
27470     onResize : function(){
27471         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27472         if(!this.boxLabel){
27473             this.el.alignTo(this.wrap, 'c-c');
27474         }
27475     },
27476
27477     initEvents : function(){
27478         Roo.form.Checkbox.superclass.initEvents.call(this);
27479         this.el.on("click", this.onClick,  this);
27480         this.el.on("change", this.onClick,  this);
27481     },
27482
27483
27484     getResizeEl : function(){
27485         return this.wrap;
27486     },
27487
27488     getPositionEl : function(){
27489         return this.wrap;
27490     },
27491
27492     
27493     // private
27494     onRender : function(ct, position){
27495         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27496        
27497         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27498         
27499         var r1 = '<table><tr>';
27500         var r2 = '<tr class="x-form-daypick-icons">';
27501         for (var i=0; i < 7; i++) {
27502             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27503             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27504         }
27505         
27506         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27507         viewEl.select('img').on('click', this.onClick, this);
27508         this.viewEl = viewEl;   
27509         
27510         
27511         // this will not work on Chrome!!!
27512         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27513         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27514         
27515         
27516           
27517
27518     },
27519
27520     // private
27521     initValue : Roo.emptyFn,
27522
27523     /**
27524      * Returns the checked state of the checkbox.
27525      * @return {Boolean} True if checked, else false
27526      */
27527     getValue : function(){
27528         return this.el.dom.value;
27529         
27530     },
27531
27532         // private
27533     onClick : function(e){ 
27534         //this.setChecked(!this.checked);
27535         Roo.get(e.target).toggleClass('x-menu-item-checked');
27536         this.refreshValue();
27537         //if(this.el.dom.checked != this.checked){
27538         //    this.setValue(this.el.dom.checked);
27539        // }
27540     },
27541     
27542     // private
27543     refreshValue : function()
27544     {
27545         var val = '';
27546         this.viewEl.select('img',true).each(function(e,i,n)  {
27547             val += e.is(".x-menu-item-checked") ? String(n) : '';
27548         });
27549         this.setValue(val, true);
27550     },
27551
27552     /**
27553      * Sets the checked state of the checkbox.
27554      * On is always based on a string comparison between inputValue and the param.
27555      * @param {Boolean/String} value - the value to set 
27556      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27557      */
27558     setValue : function(v,suppressEvent){
27559         if (!this.el.dom) {
27560             return;
27561         }
27562         var old = this.el.dom.value ;
27563         this.el.dom.value = v;
27564         if (suppressEvent) {
27565             return ;
27566         }
27567          
27568         // update display..
27569         this.viewEl.select('img',true).each(function(e,i,n)  {
27570             
27571             var on = e.is(".x-menu-item-checked");
27572             var newv = v.indexOf(String(n)) > -1;
27573             if (on != newv) {
27574                 e.toggleClass('x-menu-item-checked');
27575             }
27576             
27577         });
27578         
27579         
27580         this.fireEvent('change', this, v, old);
27581         
27582         
27583     },
27584    
27585     // handle setting of hidden value by some other method!!?!?
27586     setFromHidden: function()
27587     {
27588         if(!this.el){
27589             return;
27590         }
27591         //console.log("SET FROM HIDDEN");
27592         //alert('setFrom hidden');
27593         this.setValue(this.el.dom.value);
27594     },
27595     
27596     onDestroy : function()
27597     {
27598         if(this.viewEl){
27599             Roo.get(this.viewEl).remove();
27600         }
27601          
27602         Roo.form.DayPicker.superclass.onDestroy.call(this);
27603     }
27604
27605 });/*
27606  * RooJS Library 1.1.1
27607  * Copyright(c) 2008-2011  Alan Knowles
27608  *
27609  * License - LGPL
27610  */
27611  
27612
27613 /**
27614  * @class Roo.form.ComboCheck
27615  * @extends Roo.form.ComboBox
27616  * A combobox for multiple select items.
27617  *
27618  * FIXME - could do with a reset button..
27619  * 
27620  * @constructor
27621  * Create a new ComboCheck
27622  * @param {Object} config Configuration options
27623  */
27624 Roo.form.ComboCheck = function(config){
27625     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27626     // should verify some data...
27627     // like
27628     // hiddenName = required..
27629     // displayField = required
27630     // valudField == required
27631     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27632     var _t = this;
27633     Roo.each(req, function(e) {
27634         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27635             throw "Roo.form.ComboCheck : missing value for: " + e;
27636         }
27637     });
27638     
27639     
27640 };
27641
27642 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27643      
27644      
27645     editable : false,
27646      
27647     selectedClass: 'x-menu-item-checked', 
27648     
27649     // private
27650     onRender : function(ct, position){
27651         var _t = this;
27652         
27653         
27654         
27655         if(!this.tpl){
27656             var cls = 'x-combo-list';
27657
27658             
27659             this.tpl =  new Roo.Template({
27660                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27661                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27662                    '<span>{' + this.displayField + '}</span>' +
27663                     '</div>' 
27664                 
27665             });
27666         }
27667  
27668         
27669         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27670         this.view.singleSelect = false;
27671         this.view.multiSelect = true;
27672         this.view.toggleSelect = true;
27673         this.pageTb.add(new Roo.Toolbar.Fill(), {
27674             
27675             text: 'Done',
27676             handler: function()
27677             {
27678                 _t.collapse();
27679             }
27680         });
27681     },
27682     
27683     onViewOver : function(e, t){
27684         // do nothing...
27685         return;
27686         
27687     },
27688     
27689     onViewClick : function(doFocus,index){
27690         return;
27691         
27692     },
27693     select: function () {
27694         //Roo.log("SELECT CALLED");
27695     },
27696      
27697     selectByValue : function(xv, scrollIntoView){
27698         var ar = this.getValueArray();
27699         var sels = [];
27700         
27701         Roo.each(ar, function(v) {
27702             if(v === undefined || v === null){
27703                 return;
27704             }
27705             var r = this.findRecord(this.valueField, v);
27706             if(r){
27707                 sels.push(this.store.indexOf(r))
27708                 
27709             }
27710         },this);
27711         this.view.select(sels);
27712         return false;
27713     },
27714     
27715     
27716     
27717     onSelect : function(record, index){
27718        // Roo.log("onselect Called");
27719        // this is only called by the clear button now..
27720         this.view.clearSelections();
27721         this.setValue('[]');
27722         if (this.value != this.valueBefore) {
27723             this.fireEvent('change', this, this.value, this.valueBefore);
27724             this.valueBefore = this.value;
27725         }
27726     },
27727     getValueArray : function()
27728     {
27729         var ar = [] ;
27730         
27731         try {
27732             //Roo.log(this.value);
27733             if (typeof(this.value) == 'undefined') {
27734                 return [];
27735             }
27736             var ar = Roo.decode(this.value);
27737             return  ar instanceof Array ? ar : []; //?? valid?
27738             
27739         } catch(e) {
27740             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27741             return [];
27742         }
27743          
27744     },
27745     expand : function ()
27746     {
27747         
27748         Roo.form.ComboCheck.superclass.expand.call(this);
27749         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27750         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27751         
27752
27753     },
27754     
27755     collapse : function(){
27756         Roo.form.ComboCheck.superclass.collapse.call(this);
27757         var sl = this.view.getSelectedIndexes();
27758         var st = this.store;
27759         var nv = [];
27760         var tv = [];
27761         var r;
27762         Roo.each(sl, function(i) {
27763             r = st.getAt(i);
27764             nv.push(r.get(this.valueField));
27765         },this);
27766         this.setValue(Roo.encode(nv));
27767         if (this.value != this.valueBefore) {
27768
27769             this.fireEvent('change', this, this.value, this.valueBefore);
27770             this.valueBefore = this.value;
27771         }
27772         
27773     },
27774     
27775     setValue : function(v){
27776         // Roo.log(v);
27777         this.value = v;
27778         
27779         var vals = this.getValueArray();
27780         var tv = [];
27781         Roo.each(vals, function(k) {
27782             var r = this.findRecord(this.valueField, k);
27783             if(r){
27784                 tv.push(r.data[this.displayField]);
27785             }else if(this.valueNotFoundText !== undefined){
27786                 tv.push( this.valueNotFoundText );
27787             }
27788         },this);
27789        // Roo.log(tv);
27790         
27791         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27792         this.hiddenField.value = v;
27793         this.value = v;
27794     }
27795     
27796 });/*
27797  * Based on:
27798  * Ext JS Library 1.1.1
27799  * Copyright(c) 2006-2007, Ext JS, LLC.
27800  *
27801  * Originally Released Under LGPL - original licence link has changed is not relivant.
27802  *
27803  * Fork - LGPL
27804  * <script type="text/javascript">
27805  */
27806  
27807 /**
27808  * @class Roo.form.Signature
27809  * @extends Roo.form.Field
27810  * Signature field.  
27811  * @constructor
27812  * 
27813  * @param {Object} config Configuration options
27814  */
27815
27816 Roo.form.Signature = function(config){
27817     Roo.form.Signature.superclass.constructor.call(this, config);
27818     
27819     this.addEvents({// not in used??
27820          /**
27821          * @event confirm
27822          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27823              * @param {Roo.form.Signature} combo This combo box
27824              */
27825         'confirm' : true,
27826         /**
27827          * @event reset
27828          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27829              * @param {Roo.form.ComboBox} combo This combo box
27830              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27831              */
27832         'reset' : true
27833     });
27834 };
27835
27836 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27837     /**
27838      * @cfg {Object} labels Label to use when rendering a form.
27839      * defaults to 
27840      * labels : { 
27841      *      clear : "Clear",
27842      *      confirm : "Confirm"
27843      *  }
27844      */
27845     labels : { 
27846         clear : "Clear",
27847         confirm : "Confirm"
27848     },
27849     /**
27850      * @cfg {Number} width The signature panel width (defaults to 300)
27851      */
27852     width: 300,
27853     /**
27854      * @cfg {Number} height The signature panel height (defaults to 100)
27855      */
27856     height : 100,
27857     /**
27858      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27859      */
27860     allowBlank : false,
27861     
27862     //private
27863     // {Object} signPanel The signature SVG panel element (defaults to {})
27864     signPanel : {},
27865     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27866     isMouseDown : false,
27867     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27868     isConfirmed : false,
27869     // {String} signatureTmp SVG mapping string (defaults to empty string)
27870     signatureTmp : '',
27871     
27872     
27873     defaultAutoCreate : { // modified by initCompnoent..
27874         tag: "input",
27875         type:"hidden"
27876     },
27877
27878     // private
27879     onRender : function(ct, position){
27880         
27881         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27882         
27883         this.wrap = this.el.wrap({
27884             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27885         });
27886         
27887         this.createToolbar(this);
27888         this.signPanel = this.wrap.createChild({
27889                 tag: 'div',
27890                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27891             }, this.el
27892         );
27893             
27894         this.svgID = Roo.id();
27895         this.svgEl = this.signPanel.createChild({
27896               xmlns : 'http://www.w3.org/2000/svg',
27897               tag : 'svg',
27898               id : this.svgID + "-svg",
27899               width: this.width,
27900               height: this.height,
27901               viewBox: '0 0 '+this.width+' '+this.height,
27902               cn : [
27903                 {
27904                     tag: "rect",
27905                     id: this.svgID + "-svg-r",
27906                     width: this.width,
27907                     height: this.height,
27908                     fill: "#ffa"
27909                 },
27910                 {
27911                     tag: "line",
27912                     id: this.svgID + "-svg-l",
27913                     x1: "0", // start
27914                     y1: (this.height*0.8), // start set the line in 80% of height
27915                     x2: this.width, // end
27916                     y2: (this.height*0.8), // end set the line in 80% of height
27917                     'stroke': "#666",
27918                     'stroke-width': "1",
27919                     'stroke-dasharray': "3",
27920                     'shape-rendering': "crispEdges",
27921                     'pointer-events': "none"
27922                 },
27923                 {
27924                     tag: "path",
27925                     id: this.svgID + "-svg-p",
27926                     'stroke': "navy",
27927                     'stroke-width': "3",
27928                     'fill': "none",
27929                     'pointer-events': 'none'
27930                 }
27931               ]
27932         });
27933         this.createSVG();
27934         this.svgBox = this.svgEl.dom.getScreenCTM();
27935     },
27936     createSVG : function(){ 
27937         var svg = this.signPanel;
27938         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27939         var t = this;
27940
27941         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27942         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27943         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27944         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27945         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27946         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27947         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27948         
27949     },
27950     isTouchEvent : function(e){
27951         return e.type.match(/^touch/);
27952     },
27953     getCoords : function (e) {
27954         var pt    = this.svgEl.dom.createSVGPoint();
27955         pt.x = e.clientX; 
27956         pt.y = e.clientY;
27957         if (this.isTouchEvent(e)) {
27958             pt.x =  e.targetTouches[0].clientX;
27959             pt.y = e.targetTouches[0].clientY;
27960         }
27961         var a = this.svgEl.dom.getScreenCTM();
27962         var b = a.inverse();
27963         var mx = pt.matrixTransform(b);
27964         return mx.x + ',' + mx.y;
27965     },
27966     //mouse event headler 
27967     down : function (e) {
27968         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27969         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27970         
27971         this.isMouseDown = true;
27972         
27973         e.preventDefault();
27974     },
27975     move : function (e) {
27976         if (this.isMouseDown) {
27977             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27978             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27979         }
27980         
27981         e.preventDefault();
27982     },
27983     up : function (e) {
27984         this.isMouseDown = false;
27985         var sp = this.signatureTmp.split(' ');
27986         
27987         if(sp.length > 1){
27988             if(!sp[sp.length-2].match(/^L/)){
27989                 sp.pop();
27990                 sp.pop();
27991                 sp.push("");
27992                 this.signatureTmp = sp.join(" ");
27993             }
27994         }
27995         if(this.getValue() != this.signatureTmp){
27996             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27997             this.isConfirmed = false;
27998         }
27999         e.preventDefault();
28000     },
28001     
28002     /**
28003      * Protected method that will not generally be called directly. It
28004      * is called when the editor creates its toolbar. Override this method if you need to
28005      * add custom toolbar buttons.
28006      * @param {HtmlEditor} editor
28007      */
28008     createToolbar : function(editor){
28009          function btn(id, toggle, handler){
28010             var xid = fid + '-'+ id ;
28011             return {
28012                 id : xid,
28013                 cmd : id,
28014                 cls : 'x-btn-icon x-edit-'+id,
28015                 enableToggle:toggle !== false,
28016                 scope: editor, // was editor...
28017                 handler:handler||editor.relayBtnCmd,
28018                 clickEvent:'mousedown',
28019                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28020                 tabIndex:-1
28021             };
28022         }
28023         
28024         
28025         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28026         this.tb = tb;
28027         this.tb.add(
28028            {
28029                 cls : ' x-signature-btn x-signature-'+id,
28030                 scope: editor, // was editor...
28031                 handler: this.reset,
28032                 clickEvent:'mousedown',
28033                 text: this.labels.clear
28034             },
28035             {
28036                  xtype : 'Fill',
28037                  xns: Roo.Toolbar
28038             }, 
28039             {
28040                 cls : '  x-signature-btn x-signature-'+id,
28041                 scope: editor, // was editor...
28042                 handler: this.confirmHandler,
28043                 clickEvent:'mousedown',
28044                 text: this.labels.confirm
28045             }
28046         );
28047     
28048     },
28049     //public
28050     /**
28051      * when user is clicked confirm then show this image.....
28052      * 
28053      * @return {String} Image Data URI
28054      */
28055     getImageDataURI : function(){
28056         var svg = this.svgEl.dom.parentNode.innerHTML;
28057         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28058         return src; 
28059     },
28060     /**
28061      * 
28062      * @return {Boolean} this.isConfirmed
28063      */
28064     getConfirmed : function(){
28065         return this.isConfirmed;
28066     },
28067     /**
28068      * 
28069      * @return {Number} this.width
28070      */
28071     getWidth : function(){
28072         return this.width;
28073     },
28074     /**
28075      * 
28076      * @return {Number} this.height
28077      */
28078     getHeight : function(){
28079         return this.height;
28080     },
28081     // private
28082     getSignature : function(){
28083         return this.signatureTmp;
28084     },
28085     // private
28086     reset : function(){
28087         this.signatureTmp = '';
28088         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28089         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28090         this.isConfirmed = false;
28091         Roo.form.Signature.superclass.reset.call(this);
28092     },
28093     setSignature : function(s){
28094         this.signatureTmp = s;
28095         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28096         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28097         this.setValue(s);
28098         this.isConfirmed = false;
28099         Roo.form.Signature.superclass.reset.call(this);
28100     }, 
28101     test : function(){
28102 //        Roo.log(this.signPanel.dom.contentWindow.up())
28103     },
28104     //private
28105     setConfirmed : function(){
28106         
28107         
28108         
28109 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28110     },
28111     // private
28112     confirmHandler : function(){
28113         if(!this.getSignature()){
28114             return;
28115         }
28116         
28117         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28118         this.setValue(this.getSignature());
28119         this.isConfirmed = true;
28120         
28121         this.fireEvent('confirm', this);
28122     },
28123     // private
28124     // Subclasses should provide the validation implementation by overriding this
28125     validateValue : function(value){
28126         if(this.allowBlank){
28127             return true;
28128         }
28129         
28130         if(this.isConfirmed){
28131             return true;
28132         }
28133         return false;
28134     }
28135 });/*
28136  * Based on:
28137  * Ext JS Library 1.1.1
28138  * Copyright(c) 2006-2007, Ext JS, LLC.
28139  *
28140  * Originally Released Under LGPL - original licence link has changed is not relivant.
28141  *
28142  * Fork - LGPL
28143  * <script type="text/javascript">
28144  */
28145  
28146
28147 /**
28148  * @class Roo.form.ComboBox
28149  * @extends Roo.form.TriggerField
28150  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28151  * @constructor
28152  * Create a new ComboBox.
28153  * @param {Object} config Configuration options
28154  */
28155 Roo.form.Select = function(config){
28156     Roo.form.Select.superclass.constructor.call(this, config);
28157      
28158 };
28159
28160 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28161     /**
28162      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28163      */
28164     /**
28165      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28166      * rendering into an Roo.Editor, defaults to false)
28167      */
28168     /**
28169      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28170      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28171      */
28172     /**
28173      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28174      */
28175     /**
28176      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28177      * the dropdown list (defaults to undefined, with no header element)
28178      */
28179
28180      /**
28181      * @cfg {String/Roo.Template} tpl The template to use to render the output
28182      */
28183      
28184     // private
28185     defaultAutoCreate : {tag: "select"  },
28186     /**
28187      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28188      */
28189     listWidth: undefined,
28190     /**
28191      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28192      * mode = 'remote' or 'text' if mode = 'local')
28193      */
28194     displayField: undefined,
28195     /**
28196      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28197      * mode = 'remote' or 'value' if mode = 'local'). 
28198      * Note: use of a valueField requires the user make a selection
28199      * in order for a value to be mapped.
28200      */
28201     valueField: undefined,
28202     
28203     
28204     /**
28205      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28206      * field's data value (defaults to the underlying DOM element's name)
28207      */
28208     hiddenName: undefined,
28209     /**
28210      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28211      */
28212     listClass: '',
28213     /**
28214      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28215      */
28216     selectedClass: 'x-combo-selected',
28217     /**
28218      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28219      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28220      * which displays a downward arrow icon).
28221      */
28222     triggerClass : 'x-form-arrow-trigger',
28223     /**
28224      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28225      */
28226     shadow:'sides',
28227     /**
28228      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28229      * anchor positions (defaults to 'tl-bl')
28230      */
28231     listAlign: 'tl-bl?',
28232     /**
28233      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28234      */
28235     maxHeight: 300,
28236     /**
28237      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28238      * query specified by the allQuery config option (defaults to 'query')
28239      */
28240     triggerAction: 'query',
28241     /**
28242      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28243      * (defaults to 4, does not apply if editable = false)
28244      */
28245     minChars : 4,
28246     /**
28247      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28248      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28249      */
28250     typeAhead: false,
28251     /**
28252      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28253      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28254      */
28255     queryDelay: 500,
28256     /**
28257      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28258      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28259      */
28260     pageSize: 0,
28261     /**
28262      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28263      * when editable = true (defaults to false)
28264      */
28265     selectOnFocus:false,
28266     /**
28267      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28268      */
28269     queryParam: 'query',
28270     /**
28271      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28272      * when mode = 'remote' (defaults to 'Loading...')
28273      */
28274     loadingText: 'Loading...',
28275     /**
28276      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28277      */
28278     resizable: false,
28279     /**
28280      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28281      */
28282     handleHeight : 8,
28283     /**
28284      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28285      * traditional select (defaults to true)
28286      */
28287     editable: true,
28288     /**
28289      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28290      */
28291     allQuery: '',
28292     /**
28293      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28294      */
28295     mode: 'remote',
28296     /**
28297      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28298      * listWidth has a higher value)
28299      */
28300     minListWidth : 70,
28301     /**
28302      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28303      * allow the user to set arbitrary text into the field (defaults to false)
28304      */
28305     forceSelection:false,
28306     /**
28307      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28308      * if typeAhead = true (defaults to 250)
28309      */
28310     typeAheadDelay : 250,
28311     /**
28312      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28313      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28314      */
28315     valueNotFoundText : undefined,
28316     
28317     /**
28318      * @cfg {String} defaultValue The value displayed after loading the store.
28319      */
28320     defaultValue: '',
28321     
28322     /**
28323      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28324      */
28325     blockFocus : false,
28326     
28327     /**
28328      * @cfg {Boolean} disableClear Disable showing of clear button.
28329      */
28330     disableClear : false,
28331     /**
28332      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28333      */
28334     alwaysQuery : false,
28335     
28336     //private
28337     addicon : false,
28338     editicon: false,
28339     
28340     // element that contains real text value.. (when hidden is used..)
28341      
28342     // private
28343     onRender : function(ct, position){
28344         Roo.form.Field.prototype.onRender.call(this, ct, position);
28345         
28346         if(this.store){
28347             this.store.on('beforeload', this.onBeforeLoad, this);
28348             this.store.on('load', this.onLoad, this);
28349             this.store.on('loadexception', this.onLoadException, this);
28350             this.store.load({});
28351         }
28352         
28353         
28354         
28355     },
28356
28357     // private
28358     initEvents : function(){
28359         //Roo.form.ComboBox.superclass.initEvents.call(this);
28360  
28361     },
28362
28363     onDestroy : function(){
28364        
28365         if(this.store){
28366             this.store.un('beforeload', this.onBeforeLoad, this);
28367             this.store.un('load', this.onLoad, this);
28368             this.store.un('loadexception', this.onLoadException, this);
28369         }
28370         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28371     },
28372
28373     // private
28374     fireKey : function(e){
28375         if(e.isNavKeyPress() && !this.list.isVisible()){
28376             this.fireEvent("specialkey", this, e);
28377         }
28378     },
28379
28380     // private
28381     onResize: function(w, h){
28382         
28383         return; 
28384     
28385         
28386     },
28387
28388     /**
28389      * Allow or prevent the user from directly editing the field text.  If false is passed,
28390      * the user will only be able to select from the items defined in the dropdown list.  This method
28391      * is the runtime equivalent of setting the 'editable' config option at config time.
28392      * @param {Boolean} value True to allow the user to directly edit the field text
28393      */
28394     setEditable : function(value){
28395          
28396     },
28397
28398     // private
28399     onBeforeLoad : function(){
28400         
28401         Roo.log("Select before load");
28402         return;
28403     
28404         this.innerList.update(this.loadingText ?
28405                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28406         //this.restrictHeight();
28407         this.selectedIndex = -1;
28408     },
28409
28410     // private
28411     onLoad : function(){
28412
28413     
28414         var dom = this.el.dom;
28415         dom.innerHTML = '';
28416          var od = dom.ownerDocument;
28417          
28418         if (this.emptyText) {
28419             var op = od.createElement('option');
28420             op.setAttribute('value', '');
28421             op.innerHTML = String.format('{0}', this.emptyText);
28422             dom.appendChild(op);
28423         }
28424         if(this.store.getCount() > 0){
28425            
28426             var vf = this.valueField;
28427             var df = this.displayField;
28428             this.store.data.each(function(r) {
28429                 // which colmsn to use... testing - cdoe / title..
28430                 var op = od.createElement('option');
28431                 op.setAttribute('value', r.data[vf]);
28432                 op.innerHTML = String.format('{0}', r.data[df]);
28433                 dom.appendChild(op);
28434             });
28435             if (typeof(this.defaultValue != 'undefined')) {
28436                 this.setValue(this.defaultValue);
28437             }
28438             
28439              
28440         }else{
28441             //this.onEmptyResults();
28442         }
28443         //this.el.focus();
28444     },
28445     // private
28446     onLoadException : function()
28447     {
28448         dom.innerHTML = '';
28449             
28450         Roo.log("Select on load exception");
28451         return;
28452     
28453         this.collapse();
28454         Roo.log(this.store.reader.jsonData);
28455         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28456             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28457         }
28458         
28459         
28460     },
28461     // private
28462     onTypeAhead : function(){
28463          
28464     },
28465
28466     // private
28467     onSelect : function(record, index){
28468         Roo.log('on select?');
28469         return;
28470         if(this.fireEvent('beforeselect', this, record, index) !== false){
28471             this.setFromData(index > -1 ? record.data : false);
28472             this.collapse();
28473             this.fireEvent('select', this, record, index);
28474         }
28475     },
28476
28477     /**
28478      * Returns the currently selected field value or empty string if no value is set.
28479      * @return {String} value The selected value
28480      */
28481     getValue : function(){
28482         var dom = this.el.dom;
28483         this.value = dom.options[dom.selectedIndex].value;
28484         return this.value;
28485         
28486     },
28487
28488     /**
28489      * Clears any text/value currently set in the field
28490      */
28491     clearValue : function(){
28492         this.value = '';
28493         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28494         
28495     },
28496
28497     /**
28498      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28499      * will be displayed in the field.  If the value does not match the data value of an existing item,
28500      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28501      * Otherwise the field will be blank (although the value will still be set).
28502      * @param {String} value The value to match
28503      */
28504     setValue : function(v){
28505         var d = this.el.dom;
28506         for (var i =0; i < d.options.length;i++) {
28507             if (v == d.options[i].value) {
28508                 d.selectedIndex = i;
28509                 this.value = v;
28510                 return;
28511             }
28512         }
28513         this.clearValue();
28514     },
28515     /**
28516      * @property {Object} the last set data for the element
28517      */
28518     
28519     lastData : false,
28520     /**
28521      * Sets the value of the field based on a object which is related to the record format for the store.
28522      * @param {Object} value the value to set as. or false on reset?
28523      */
28524     setFromData : function(o){
28525         Roo.log('setfrom data?');
28526          
28527         
28528         
28529     },
28530     // private
28531     reset : function(){
28532         this.clearValue();
28533     },
28534     // private
28535     findRecord : function(prop, value){
28536         
28537         return false;
28538     
28539         var record;
28540         if(this.store.getCount() > 0){
28541             this.store.each(function(r){
28542                 if(r.data[prop] == value){
28543                     record = r;
28544                     return false;
28545                 }
28546                 return true;
28547             });
28548         }
28549         return record;
28550     },
28551     
28552     getName: function()
28553     {
28554         // returns hidden if it's set..
28555         if (!this.rendered) {return ''};
28556         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28557         
28558     },
28559      
28560
28561     
28562
28563     // private
28564     onEmptyResults : function(){
28565         Roo.log('empty results');
28566         //this.collapse();
28567     },
28568
28569     /**
28570      * Returns true if the dropdown list is expanded, else false.
28571      */
28572     isExpanded : function(){
28573         return false;
28574     },
28575
28576     /**
28577      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28578      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28579      * @param {String} value The data value of the item to select
28580      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28581      * selected item if it is not currently in view (defaults to true)
28582      * @return {Boolean} True if the value matched an item in the list, else false
28583      */
28584     selectByValue : function(v, scrollIntoView){
28585         Roo.log('select By Value');
28586         return false;
28587     
28588         if(v !== undefined && v !== null){
28589             var r = this.findRecord(this.valueField || this.displayField, v);
28590             if(r){
28591                 this.select(this.store.indexOf(r), scrollIntoView);
28592                 return true;
28593             }
28594         }
28595         return false;
28596     },
28597
28598     /**
28599      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28600      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28601      * @param {Number} index The zero-based index of the list item to select
28602      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28603      * selected item if it is not currently in view (defaults to true)
28604      */
28605     select : function(index, scrollIntoView){
28606         Roo.log('select ');
28607         return  ;
28608         
28609         this.selectedIndex = index;
28610         this.view.select(index);
28611         if(scrollIntoView !== false){
28612             var el = this.view.getNode(index);
28613             if(el){
28614                 this.innerList.scrollChildIntoView(el, false);
28615             }
28616         }
28617     },
28618
28619       
28620
28621     // private
28622     validateBlur : function(){
28623         
28624         return;
28625         
28626     },
28627
28628     // private
28629     initQuery : function(){
28630         this.doQuery(this.getRawValue());
28631     },
28632
28633     // private
28634     doForce : function(){
28635         if(this.el.dom.value.length > 0){
28636             this.el.dom.value =
28637                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28638              
28639         }
28640     },
28641
28642     /**
28643      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28644      * query allowing the query action to be canceled if needed.
28645      * @param {String} query The SQL query to execute
28646      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28647      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28648      * saved in the current store (defaults to false)
28649      */
28650     doQuery : function(q, forceAll){
28651         
28652         Roo.log('doQuery?');
28653         if(q === undefined || q === null){
28654             q = '';
28655         }
28656         var qe = {
28657             query: q,
28658             forceAll: forceAll,
28659             combo: this,
28660             cancel:false
28661         };
28662         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28663             return false;
28664         }
28665         q = qe.query;
28666         forceAll = qe.forceAll;
28667         if(forceAll === true || (q.length >= this.minChars)){
28668             if(this.lastQuery != q || this.alwaysQuery){
28669                 this.lastQuery = q;
28670                 if(this.mode == 'local'){
28671                     this.selectedIndex = -1;
28672                     if(forceAll){
28673                         this.store.clearFilter();
28674                     }else{
28675                         this.store.filter(this.displayField, q);
28676                     }
28677                     this.onLoad();
28678                 }else{
28679                     this.store.baseParams[this.queryParam] = q;
28680                     this.store.load({
28681                         params: this.getParams(q)
28682                     });
28683                     this.expand();
28684                 }
28685             }else{
28686                 this.selectedIndex = -1;
28687                 this.onLoad();   
28688             }
28689         }
28690     },
28691
28692     // private
28693     getParams : function(q){
28694         var p = {};
28695         //p[this.queryParam] = q;
28696         if(this.pageSize){
28697             p.start = 0;
28698             p.limit = this.pageSize;
28699         }
28700         return p;
28701     },
28702
28703     /**
28704      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28705      */
28706     collapse : function(){
28707         
28708     },
28709
28710     // private
28711     collapseIf : function(e){
28712         
28713     },
28714
28715     /**
28716      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28717      */
28718     expand : function(){
28719         
28720     } ,
28721
28722     // private
28723      
28724
28725     /** 
28726     * @cfg {Boolean} grow 
28727     * @hide 
28728     */
28729     /** 
28730     * @cfg {Number} growMin 
28731     * @hide 
28732     */
28733     /** 
28734     * @cfg {Number} growMax 
28735     * @hide 
28736     */
28737     /**
28738      * @hide
28739      * @method autoSize
28740      */
28741     
28742     setWidth : function()
28743     {
28744         
28745     },
28746     getResizeEl : function(){
28747         return this.el;
28748     }
28749 });//<script type="text/javasscript">
28750  
28751
28752 /**
28753  * @class Roo.DDView
28754  * A DnD enabled version of Roo.View.
28755  * @param {Element/String} container The Element in which to create the View.
28756  * @param {String} tpl The template string used to create the markup for each element of the View
28757  * @param {Object} config The configuration properties. These include all the config options of
28758  * {@link Roo.View} plus some specific to this class.<br>
28759  * <p>
28760  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28761  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28762  * <p>
28763  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28764 .x-view-drag-insert-above {
28765         border-top:1px dotted #3366cc;
28766 }
28767 .x-view-drag-insert-below {
28768         border-bottom:1px dotted #3366cc;
28769 }
28770 </code></pre>
28771  * 
28772  */
28773  
28774 Roo.DDView = function(container, tpl, config) {
28775     Roo.DDView.superclass.constructor.apply(this, arguments);
28776     this.getEl().setStyle("outline", "0px none");
28777     this.getEl().unselectable();
28778     if (this.dragGroup) {
28779                 this.setDraggable(this.dragGroup.split(","));
28780     }
28781     if (this.dropGroup) {
28782                 this.setDroppable(this.dropGroup.split(","));
28783     }
28784     if (this.deletable) {
28785         this.setDeletable();
28786     }
28787     this.isDirtyFlag = false;
28788         this.addEvents({
28789                 "drop" : true
28790         });
28791 };
28792
28793 Roo.extend(Roo.DDView, Roo.View, {
28794 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28795 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28796 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28797 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28798
28799         isFormField: true,
28800
28801         reset: Roo.emptyFn,
28802         
28803         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28804
28805         validate: function() {
28806                 return true;
28807         },
28808         
28809         destroy: function() {
28810                 this.purgeListeners();
28811                 this.getEl.removeAllListeners();
28812                 this.getEl().remove();
28813                 if (this.dragZone) {
28814                         if (this.dragZone.destroy) {
28815                                 this.dragZone.destroy();
28816                         }
28817                 }
28818                 if (this.dropZone) {
28819                         if (this.dropZone.destroy) {
28820                                 this.dropZone.destroy();
28821                         }
28822                 }
28823         },
28824
28825 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28826         getName: function() {
28827                 return this.name;
28828         },
28829
28830 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28831         setValue: function(v) {
28832                 if (!this.store) {
28833                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28834                 }
28835                 var data = {};
28836                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28837                 this.store.proxy = new Roo.data.MemoryProxy(data);
28838                 this.store.load();
28839         },
28840
28841 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28842         getValue: function() {
28843                 var result = '(';
28844                 this.store.each(function(rec) {
28845                         result += rec.id + ',';
28846                 });
28847                 return result.substr(0, result.length - 1) + ')';
28848         },
28849         
28850         getIds: function() {
28851                 var i = 0, result = new Array(this.store.getCount());
28852                 this.store.each(function(rec) {
28853                         result[i++] = rec.id;
28854                 });
28855                 return result;
28856         },
28857         
28858         isDirty: function() {
28859                 return this.isDirtyFlag;
28860         },
28861
28862 /**
28863  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28864  *      whole Element becomes the target, and this causes the drop gesture to append.
28865  */
28866     getTargetFromEvent : function(e) {
28867                 var target = e.getTarget();
28868                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28869                 target = target.parentNode;
28870                 }
28871                 if (!target) {
28872                         target = this.el.dom.lastChild || this.el.dom;
28873                 }
28874                 return target;
28875     },
28876
28877 /**
28878  *      Create the drag data which consists of an object which has the property "ddel" as
28879  *      the drag proxy element. 
28880  */
28881     getDragData : function(e) {
28882         var target = this.findItemFromChild(e.getTarget());
28883                 if(target) {
28884                         this.handleSelection(e);
28885                         var selNodes = this.getSelectedNodes();
28886             var dragData = {
28887                 source: this,
28888                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28889                 nodes: selNodes,
28890                 records: []
28891                         };
28892                         var selectedIndices = this.getSelectedIndexes();
28893                         for (var i = 0; i < selectedIndices.length; i++) {
28894                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28895                         }
28896                         if (selNodes.length == 1) {
28897                                 dragData.ddel = target.cloneNode(true); // the div element
28898                         } else {
28899                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28900                                 div.className = 'multi-proxy';
28901                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28902                                         div.appendChild(selNodes[i].cloneNode(true));
28903                                 }
28904                                 dragData.ddel = div;
28905                         }
28906             //console.log(dragData)
28907             //console.log(dragData.ddel.innerHTML)
28908                         return dragData;
28909                 }
28910         //console.log('nodragData')
28911                 return false;
28912     },
28913     
28914 /**     Specify to which ddGroup items in this DDView may be dragged. */
28915     setDraggable: function(ddGroup) {
28916         if (ddGroup instanceof Array) {
28917                 Roo.each(ddGroup, this.setDraggable, this);
28918                 return;
28919         }
28920         if (this.dragZone) {
28921                 this.dragZone.addToGroup(ddGroup);
28922         } else {
28923                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28924                                 containerScroll: true,
28925                                 ddGroup: ddGroup 
28926
28927                         });
28928 //                      Draggability implies selection. DragZone's mousedown selects the element.
28929                         if (!this.multiSelect) { this.singleSelect = true; }
28930
28931 //                      Wire the DragZone's handlers up to methods in *this*
28932                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28933                 }
28934     },
28935
28936 /**     Specify from which ddGroup this DDView accepts drops. */
28937     setDroppable: function(ddGroup) {
28938         if (ddGroup instanceof Array) {
28939                 Roo.each(ddGroup, this.setDroppable, this);
28940                 return;
28941         }
28942         if (this.dropZone) {
28943                 this.dropZone.addToGroup(ddGroup);
28944         } else {
28945                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28946                                 containerScroll: true,
28947                                 ddGroup: ddGroup
28948                         });
28949
28950 //                      Wire the DropZone's handlers up to methods in *this*
28951                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28952                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28953                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28954                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28955                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28956                 }
28957     },
28958
28959 /**     Decide whether to drop above or below a View node. */
28960     getDropPoint : function(e, n, dd){
28961         if (n == this.el.dom) { return "above"; }
28962                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28963                 var c = t + (b - t) / 2;
28964                 var y = Roo.lib.Event.getPageY(e);
28965                 if(y <= c) {
28966                         return "above";
28967                 }else{
28968                         return "below";
28969                 }
28970     },
28971
28972     onNodeEnter : function(n, dd, e, data){
28973                 return false;
28974     },
28975     
28976     onNodeOver : function(n, dd, e, data){
28977                 var pt = this.getDropPoint(e, n, dd);
28978                 // set the insert point style on the target node
28979                 var dragElClass = this.dropNotAllowed;
28980                 if (pt) {
28981                         var targetElClass;
28982                         if (pt == "above"){
28983                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28984                                 targetElClass = "x-view-drag-insert-above";
28985                         } else {
28986                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28987                                 targetElClass = "x-view-drag-insert-below";
28988                         }
28989                         if (this.lastInsertClass != targetElClass){
28990                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28991                                 this.lastInsertClass = targetElClass;
28992                         }
28993                 }
28994                 return dragElClass;
28995         },
28996
28997     onNodeOut : function(n, dd, e, data){
28998                 this.removeDropIndicators(n);
28999     },
29000
29001     onNodeDrop : function(n, dd, e, data){
29002         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29003                 return false;
29004         }
29005         var pt = this.getDropPoint(e, n, dd);
29006                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29007                 if (pt == "below") { insertAt++; }
29008                 for (var i = 0; i < data.records.length; i++) {
29009                         var r = data.records[i];
29010                         var dup = this.store.getById(r.id);
29011                         if (dup && (dd != this.dragZone)) {
29012                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29013                         } else {
29014                                 if (data.copy) {
29015                                         this.store.insert(insertAt++, r.copy());
29016                                 } else {
29017                                         data.source.isDirtyFlag = true;
29018                                         r.store.remove(r);
29019                                         this.store.insert(insertAt++, r);
29020                                 }
29021                                 this.isDirtyFlag = true;
29022                         }
29023                 }
29024                 this.dragZone.cachedTarget = null;
29025                 return true;
29026     },
29027
29028     removeDropIndicators : function(n){
29029                 if(n){
29030                         Roo.fly(n).removeClass([
29031                                 "x-view-drag-insert-above",
29032                                 "x-view-drag-insert-below"]);
29033                         this.lastInsertClass = "_noclass";
29034                 }
29035     },
29036
29037 /**
29038  *      Utility method. Add a delete option to the DDView's context menu.
29039  *      @param {String} imageUrl The URL of the "delete" icon image.
29040  */
29041         setDeletable: function(imageUrl) {
29042                 if (!this.singleSelect && !this.multiSelect) {
29043                         this.singleSelect = true;
29044                 }
29045                 var c = this.getContextMenu();
29046                 this.contextMenu.on("itemclick", function(item) {
29047                         switch (item.id) {
29048                                 case "delete":
29049                                         this.remove(this.getSelectedIndexes());
29050                                         break;
29051                         }
29052                 }, this);
29053                 this.contextMenu.add({
29054                         icon: imageUrl,
29055                         id: "delete",
29056                         text: 'Delete'
29057                 });
29058         },
29059         
29060 /**     Return the context menu for this DDView. */
29061         getContextMenu: function() {
29062                 if (!this.contextMenu) {
29063 //                      Create the View's context menu
29064                         this.contextMenu = new Roo.menu.Menu({
29065                                 id: this.id + "-contextmenu"
29066                         });
29067                         this.el.on("contextmenu", this.showContextMenu, this);
29068                 }
29069                 return this.contextMenu;
29070         },
29071         
29072         disableContextMenu: function() {
29073                 if (this.contextMenu) {
29074                         this.el.un("contextmenu", this.showContextMenu, this);
29075                 }
29076         },
29077
29078         showContextMenu: function(e, item) {
29079         item = this.findItemFromChild(e.getTarget());
29080                 if (item) {
29081                         e.stopEvent();
29082                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29083                         this.contextMenu.showAt(e.getXY());
29084             }
29085     },
29086
29087 /**
29088  *      Remove {@link Roo.data.Record}s at the specified indices.
29089  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29090  */
29091     remove: function(selectedIndices) {
29092                 selectedIndices = [].concat(selectedIndices);
29093                 for (var i = 0; i < selectedIndices.length; i++) {
29094                         var rec = this.store.getAt(selectedIndices[i]);
29095                         this.store.remove(rec);
29096                 }
29097     },
29098
29099 /**
29100  *      Double click fires the event, but also, if this is draggable, and there is only one other
29101  *      related DropZone, it transfers the selected node.
29102  */
29103     onDblClick : function(e){
29104         var item = this.findItemFromChild(e.getTarget());
29105         if(item){
29106             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29107                 return false;
29108             }
29109             if (this.dragGroup) {
29110                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29111                     while (targets.indexOf(this.dropZone) > -1) {
29112                             targets.remove(this.dropZone);
29113                                 }
29114                     if (targets.length == 1) {
29115                                         this.dragZone.cachedTarget = null;
29116                         var el = Roo.get(targets[0].getEl());
29117                         var box = el.getBox(true);
29118                         targets[0].onNodeDrop(el.dom, {
29119                                 target: el.dom,
29120                                 xy: [box.x, box.y + box.height - 1]
29121                         }, null, this.getDragData(e));
29122                     }
29123                 }
29124         }
29125     },
29126     
29127     handleSelection: function(e) {
29128                 this.dragZone.cachedTarget = null;
29129         var item = this.findItemFromChild(e.getTarget());
29130         if (!item) {
29131                 this.clearSelections(true);
29132                 return;
29133         }
29134                 if (item && (this.multiSelect || this.singleSelect)){
29135                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29136                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29137                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29138                                 this.unselect(item);
29139                         } else {
29140                                 this.select(item, this.multiSelect && e.ctrlKey);
29141                                 this.lastSelection = item;
29142                         }
29143                 }
29144     },
29145
29146     onItemClick : function(item, index, e){
29147                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29148                         return false;
29149                 }
29150                 return true;
29151     },
29152
29153     unselect : function(nodeInfo, suppressEvent){
29154                 var node = this.getNode(nodeInfo);
29155                 if(node && this.isSelected(node)){
29156                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29157                                 Roo.fly(node).removeClass(this.selectedClass);
29158                                 this.selections.remove(node);
29159                                 if(!suppressEvent){
29160                                         this.fireEvent("selectionchange", this, this.selections);
29161                                 }
29162                         }
29163                 }
29164     }
29165 });
29166 /*
29167  * Based on:
29168  * Ext JS Library 1.1.1
29169  * Copyright(c) 2006-2007, Ext JS, LLC.
29170  *
29171  * Originally Released Under LGPL - original licence link has changed is not relivant.
29172  *
29173  * Fork - LGPL
29174  * <script type="text/javascript">
29175  */
29176  
29177 /**
29178  * @class Roo.LayoutManager
29179  * @extends Roo.util.Observable
29180  * Base class for layout managers.
29181  */
29182 Roo.LayoutManager = function(container, config){
29183     Roo.LayoutManager.superclass.constructor.call(this);
29184     this.el = Roo.get(container);
29185     // ie scrollbar fix
29186     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29187         document.body.scroll = "no";
29188     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29189         this.el.position('relative');
29190     }
29191     this.id = this.el.id;
29192     this.el.addClass("x-layout-container");
29193     /** false to disable window resize monitoring @type Boolean */
29194     this.monitorWindowResize = true;
29195     this.regions = {};
29196     this.addEvents({
29197         /**
29198          * @event layout
29199          * Fires when a layout is performed. 
29200          * @param {Roo.LayoutManager} this
29201          */
29202         "layout" : true,
29203         /**
29204          * @event regionresized
29205          * Fires when the user resizes a region. 
29206          * @param {Roo.LayoutRegion} region The resized region
29207          * @param {Number} newSize The new size (width for east/west, height for north/south)
29208          */
29209         "regionresized" : true,
29210         /**
29211          * @event regioncollapsed
29212          * Fires when a region is collapsed. 
29213          * @param {Roo.LayoutRegion} region The collapsed region
29214          */
29215         "regioncollapsed" : true,
29216         /**
29217          * @event regionexpanded
29218          * Fires when a region is expanded.  
29219          * @param {Roo.LayoutRegion} region The expanded region
29220          */
29221         "regionexpanded" : true
29222     });
29223     this.updating = false;
29224     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29225 };
29226
29227 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29228     /**
29229      * Returns true if this layout is currently being updated
29230      * @return {Boolean}
29231      */
29232     isUpdating : function(){
29233         return this.updating; 
29234     },
29235     
29236     /**
29237      * Suspend the LayoutManager from doing auto-layouts while
29238      * making multiple add or remove calls
29239      */
29240     beginUpdate : function(){
29241         this.updating = true;    
29242     },
29243     
29244     /**
29245      * Restore auto-layouts and optionally disable the manager from performing a layout
29246      * @param {Boolean} noLayout true to disable a layout update 
29247      */
29248     endUpdate : function(noLayout){
29249         this.updating = false;
29250         if(!noLayout){
29251             this.layout();
29252         }    
29253     },
29254     
29255     layout: function(){
29256         
29257     },
29258     
29259     onRegionResized : function(region, newSize){
29260         this.fireEvent("regionresized", region, newSize);
29261         this.layout();
29262     },
29263     
29264     onRegionCollapsed : function(region){
29265         this.fireEvent("regioncollapsed", region);
29266     },
29267     
29268     onRegionExpanded : function(region){
29269         this.fireEvent("regionexpanded", region);
29270     },
29271         
29272     /**
29273      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29274      * performs box-model adjustments.
29275      * @return {Object} The size as an object {width: (the width), height: (the height)}
29276      */
29277     getViewSize : function(){
29278         var size;
29279         if(this.el.dom != document.body){
29280             size = this.el.getSize();
29281         }else{
29282             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29283         }
29284         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29285         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29286         return size;
29287     },
29288     
29289     /**
29290      * Returns the Element this layout is bound to.
29291      * @return {Roo.Element}
29292      */
29293     getEl : function(){
29294         return this.el;
29295     },
29296     
29297     /**
29298      * Returns the specified region.
29299      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29300      * @return {Roo.LayoutRegion}
29301      */
29302     getRegion : function(target){
29303         return this.regions[target.toLowerCase()];
29304     },
29305     
29306     onWindowResize : function(){
29307         if(this.monitorWindowResize){
29308             this.layout();
29309         }
29310     }
29311 });/*
29312  * Based on:
29313  * Ext JS Library 1.1.1
29314  * Copyright(c) 2006-2007, Ext JS, LLC.
29315  *
29316  * Originally Released Under LGPL - original licence link has changed is not relivant.
29317  *
29318  * Fork - LGPL
29319  * <script type="text/javascript">
29320  */
29321 /**
29322  * @class Roo.BorderLayout
29323  * @extends Roo.LayoutManager
29324  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29325  * please see: <br><br>
29326  * <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>
29327  * <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>
29328  * Example:
29329  <pre><code>
29330  var layout = new Roo.BorderLayout(document.body, {
29331     north: {
29332         initialSize: 25,
29333         titlebar: false
29334     },
29335     west: {
29336         split:true,
29337         initialSize: 200,
29338         minSize: 175,
29339         maxSize: 400,
29340         titlebar: true,
29341         collapsible: true
29342     },
29343     east: {
29344         split:true,
29345         initialSize: 202,
29346         minSize: 175,
29347         maxSize: 400,
29348         titlebar: true,
29349         collapsible: true
29350     },
29351     south: {
29352         split:true,
29353         initialSize: 100,
29354         minSize: 100,
29355         maxSize: 200,
29356         titlebar: true,
29357         collapsible: true
29358     },
29359     center: {
29360         titlebar: true,
29361         autoScroll:true,
29362         resizeTabs: true,
29363         minTabWidth: 50,
29364         preferredTabWidth: 150
29365     }
29366 });
29367
29368 // shorthand
29369 var CP = Roo.ContentPanel;
29370
29371 layout.beginUpdate();
29372 layout.add("north", new CP("north", "North"));
29373 layout.add("south", new CP("south", {title: "South", closable: true}));
29374 layout.add("west", new CP("west", {title: "West"}));
29375 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29376 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29377 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29378 layout.getRegion("center").showPanel("center1");
29379 layout.endUpdate();
29380 </code></pre>
29381
29382 <b>The container the layout is rendered into can be either the body element or any other element.
29383 If it is not the body element, the container needs to either be an absolute positioned element,
29384 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29385 the container size if it is not the body element.</b>
29386
29387 * @constructor
29388 * Create a new BorderLayout
29389 * @param {String/HTMLElement/Element} container The container this layout is bound to
29390 * @param {Object} config Configuration options
29391  */
29392 Roo.BorderLayout = function(container, config){
29393     config = config || {};
29394     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29395     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29396     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29397         var target = this.factory.validRegions[i];
29398         if(config[target]){
29399             this.addRegion(target, config[target]);
29400         }
29401     }
29402 };
29403
29404 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29405     /**
29406      * Creates and adds a new region if it doesn't already exist.
29407      * @param {String} target The target region key (north, south, east, west or center).
29408      * @param {Object} config The regions config object
29409      * @return {BorderLayoutRegion} The new region
29410      */
29411     addRegion : function(target, config){
29412         if(!this.regions[target]){
29413             var r = this.factory.create(target, this, config);
29414             this.bindRegion(target, r);
29415         }
29416         return this.regions[target];
29417     },
29418
29419     // private (kinda)
29420     bindRegion : function(name, r){
29421         this.regions[name] = r;
29422         r.on("visibilitychange", this.layout, this);
29423         r.on("paneladded", this.layout, this);
29424         r.on("panelremoved", this.layout, this);
29425         r.on("invalidated", this.layout, this);
29426         r.on("resized", this.onRegionResized, this);
29427         r.on("collapsed", this.onRegionCollapsed, this);
29428         r.on("expanded", this.onRegionExpanded, this);
29429     },
29430
29431     /**
29432      * Performs a layout update.
29433      */
29434     layout : function(){
29435         if(this.updating) {
29436             return;
29437         }
29438         var size = this.getViewSize();
29439         var w = size.width;
29440         var h = size.height;
29441         var centerW = w;
29442         var centerH = h;
29443         var centerY = 0;
29444         var centerX = 0;
29445         //var x = 0, y = 0;
29446
29447         var rs = this.regions;
29448         var north = rs["north"];
29449         var south = rs["south"]; 
29450         var west = rs["west"];
29451         var east = rs["east"];
29452         var center = rs["center"];
29453         //if(this.hideOnLayout){ // not supported anymore
29454             //c.el.setStyle("display", "none");
29455         //}
29456         if(north && north.isVisible()){
29457             var b = north.getBox();
29458             var m = north.getMargins();
29459             b.width = w - (m.left+m.right);
29460             b.x = m.left;
29461             b.y = m.top;
29462             centerY = b.height + b.y + m.bottom;
29463             centerH -= centerY;
29464             north.updateBox(this.safeBox(b));
29465         }
29466         if(south && south.isVisible()){
29467             var b = south.getBox();
29468             var m = south.getMargins();
29469             b.width = w - (m.left+m.right);
29470             b.x = m.left;
29471             var totalHeight = (b.height + m.top + m.bottom);
29472             b.y = h - totalHeight + m.top;
29473             centerH -= totalHeight;
29474             south.updateBox(this.safeBox(b));
29475         }
29476         if(west && west.isVisible()){
29477             var b = west.getBox();
29478             var m = west.getMargins();
29479             b.height = centerH - (m.top+m.bottom);
29480             b.x = m.left;
29481             b.y = centerY + m.top;
29482             var totalWidth = (b.width + m.left + m.right);
29483             centerX += totalWidth;
29484             centerW -= totalWidth;
29485             west.updateBox(this.safeBox(b));
29486         }
29487         if(east && east.isVisible()){
29488             var b = east.getBox();
29489             var m = east.getMargins();
29490             b.height = centerH - (m.top+m.bottom);
29491             var totalWidth = (b.width + m.left + m.right);
29492             b.x = w - totalWidth + m.left;
29493             b.y = centerY + m.top;
29494             centerW -= totalWidth;
29495             east.updateBox(this.safeBox(b));
29496         }
29497         if(center){
29498             var m = center.getMargins();
29499             var centerBox = {
29500                 x: centerX + m.left,
29501                 y: centerY + m.top,
29502                 width: centerW - (m.left+m.right),
29503                 height: centerH - (m.top+m.bottom)
29504             };
29505             //if(this.hideOnLayout){
29506                 //center.el.setStyle("display", "block");
29507             //}
29508             center.updateBox(this.safeBox(centerBox));
29509         }
29510         this.el.repaint();
29511         this.fireEvent("layout", this);
29512     },
29513
29514     // private
29515     safeBox : function(box){
29516         box.width = Math.max(0, box.width);
29517         box.height = Math.max(0, box.height);
29518         return box;
29519     },
29520
29521     /**
29522      * Adds a ContentPanel (or subclass) to this layout.
29523      * @param {String} target The target region key (north, south, east, west or center).
29524      * @param {Roo.ContentPanel} panel The panel to add
29525      * @return {Roo.ContentPanel} The added panel
29526      */
29527     add : function(target, panel){
29528          
29529         target = target.toLowerCase();
29530         return this.regions[target].add(panel);
29531     },
29532
29533     /**
29534      * Remove a ContentPanel (or subclass) to this layout.
29535      * @param {String} target The target region key (north, south, east, west or center).
29536      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29537      * @return {Roo.ContentPanel} The removed panel
29538      */
29539     remove : function(target, panel){
29540         target = target.toLowerCase();
29541         return this.regions[target].remove(panel);
29542     },
29543
29544     /**
29545      * Searches all regions for a panel with the specified id
29546      * @param {String} panelId
29547      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29548      */
29549     findPanel : function(panelId){
29550         var rs = this.regions;
29551         for(var target in rs){
29552             if(typeof rs[target] != "function"){
29553                 var p = rs[target].getPanel(panelId);
29554                 if(p){
29555                     return p;
29556                 }
29557             }
29558         }
29559         return null;
29560     },
29561
29562     /**
29563      * Searches all regions for a panel with the specified id and activates (shows) it.
29564      * @param {String/ContentPanel} panelId The panels id or the panel itself
29565      * @return {Roo.ContentPanel} The shown panel or null
29566      */
29567     showPanel : function(panelId) {
29568       var rs = this.regions;
29569       for(var target in rs){
29570          var r = rs[target];
29571          if(typeof r != "function"){
29572             if(r.hasPanel(panelId)){
29573                return r.showPanel(panelId);
29574             }
29575          }
29576       }
29577       return null;
29578    },
29579
29580    /**
29581      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29582      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29583      */
29584     restoreState : function(provider){
29585         if(!provider){
29586             provider = Roo.state.Manager;
29587         }
29588         var sm = new Roo.LayoutStateManager();
29589         sm.init(this, provider);
29590     },
29591
29592     /**
29593      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29594      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29595      * a valid ContentPanel config object.  Example:
29596      * <pre><code>
29597 // Create the main layout
29598 var layout = new Roo.BorderLayout('main-ct', {
29599     west: {
29600         split:true,
29601         minSize: 175,
29602         titlebar: true
29603     },
29604     center: {
29605         title:'Components'
29606     }
29607 }, 'main-ct');
29608
29609 // Create and add multiple ContentPanels at once via configs
29610 layout.batchAdd({
29611    west: {
29612        id: 'source-files',
29613        autoCreate:true,
29614        title:'Ext Source Files',
29615        autoScroll:true,
29616        fitToFrame:true
29617    },
29618    center : {
29619        el: cview,
29620        autoScroll:true,
29621        fitToFrame:true,
29622        toolbar: tb,
29623        resizeEl:'cbody'
29624    }
29625 });
29626 </code></pre>
29627      * @param {Object} regions An object containing ContentPanel configs by region name
29628      */
29629     batchAdd : function(regions){
29630         this.beginUpdate();
29631         for(var rname in regions){
29632             var lr = this.regions[rname];
29633             if(lr){
29634                 this.addTypedPanels(lr, regions[rname]);
29635             }
29636         }
29637         this.endUpdate();
29638     },
29639
29640     // private
29641     addTypedPanels : function(lr, ps){
29642         if(typeof ps == 'string'){
29643             lr.add(new Roo.ContentPanel(ps));
29644         }
29645         else if(ps instanceof Array){
29646             for(var i =0, len = ps.length; i < len; i++){
29647                 this.addTypedPanels(lr, ps[i]);
29648             }
29649         }
29650         else if(!ps.events){ // raw config?
29651             var el = ps.el;
29652             delete ps.el; // prevent conflict
29653             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29654         }
29655         else {  // panel object assumed!
29656             lr.add(ps);
29657         }
29658     },
29659     /**
29660      * Adds a xtype elements to the layout.
29661      * <pre><code>
29662
29663 layout.addxtype({
29664        xtype : 'ContentPanel',
29665        region: 'west',
29666        items: [ .... ]
29667    }
29668 );
29669
29670 layout.addxtype({
29671         xtype : 'NestedLayoutPanel',
29672         region: 'west',
29673         layout: {
29674            center: { },
29675            west: { }   
29676         },
29677         items : [ ... list of content panels or nested layout panels.. ]
29678    }
29679 );
29680 </code></pre>
29681      * @param {Object} cfg Xtype definition of item to add.
29682      */
29683     addxtype : function(cfg)
29684     {
29685         // basically accepts a pannel...
29686         // can accept a layout region..!?!?
29687         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29688         
29689         if (!cfg.xtype.match(/Panel$/)) {
29690             return false;
29691         }
29692         var ret = false;
29693         
29694         if (typeof(cfg.region) == 'undefined') {
29695             Roo.log("Failed to add Panel, region was not set");
29696             Roo.log(cfg);
29697             return false;
29698         }
29699         var region = cfg.region;
29700         delete cfg.region;
29701         
29702           
29703         var xitems = [];
29704         if (cfg.items) {
29705             xitems = cfg.items;
29706             delete cfg.items;
29707         }
29708         var nb = false;
29709         
29710         switch(cfg.xtype) 
29711         {
29712             case 'ContentPanel':  // ContentPanel (el, cfg)
29713             case 'ScrollPanel':  // ContentPanel (el, cfg)
29714             case 'ViewPanel': 
29715                 if(cfg.autoCreate) {
29716                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29717                 } else {
29718                     var el = this.el.createChild();
29719                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29720                 }
29721                 
29722                 this.add(region, ret);
29723                 break;
29724             
29725             
29726             case 'TreePanel': // our new panel!
29727                 cfg.el = this.el.createChild();
29728                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29729                 this.add(region, ret);
29730                 break;
29731             
29732             case 'NestedLayoutPanel': 
29733                 // create a new Layout (which is  a Border Layout...
29734                 var el = this.el.createChild();
29735                 var clayout = cfg.layout;
29736                 delete cfg.layout;
29737                 clayout.items   = clayout.items  || [];
29738                 // replace this exitems with the clayout ones..
29739                 xitems = clayout.items;
29740                  
29741                 
29742                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29743                     cfg.background = false;
29744                 }
29745                 var layout = new Roo.BorderLayout(el, clayout);
29746                 
29747                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29748                 //console.log('adding nested layout panel '  + cfg.toSource());
29749                 this.add(region, ret);
29750                 nb = {}; /// find first...
29751                 break;
29752                 
29753             case 'GridPanel': 
29754             
29755                 // needs grid and region
29756                 
29757                 //var el = this.getRegion(region).el.createChild();
29758                 var el = this.el.createChild();
29759                 // create the grid first...
29760                 
29761                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29762                 delete cfg.grid;
29763                 if (region == 'center' && this.active ) {
29764                     cfg.background = false;
29765                 }
29766                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29767                 
29768                 this.add(region, ret);
29769                 if (cfg.background) {
29770                     ret.on('activate', function(gp) {
29771                         if (!gp.grid.rendered) {
29772                             gp.grid.render();
29773                         }
29774                     });
29775                 } else {
29776                     grid.render();
29777                 }
29778                 break;
29779            
29780            
29781            
29782                 
29783                 
29784                 
29785             default:
29786                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29787                     
29788                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29789                     this.add(region, ret);
29790                 } else {
29791                 
29792                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29793                     return null;
29794                 }
29795                 
29796              // GridPanel (grid, cfg)
29797             
29798         }
29799         this.beginUpdate();
29800         // add children..
29801         var region = '';
29802         var abn = {};
29803         Roo.each(xitems, function(i)  {
29804             region = nb && i.region ? i.region : false;
29805             
29806             var add = ret.addxtype(i);
29807            
29808             if (region) {
29809                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29810                 if (!i.background) {
29811                     abn[region] = nb[region] ;
29812                 }
29813             }
29814             
29815         });
29816         this.endUpdate();
29817
29818         // make the last non-background panel active..
29819         //if (nb) { Roo.log(abn); }
29820         if (nb) {
29821             
29822             for(var r in abn) {
29823                 region = this.getRegion(r);
29824                 if (region) {
29825                     // tried using nb[r], but it does not work..
29826                      
29827                     region.showPanel(abn[r]);
29828                    
29829                 }
29830             }
29831         }
29832         return ret;
29833         
29834     }
29835 });
29836
29837 /**
29838  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29839  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29840  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29841  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29842  * <pre><code>
29843 // shorthand
29844 var CP = Roo.ContentPanel;
29845
29846 var layout = Roo.BorderLayout.create({
29847     north: {
29848         initialSize: 25,
29849         titlebar: false,
29850         panels: [new CP("north", "North")]
29851     },
29852     west: {
29853         split:true,
29854         initialSize: 200,
29855         minSize: 175,
29856         maxSize: 400,
29857         titlebar: true,
29858         collapsible: true,
29859         panels: [new CP("west", {title: "West"})]
29860     },
29861     east: {
29862         split:true,
29863         initialSize: 202,
29864         minSize: 175,
29865         maxSize: 400,
29866         titlebar: true,
29867         collapsible: true,
29868         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29869     },
29870     south: {
29871         split:true,
29872         initialSize: 100,
29873         minSize: 100,
29874         maxSize: 200,
29875         titlebar: true,
29876         collapsible: true,
29877         panels: [new CP("south", {title: "South", closable: true})]
29878     },
29879     center: {
29880         titlebar: true,
29881         autoScroll:true,
29882         resizeTabs: true,
29883         minTabWidth: 50,
29884         preferredTabWidth: 150,
29885         panels: [
29886             new CP("center1", {title: "Close Me", closable: true}),
29887             new CP("center2", {title: "Center Panel", closable: false})
29888         ]
29889     }
29890 }, document.body);
29891
29892 layout.getRegion("center").showPanel("center1");
29893 </code></pre>
29894  * @param config
29895  * @param targetEl
29896  */
29897 Roo.BorderLayout.create = function(config, targetEl){
29898     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29899     layout.beginUpdate();
29900     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29901     for(var j = 0, jlen = regions.length; j < jlen; j++){
29902         var lr = regions[j];
29903         if(layout.regions[lr] && config[lr].panels){
29904             var r = layout.regions[lr];
29905             var ps = config[lr].panels;
29906             layout.addTypedPanels(r, ps);
29907         }
29908     }
29909     layout.endUpdate();
29910     return layout;
29911 };
29912
29913 // private
29914 Roo.BorderLayout.RegionFactory = {
29915     // private
29916     validRegions : ["north","south","east","west","center"],
29917
29918     // private
29919     create : function(target, mgr, config){
29920         target = target.toLowerCase();
29921         if(config.lightweight || config.basic){
29922             return new Roo.BasicLayoutRegion(mgr, config, target);
29923         }
29924         switch(target){
29925             case "north":
29926                 return new Roo.NorthLayoutRegion(mgr, config);
29927             case "south":
29928                 return new Roo.SouthLayoutRegion(mgr, config);
29929             case "east":
29930                 return new Roo.EastLayoutRegion(mgr, config);
29931             case "west":
29932                 return new Roo.WestLayoutRegion(mgr, config);
29933             case "center":
29934                 return new Roo.CenterLayoutRegion(mgr, config);
29935         }
29936         throw 'Layout region "'+target+'" not supported.';
29937     }
29938 };/*
29939  * Based on:
29940  * Ext JS Library 1.1.1
29941  * Copyright(c) 2006-2007, Ext JS, LLC.
29942  *
29943  * Originally Released Under LGPL - original licence link has changed is not relivant.
29944  *
29945  * Fork - LGPL
29946  * <script type="text/javascript">
29947  */
29948  
29949 /**
29950  * @class Roo.BasicLayoutRegion
29951  * @extends Roo.util.Observable
29952  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29953  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29954  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29955  */
29956 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29957     this.mgr = mgr;
29958     this.position  = pos;
29959     this.events = {
29960         /**
29961          * @scope Roo.BasicLayoutRegion
29962          */
29963         
29964         /**
29965          * @event beforeremove
29966          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29967          * @param {Roo.LayoutRegion} this
29968          * @param {Roo.ContentPanel} panel The panel
29969          * @param {Object} e The cancel event object
29970          */
29971         "beforeremove" : true,
29972         /**
29973          * @event invalidated
29974          * Fires when the layout for this region is changed.
29975          * @param {Roo.LayoutRegion} this
29976          */
29977         "invalidated" : true,
29978         /**
29979          * @event visibilitychange
29980          * Fires when this region is shown or hidden 
29981          * @param {Roo.LayoutRegion} this
29982          * @param {Boolean} visibility true or false
29983          */
29984         "visibilitychange" : true,
29985         /**
29986          * @event paneladded
29987          * Fires when a panel is added. 
29988          * @param {Roo.LayoutRegion} this
29989          * @param {Roo.ContentPanel} panel The panel
29990          */
29991         "paneladded" : true,
29992         /**
29993          * @event panelremoved
29994          * Fires when a panel is removed. 
29995          * @param {Roo.LayoutRegion} this
29996          * @param {Roo.ContentPanel} panel The panel
29997          */
29998         "panelremoved" : true,
29999         /**
30000          * @event beforecollapse
30001          * Fires when this region before collapse.
30002          * @param {Roo.LayoutRegion} this
30003          */
30004         "beforecollapse" : true,
30005         /**
30006          * @event collapsed
30007          * Fires when this region is collapsed.
30008          * @param {Roo.LayoutRegion} this
30009          */
30010         "collapsed" : true,
30011         /**
30012          * @event expanded
30013          * Fires when this region is expanded.
30014          * @param {Roo.LayoutRegion} this
30015          */
30016         "expanded" : true,
30017         /**
30018          * @event slideshow
30019          * Fires when this region is slid into view.
30020          * @param {Roo.LayoutRegion} this
30021          */
30022         "slideshow" : true,
30023         /**
30024          * @event slidehide
30025          * Fires when this region slides out of view. 
30026          * @param {Roo.LayoutRegion} this
30027          */
30028         "slidehide" : true,
30029         /**
30030          * @event panelactivated
30031          * Fires when a panel is activated. 
30032          * @param {Roo.LayoutRegion} this
30033          * @param {Roo.ContentPanel} panel The activated panel
30034          */
30035         "panelactivated" : true,
30036         /**
30037          * @event resized
30038          * Fires when the user resizes this region. 
30039          * @param {Roo.LayoutRegion} this
30040          * @param {Number} newSize The new size (width for east/west, height for north/south)
30041          */
30042         "resized" : true
30043     };
30044     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30045     this.panels = new Roo.util.MixedCollection();
30046     this.panels.getKey = this.getPanelId.createDelegate(this);
30047     this.box = null;
30048     this.activePanel = null;
30049     // ensure listeners are added...
30050     
30051     if (config.listeners || config.events) {
30052         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30053             listeners : config.listeners || {},
30054             events : config.events || {}
30055         });
30056     }
30057     
30058     if(skipConfig !== true){
30059         this.applyConfig(config);
30060     }
30061 };
30062
30063 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30064     getPanelId : function(p){
30065         return p.getId();
30066     },
30067     
30068     applyConfig : function(config){
30069         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30070         this.config = config;
30071         
30072     },
30073     
30074     /**
30075      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30076      * the width, for horizontal (north, south) the height.
30077      * @param {Number} newSize The new width or height
30078      */
30079     resizeTo : function(newSize){
30080         var el = this.el ? this.el :
30081                  (this.activePanel ? this.activePanel.getEl() : null);
30082         if(el){
30083             switch(this.position){
30084                 case "east":
30085                 case "west":
30086                     el.setWidth(newSize);
30087                     this.fireEvent("resized", this, newSize);
30088                 break;
30089                 case "north":
30090                 case "south":
30091                     el.setHeight(newSize);
30092                     this.fireEvent("resized", this, newSize);
30093                 break;                
30094             }
30095         }
30096     },
30097     
30098     getBox : function(){
30099         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30100     },
30101     
30102     getMargins : function(){
30103         return this.margins;
30104     },
30105     
30106     updateBox : function(box){
30107         this.box = box;
30108         var el = this.activePanel.getEl();
30109         el.dom.style.left = box.x + "px";
30110         el.dom.style.top = box.y + "px";
30111         this.activePanel.setSize(box.width, box.height);
30112     },
30113     
30114     /**
30115      * Returns the container element for this region.
30116      * @return {Roo.Element}
30117      */
30118     getEl : function(){
30119         return this.activePanel;
30120     },
30121     
30122     /**
30123      * Returns true if this region is currently visible.
30124      * @return {Boolean}
30125      */
30126     isVisible : function(){
30127         return this.activePanel ? true : false;
30128     },
30129     
30130     setActivePanel : function(panel){
30131         panel = this.getPanel(panel);
30132         if(this.activePanel && this.activePanel != panel){
30133             this.activePanel.setActiveState(false);
30134             this.activePanel.getEl().setLeftTop(-10000,-10000);
30135         }
30136         this.activePanel = panel;
30137         panel.setActiveState(true);
30138         if(this.box){
30139             panel.setSize(this.box.width, this.box.height);
30140         }
30141         this.fireEvent("panelactivated", this, panel);
30142         this.fireEvent("invalidated");
30143     },
30144     
30145     /**
30146      * Show the specified panel.
30147      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30148      * @return {Roo.ContentPanel} The shown panel or null
30149      */
30150     showPanel : function(panel){
30151         if(panel = this.getPanel(panel)){
30152             this.setActivePanel(panel);
30153         }
30154         return panel;
30155     },
30156     
30157     /**
30158      * Get the active panel for this region.
30159      * @return {Roo.ContentPanel} The active panel or null
30160      */
30161     getActivePanel : function(){
30162         return this.activePanel;
30163     },
30164     
30165     /**
30166      * Add the passed ContentPanel(s)
30167      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30168      * @return {Roo.ContentPanel} The panel added (if only one was added)
30169      */
30170     add : function(panel){
30171         if(arguments.length > 1){
30172             for(var i = 0, len = arguments.length; i < len; i++) {
30173                 this.add(arguments[i]);
30174             }
30175             return null;
30176         }
30177         if(this.hasPanel(panel)){
30178             this.showPanel(panel);
30179             return panel;
30180         }
30181         var el = panel.getEl();
30182         if(el.dom.parentNode != this.mgr.el.dom){
30183             this.mgr.el.dom.appendChild(el.dom);
30184         }
30185         if(panel.setRegion){
30186             panel.setRegion(this);
30187         }
30188         this.panels.add(panel);
30189         el.setStyle("position", "absolute");
30190         if(!panel.background){
30191             this.setActivePanel(panel);
30192             if(this.config.initialSize && this.panels.getCount()==1){
30193                 this.resizeTo(this.config.initialSize);
30194             }
30195         }
30196         this.fireEvent("paneladded", this, panel);
30197         return panel;
30198     },
30199     
30200     /**
30201      * Returns true if the panel is in this region.
30202      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30203      * @return {Boolean}
30204      */
30205     hasPanel : function(panel){
30206         if(typeof panel == "object"){ // must be panel obj
30207             panel = panel.getId();
30208         }
30209         return this.getPanel(panel) ? true : false;
30210     },
30211     
30212     /**
30213      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30214      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30215      * @param {Boolean} preservePanel Overrides the config preservePanel option
30216      * @return {Roo.ContentPanel} The panel that was removed
30217      */
30218     remove : function(panel, preservePanel){
30219         panel = this.getPanel(panel);
30220         if(!panel){
30221             return null;
30222         }
30223         var e = {};
30224         this.fireEvent("beforeremove", this, panel, e);
30225         if(e.cancel === true){
30226             return null;
30227         }
30228         var panelId = panel.getId();
30229         this.panels.removeKey(panelId);
30230         return panel;
30231     },
30232     
30233     /**
30234      * Returns the panel specified or null if it's not in this region.
30235      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30236      * @return {Roo.ContentPanel}
30237      */
30238     getPanel : function(id){
30239         if(typeof id == "object"){ // must be panel obj
30240             return id;
30241         }
30242         return this.panels.get(id);
30243     },
30244     
30245     /**
30246      * Returns this regions position (north/south/east/west/center).
30247      * @return {String} 
30248      */
30249     getPosition: function(){
30250         return this.position;    
30251     }
30252 });/*
30253  * Based on:
30254  * Ext JS Library 1.1.1
30255  * Copyright(c) 2006-2007, Ext JS, LLC.
30256  *
30257  * Originally Released Under LGPL - original licence link has changed is not relivant.
30258  *
30259  * Fork - LGPL
30260  * <script type="text/javascript">
30261  */
30262  
30263 /**
30264  * @class Roo.LayoutRegion
30265  * @extends Roo.BasicLayoutRegion
30266  * This class represents a region in a layout manager.
30267  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30268  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30269  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30270  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30271  * @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})
30272  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30273  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30274  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30275  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30276  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30277  * @cfg {String}    title           The title for the region (overrides panel titles)
30278  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30279  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30280  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30281  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30282  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30283  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30284  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30285  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30286  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30287  * @cfg {Boolean}   showPin         True to show a pin button
30288  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30289  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30290  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30291  * @cfg {Number}    width           For East/West panels
30292  * @cfg {Number}    height          For North/South panels
30293  * @cfg {Boolean}   split           To show the splitter
30294  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30295  */
30296 Roo.LayoutRegion = function(mgr, config, pos){
30297     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30298     var dh = Roo.DomHelper;
30299     /** This region's container element 
30300     * @type Roo.Element */
30301     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30302     /** This region's title element 
30303     * @type Roo.Element */
30304
30305     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30306         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30307         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30308     ]}, true);
30309     this.titleEl.enableDisplayMode();
30310     /** This region's title text element 
30311     * @type HTMLElement */
30312     this.titleTextEl = this.titleEl.dom.firstChild;
30313     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30314     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30315     this.closeBtn.enableDisplayMode();
30316     this.closeBtn.on("click", this.closeClicked, this);
30317     this.closeBtn.hide();
30318
30319     this.createBody(config);
30320     this.visible = true;
30321     this.collapsed = false;
30322
30323     if(config.hideWhenEmpty){
30324         this.hide();
30325         this.on("paneladded", this.validateVisibility, this);
30326         this.on("panelremoved", this.validateVisibility, this);
30327     }
30328     this.applyConfig(config);
30329 };
30330
30331 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30332
30333     createBody : function(){
30334         /** This region's body element 
30335         * @type Roo.Element */
30336         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30337     },
30338
30339     applyConfig : function(c){
30340         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30341             var dh = Roo.DomHelper;
30342             if(c.titlebar !== false){
30343                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30344                 this.collapseBtn.on("click", this.collapse, this);
30345                 this.collapseBtn.enableDisplayMode();
30346
30347                 if(c.showPin === true || this.showPin){
30348                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30349                     this.stickBtn.enableDisplayMode();
30350                     this.stickBtn.on("click", this.expand, this);
30351                     this.stickBtn.hide();
30352                 }
30353             }
30354             /** This region's collapsed element
30355             * @type Roo.Element */
30356             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30357                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30358             ]}, true);
30359             if(c.floatable !== false){
30360                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30361                this.collapsedEl.on("click", this.collapseClick, this);
30362             }
30363
30364             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30365                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30366                    id: "message", unselectable: "on", style:{"float":"left"}});
30367                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30368              }
30369             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30370             this.expandBtn.on("click", this.expand, this);
30371         }
30372         if(this.collapseBtn){
30373             this.collapseBtn.setVisible(c.collapsible == true);
30374         }
30375         this.cmargins = c.cmargins || this.cmargins ||
30376                          (this.position == "west" || this.position == "east" ?
30377                              {top: 0, left: 2, right:2, bottom: 0} :
30378                              {top: 2, left: 0, right:0, bottom: 2});
30379         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30380         this.bottomTabs = c.tabPosition != "top";
30381         this.autoScroll = c.autoScroll || false;
30382         if(this.autoScroll){
30383             this.bodyEl.setStyle("overflow", "auto");
30384         }else{
30385             this.bodyEl.setStyle("overflow", "hidden");
30386         }
30387         //if(c.titlebar !== false){
30388             if((!c.titlebar && !c.title) || c.titlebar === false){
30389                 this.titleEl.hide();
30390             }else{
30391                 this.titleEl.show();
30392                 if(c.title){
30393                     this.titleTextEl.innerHTML = c.title;
30394                 }
30395             }
30396         //}
30397         this.duration = c.duration || .30;
30398         this.slideDuration = c.slideDuration || .45;
30399         this.config = c;
30400         if(c.collapsed){
30401             this.collapse(true);
30402         }
30403         if(c.hidden){
30404             this.hide();
30405         }
30406     },
30407     /**
30408      * Returns true if this region is currently visible.
30409      * @return {Boolean}
30410      */
30411     isVisible : function(){
30412         return this.visible;
30413     },
30414
30415     /**
30416      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30417      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30418      */
30419     setCollapsedTitle : function(title){
30420         title = title || "&#160;";
30421         if(this.collapsedTitleTextEl){
30422             this.collapsedTitleTextEl.innerHTML = title;
30423         }
30424     },
30425
30426     getBox : function(){
30427         var b;
30428         if(!this.collapsed){
30429             b = this.el.getBox(false, true);
30430         }else{
30431             b = this.collapsedEl.getBox(false, true);
30432         }
30433         return b;
30434     },
30435
30436     getMargins : function(){
30437         return this.collapsed ? this.cmargins : this.margins;
30438     },
30439
30440     highlight : function(){
30441         this.el.addClass("x-layout-panel-dragover");
30442     },
30443
30444     unhighlight : function(){
30445         this.el.removeClass("x-layout-panel-dragover");
30446     },
30447
30448     updateBox : function(box){
30449         this.box = box;
30450         if(!this.collapsed){
30451             this.el.dom.style.left = box.x + "px";
30452             this.el.dom.style.top = box.y + "px";
30453             this.updateBody(box.width, box.height);
30454         }else{
30455             this.collapsedEl.dom.style.left = box.x + "px";
30456             this.collapsedEl.dom.style.top = box.y + "px";
30457             this.collapsedEl.setSize(box.width, box.height);
30458         }
30459         if(this.tabs){
30460             this.tabs.autoSizeTabs();
30461         }
30462     },
30463
30464     updateBody : function(w, h){
30465         if(w !== null){
30466             this.el.setWidth(w);
30467             w -= this.el.getBorderWidth("rl");
30468             if(this.config.adjustments){
30469                 w += this.config.adjustments[0];
30470             }
30471         }
30472         if(h !== null){
30473             this.el.setHeight(h);
30474             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30475             h -= this.el.getBorderWidth("tb");
30476             if(this.config.adjustments){
30477                 h += this.config.adjustments[1];
30478             }
30479             this.bodyEl.setHeight(h);
30480             if(this.tabs){
30481                 h = this.tabs.syncHeight(h);
30482             }
30483         }
30484         if(this.panelSize){
30485             w = w !== null ? w : this.panelSize.width;
30486             h = h !== null ? h : this.panelSize.height;
30487         }
30488         if(this.activePanel){
30489             var el = this.activePanel.getEl();
30490             w = w !== null ? w : el.getWidth();
30491             h = h !== null ? h : el.getHeight();
30492             this.panelSize = {width: w, height: h};
30493             this.activePanel.setSize(w, h);
30494         }
30495         if(Roo.isIE && this.tabs){
30496             this.tabs.el.repaint();
30497         }
30498     },
30499
30500     /**
30501      * Returns the container element for this region.
30502      * @return {Roo.Element}
30503      */
30504     getEl : function(){
30505         return this.el;
30506     },
30507
30508     /**
30509      * Hides this region.
30510      */
30511     hide : function(){
30512         if(!this.collapsed){
30513             this.el.dom.style.left = "-2000px";
30514             this.el.hide();
30515         }else{
30516             this.collapsedEl.dom.style.left = "-2000px";
30517             this.collapsedEl.hide();
30518         }
30519         this.visible = false;
30520         this.fireEvent("visibilitychange", this, false);
30521     },
30522
30523     /**
30524      * Shows this region if it was previously hidden.
30525      */
30526     show : function(){
30527         if(!this.collapsed){
30528             this.el.show();
30529         }else{
30530             this.collapsedEl.show();
30531         }
30532         this.visible = true;
30533         this.fireEvent("visibilitychange", this, true);
30534     },
30535
30536     closeClicked : function(){
30537         if(this.activePanel){
30538             this.remove(this.activePanel);
30539         }
30540     },
30541
30542     collapseClick : function(e){
30543         if(this.isSlid){
30544            e.stopPropagation();
30545            this.slideIn();
30546         }else{
30547            e.stopPropagation();
30548            this.slideOut();
30549         }
30550     },
30551
30552     /**
30553      * Collapses this region.
30554      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30555      */
30556     collapse : function(skipAnim, skipCheck){
30557         if(this.collapsed) {
30558             return;
30559         }
30560         
30561         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30562             
30563             this.collapsed = true;
30564             if(this.split){
30565                 this.split.el.hide();
30566             }
30567             if(this.config.animate && skipAnim !== true){
30568                 this.fireEvent("invalidated", this);
30569                 this.animateCollapse();
30570             }else{
30571                 this.el.setLocation(-20000,-20000);
30572                 this.el.hide();
30573                 this.collapsedEl.show();
30574                 this.fireEvent("collapsed", this);
30575                 this.fireEvent("invalidated", this);
30576             }
30577         }
30578         
30579     },
30580
30581     animateCollapse : function(){
30582         // overridden
30583     },
30584
30585     /**
30586      * Expands this region if it was previously collapsed.
30587      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30588      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30589      */
30590     expand : function(e, skipAnim){
30591         if(e) {
30592             e.stopPropagation();
30593         }
30594         if(!this.collapsed || this.el.hasActiveFx()) {
30595             return;
30596         }
30597         if(this.isSlid){
30598             this.afterSlideIn();
30599             skipAnim = true;
30600         }
30601         this.collapsed = false;
30602         if(this.config.animate && skipAnim !== true){
30603             this.animateExpand();
30604         }else{
30605             this.el.show();
30606             if(this.split){
30607                 this.split.el.show();
30608             }
30609             this.collapsedEl.setLocation(-2000,-2000);
30610             this.collapsedEl.hide();
30611             this.fireEvent("invalidated", this);
30612             this.fireEvent("expanded", this);
30613         }
30614     },
30615
30616     animateExpand : function(){
30617         // overridden
30618     },
30619
30620     initTabs : function()
30621     {
30622         this.bodyEl.setStyle("overflow", "hidden");
30623         var ts = new Roo.TabPanel(
30624                 this.bodyEl.dom,
30625                 {
30626                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30627                     disableTooltips: this.config.disableTabTips,
30628                     toolbar : this.config.toolbar
30629                 }
30630         );
30631         if(this.config.hideTabs){
30632             ts.stripWrap.setDisplayed(false);
30633         }
30634         this.tabs = ts;
30635         ts.resizeTabs = this.config.resizeTabs === true;
30636         ts.minTabWidth = this.config.minTabWidth || 40;
30637         ts.maxTabWidth = this.config.maxTabWidth || 250;
30638         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30639         ts.monitorResize = false;
30640         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30641         ts.bodyEl.addClass('x-layout-tabs-body');
30642         this.panels.each(this.initPanelAsTab, this);
30643     },
30644
30645     initPanelAsTab : function(panel){
30646         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30647                     this.config.closeOnTab && panel.isClosable());
30648         if(panel.tabTip !== undefined){
30649             ti.setTooltip(panel.tabTip);
30650         }
30651         ti.on("activate", function(){
30652               this.setActivePanel(panel);
30653         }, this);
30654         if(this.config.closeOnTab){
30655             ti.on("beforeclose", function(t, e){
30656                 e.cancel = true;
30657                 this.remove(panel);
30658             }, this);
30659         }
30660         return ti;
30661     },
30662
30663     updatePanelTitle : function(panel, title){
30664         if(this.activePanel == panel){
30665             this.updateTitle(title);
30666         }
30667         if(this.tabs){
30668             var ti = this.tabs.getTab(panel.getEl().id);
30669             ti.setText(title);
30670             if(panel.tabTip !== undefined){
30671                 ti.setTooltip(panel.tabTip);
30672             }
30673         }
30674     },
30675
30676     updateTitle : function(title){
30677         if(this.titleTextEl && !this.config.title){
30678             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30679         }
30680     },
30681
30682     setActivePanel : function(panel){
30683         panel = this.getPanel(panel);
30684         if(this.activePanel && this.activePanel != panel){
30685             this.activePanel.setActiveState(false);
30686         }
30687         this.activePanel = panel;
30688         panel.setActiveState(true);
30689         if(this.panelSize){
30690             panel.setSize(this.panelSize.width, this.panelSize.height);
30691         }
30692         if(this.closeBtn){
30693             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30694         }
30695         this.updateTitle(panel.getTitle());
30696         if(this.tabs){
30697             this.fireEvent("invalidated", this);
30698         }
30699         this.fireEvent("panelactivated", this, panel);
30700     },
30701
30702     /**
30703      * Shows the specified panel.
30704      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30705      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30706      */
30707     showPanel : function(panel)
30708     {
30709         panel = this.getPanel(panel);
30710         if(panel){
30711             if(this.tabs){
30712                 var tab = this.tabs.getTab(panel.getEl().id);
30713                 if(tab.isHidden()){
30714                     this.tabs.unhideTab(tab.id);
30715                 }
30716                 tab.activate();
30717             }else{
30718                 this.setActivePanel(panel);
30719             }
30720         }
30721         return panel;
30722     },
30723
30724     /**
30725      * Get the active panel for this region.
30726      * @return {Roo.ContentPanel} The active panel or null
30727      */
30728     getActivePanel : function(){
30729         return this.activePanel;
30730     },
30731
30732     validateVisibility : function(){
30733         if(this.panels.getCount() < 1){
30734             this.updateTitle("&#160;");
30735             this.closeBtn.hide();
30736             this.hide();
30737         }else{
30738             if(!this.isVisible()){
30739                 this.show();
30740             }
30741         }
30742     },
30743
30744     /**
30745      * Adds the passed ContentPanel(s) to this region.
30746      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30747      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30748      */
30749     add : function(panel){
30750         if(arguments.length > 1){
30751             for(var i = 0, len = arguments.length; i < len; i++) {
30752                 this.add(arguments[i]);
30753             }
30754             return null;
30755         }
30756         if(this.hasPanel(panel)){
30757             this.showPanel(panel);
30758             return panel;
30759         }
30760         panel.setRegion(this);
30761         this.panels.add(panel);
30762         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30763             this.bodyEl.dom.appendChild(panel.getEl().dom);
30764             if(panel.background !== true){
30765                 this.setActivePanel(panel);
30766             }
30767             this.fireEvent("paneladded", this, panel);
30768             return panel;
30769         }
30770         if(!this.tabs){
30771             this.initTabs();
30772         }else{
30773             this.initPanelAsTab(panel);
30774         }
30775         if(panel.background !== true){
30776             this.tabs.activate(panel.getEl().id);
30777         }
30778         this.fireEvent("paneladded", this, panel);
30779         return panel;
30780     },
30781
30782     /**
30783      * Hides the tab for the specified panel.
30784      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30785      */
30786     hidePanel : function(panel){
30787         if(this.tabs && (panel = this.getPanel(panel))){
30788             this.tabs.hideTab(panel.getEl().id);
30789         }
30790     },
30791
30792     /**
30793      * Unhides the tab for a previously hidden panel.
30794      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30795      */
30796     unhidePanel : function(panel){
30797         if(this.tabs && (panel = this.getPanel(panel))){
30798             this.tabs.unhideTab(panel.getEl().id);
30799         }
30800     },
30801
30802     clearPanels : function(){
30803         while(this.panels.getCount() > 0){
30804              this.remove(this.panels.first());
30805         }
30806     },
30807
30808     /**
30809      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30810      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30811      * @param {Boolean} preservePanel Overrides the config preservePanel option
30812      * @return {Roo.ContentPanel} The panel that was removed
30813      */
30814     remove : function(panel, preservePanel){
30815         panel = this.getPanel(panel);
30816         if(!panel){
30817             return null;
30818         }
30819         var e = {};
30820         this.fireEvent("beforeremove", this, panel, e);
30821         if(e.cancel === true){
30822             return null;
30823         }
30824         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30825         var panelId = panel.getId();
30826         this.panels.removeKey(panelId);
30827         if(preservePanel){
30828             document.body.appendChild(panel.getEl().dom);
30829         }
30830         if(this.tabs){
30831             this.tabs.removeTab(panel.getEl().id);
30832         }else if (!preservePanel){
30833             this.bodyEl.dom.removeChild(panel.getEl().dom);
30834         }
30835         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30836             var p = this.panels.first();
30837             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30838             tempEl.appendChild(p.getEl().dom);
30839             this.bodyEl.update("");
30840             this.bodyEl.dom.appendChild(p.getEl().dom);
30841             tempEl = null;
30842             this.updateTitle(p.getTitle());
30843             this.tabs = null;
30844             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30845             this.setActivePanel(p);
30846         }
30847         panel.setRegion(null);
30848         if(this.activePanel == panel){
30849             this.activePanel = null;
30850         }
30851         if(this.config.autoDestroy !== false && preservePanel !== true){
30852             try{panel.destroy();}catch(e){}
30853         }
30854         this.fireEvent("panelremoved", this, panel);
30855         return panel;
30856     },
30857
30858     /**
30859      * Returns the TabPanel component used by this region
30860      * @return {Roo.TabPanel}
30861      */
30862     getTabs : function(){
30863         return this.tabs;
30864     },
30865
30866     createTool : function(parentEl, className){
30867         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30868             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30869         btn.addClassOnOver("x-layout-tools-button-over");
30870         return btn;
30871     }
30872 });/*
30873  * Based on:
30874  * Ext JS Library 1.1.1
30875  * Copyright(c) 2006-2007, Ext JS, LLC.
30876  *
30877  * Originally Released Under LGPL - original licence link has changed is not relivant.
30878  *
30879  * Fork - LGPL
30880  * <script type="text/javascript">
30881  */
30882  
30883
30884
30885 /**
30886  * @class Roo.SplitLayoutRegion
30887  * @extends Roo.LayoutRegion
30888  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30889  */
30890 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30891     this.cursor = cursor;
30892     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30893 };
30894
30895 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30896     splitTip : "Drag to resize.",
30897     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30898     useSplitTips : false,
30899
30900     applyConfig : function(config){
30901         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30902         if(config.split){
30903             if(!this.split){
30904                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30905                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30906                 /** The SplitBar for this region 
30907                 * @type Roo.SplitBar */
30908                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30909                 this.split.on("moved", this.onSplitMove, this);
30910                 this.split.useShim = config.useShim === true;
30911                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30912                 if(this.useSplitTips){
30913                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30914                 }
30915                 if(config.collapsible){
30916                     this.split.el.on("dblclick", this.collapse,  this);
30917                 }
30918             }
30919             if(typeof config.minSize != "undefined"){
30920                 this.split.minSize = config.minSize;
30921             }
30922             if(typeof config.maxSize != "undefined"){
30923                 this.split.maxSize = config.maxSize;
30924             }
30925             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30926                 this.hideSplitter();
30927             }
30928         }
30929     },
30930
30931     getHMaxSize : function(){
30932          var cmax = this.config.maxSize || 10000;
30933          var center = this.mgr.getRegion("center");
30934          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30935     },
30936
30937     getVMaxSize : function(){
30938          var cmax = this.config.maxSize || 10000;
30939          var center = this.mgr.getRegion("center");
30940          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30941     },
30942
30943     onSplitMove : function(split, newSize){
30944         this.fireEvent("resized", this, newSize);
30945     },
30946     
30947     /** 
30948      * Returns the {@link Roo.SplitBar} for this region.
30949      * @return {Roo.SplitBar}
30950      */
30951     getSplitBar : function(){
30952         return this.split;
30953     },
30954     
30955     hide : function(){
30956         this.hideSplitter();
30957         Roo.SplitLayoutRegion.superclass.hide.call(this);
30958     },
30959
30960     hideSplitter : function(){
30961         if(this.split){
30962             this.split.el.setLocation(-2000,-2000);
30963             this.split.el.hide();
30964         }
30965     },
30966
30967     show : function(){
30968         if(this.split){
30969             this.split.el.show();
30970         }
30971         Roo.SplitLayoutRegion.superclass.show.call(this);
30972     },
30973     
30974     beforeSlide: function(){
30975         if(Roo.isGecko){// firefox overflow auto bug workaround
30976             this.bodyEl.clip();
30977             if(this.tabs) {
30978                 this.tabs.bodyEl.clip();
30979             }
30980             if(this.activePanel){
30981                 this.activePanel.getEl().clip();
30982                 
30983                 if(this.activePanel.beforeSlide){
30984                     this.activePanel.beforeSlide();
30985                 }
30986             }
30987         }
30988     },
30989     
30990     afterSlide : function(){
30991         if(Roo.isGecko){// firefox overflow auto bug workaround
30992             this.bodyEl.unclip();
30993             if(this.tabs) {
30994                 this.tabs.bodyEl.unclip();
30995             }
30996             if(this.activePanel){
30997                 this.activePanel.getEl().unclip();
30998                 if(this.activePanel.afterSlide){
30999                     this.activePanel.afterSlide();
31000                 }
31001             }
31002         }
31003     },
31004
31005     initAutoHide : function(){
31006         if(this.autoHide !== false){
31007             if(!this.autoHideHd){
31008                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31009                 this.autoHideHd = {
31010                     "mouseout": function(e){
31011                         if(!e.within(this.el, true)){
31012                             st.delay(500);
31013                         }
31014                     },
31015                     "mouseover" : function(e){
31016                         st.cancel();
31017                     },
31018                     scope : this
31019                 };
31020             }
31021             this.el.on(this.autoHideHd);
31022         }
31023     },
31024
31025     clearAutoHide : function(){
31026         if(this.autoHide !== false){
31027             this.el.un("mouseout", this.autoHideHd.mouseout);
31028             this.el.un("mouseover", this.autoHideHd.mouseover);
31029         }
31030     },
31031
31032     clearMonitor : function(){
31033         Roo.get(document).un("click", this.slideInIf, this);
31034     },
31035
31036     // these names are backwards but not changed for compat
31037     slideOut : function(){
31038         if(this.isSlid || this.el.hasActiveFx()){
31039             return;
31040         }
31041         this.isSlid = true;
31042         if(this.collapseBtn){
31043             this.collapseBtn.hide();
31044         }
31045         this.closeBtnState = this.closeBtn.getStyle('display');
31046         this.closeBtn.hide();
31047         if(this.stickBtn){
31048             this.stickBtn.show();
31049         }
31050         this.el.show();
31051         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31052         this.beforeSlide();
31053         this.el.setStyle("z-index", 10001);
31054         this.el.slideIn(this.getSlideAnchor(), {
31055             callback: function(){
31056                 this.afterSlide();
31057                 this.initAutoHide();
31058                 Roo.get(document).on("click", this.slideInIf, this);
31059                 this.fireEvent("slideshow", this);
31060             },
31061             scope: this,
31062             block: true
31063         });
31064     },
31065
31066     afterSlideIn : function(){
31067         this.clearAutoHide();
31068         this.isSlid = false;
31069         this.clearMonitor();
31070         this.el.setStyle("z-index", "");
31071         if(this.collapseBtn){
31072             this.collapseBtn.show();
31073         }
31074         this.closeBtn.setStyle('display', this.closeBtnState);
31075         if(this.stickBtn){
31076             this.stickBtn.hide();
31077         }
31078         this.fireEvent("slidehide", this);
31079     },
31080
31081     slideIn : function(cb){
31082         if(!this.isSlid || this.el.hasActiveFx()){
31083             Roo.callback(cb);
31084             return;
31085         }
31086         this.isSlid = false;
31087         this.beforeSlide();
31088         this.el.slideOut(this.getSlideAnchor(), {
31089             callback: function(){
31090                 this.el.setLeftTop(-10000, -10000);
31091                 this.afterSlide();
31092                 this.afterSlideIn();
31093                 Roo.callback(cb);
31094             },
31095             scope: this,
31096             block: true
31097         });
31098     },
31099     
31100     slideInIf : function(e){
31101         if(!e.within(this.el)){
31102             this.slideIn();
31103         }
31104     },
31105
31106     animateCollapse : function(){
31107         this.beforeSlide();
31108         this.el.setStyle("z-index", 20000);
31109         var anchor = this.getSlideAnchor();
31110         this.el.slideOut(anchor, {
31111             callback : function(){
31112                 this.el.setStyle("z-index", "");
31113                 this.collapsedEl.slideIn(anchor, {duration:.3});
31114                 this.afterSlide();
31115                 this.el.setLocation(-10000,-10000);
31116                 this.el.hide();
31117                 this.fireEvent("collapsed", this);
31118             },
31119             scope: this,
31120             block: true
31121         });
31122     },
31123
31124     animateExpand : function(){
31125         this.beforeSlide();
31126         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31127         this.el.setStyle("z-index", 20000);
31128         this.collapsedEl.hide({
31129             duration:.1
31130         });
31131         this.el.slideIn(this.getSlideAnchor(), {
31132             callback : function(){
31133                 this.el.setStyle("z-index", "");
31134                 this.afterSlide();
31135                 if(this.split){
31136                     this.split.el.show();
31137                 }
31138                 this.fireEvent("invalidated", this);
31139                 this.fireEvent("expanded", this);
31140             },
31141             scope: this,
31142             block: true
31143         });
31144     },
31145
31146     anchors : {
31147         "west" : "left",
31148         "east" : "right",
31149         "north" : "top",
31150         "south" : "bottom"
31151     },
31152
31153     sanchors : {
31154         "west" : "l",
31155         "east" : "r",
31156         "north" : "t",
31157         "south" : "b"
31158     },
31159
31160     canchors : {
31161         "west" : "tl-tr",
31162         "east" : "tr-tl",
31163         "north" : "tl-bl",
31164         "south" : "bl-tl"
31165     },
31166
31167     getAnchor : function(){
31168         return this.anchors[this.position];
31169     },
31170
31171     getCollapseAnchor : function(){
31172         return this.canchors[this.position];
31173     },
31174
31175     getSlideAnchor : function(){
31176         return this.sanchors[this.position];
31177     },
31178
31179     getAlignAdj : function(){
31180         var cm = this.cmargins;
31181         switch(this.position){
31182             case "west":
31183                 return [0, 0];
31184             break;
31185             case "east":
31186                 return [0, 0];
31187             break;
31188             case "north":
31189                 return [0, 0];
31190             break;
31191             case "south":
31192                 return [0, 0];
31193             break;
31194         }
31195     },
31196
31197     getExpandAdj : function(){
31198         var c = this.collapsedEl, cm = this.cmargins;
31199         switch(this.position){
31200             case "west":
31201                 return [-(cm.right+c.getWidth()+cm.left), 0];
31202             break;
31203             case "east":
31204                 return [cm.right+c.getWidth()+cm.left, 0];
31205             break;
31206             case "north":
31207                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31208             break;
31209             case "south":
31210                 return [0, cm.top+cm.bottom+c.getHeight()];
31211             break;
31212         }
31213     }
31214 });/*
31215  * Based on:
31216  * Ext JS Library 1.1.1
31217  * Copyright(c) 2006-2007, Ext JS, LLC.
31218  *
31219  * Originally Released Under LGPL - original licence link has changed is not relivant.
31220  *
31221  * Fork - LGPL
31222  * <script type="text/javascript">
31223  */
31224 /*
31225  * These classes are private internal classes
31226  */
31227 Roo.CenterLayoutRegion = function(mgr, config){
31228     Roo.LayoutRegion.call(this, mgr, config, "center");
31229     this.visible = true;
31230     this.minWidth = config.minWidth || 20;
31231     this.minHeight = config.minHeight || 20;
31232 };
31233
31234 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31235     hide : function(){
31236         // center panel can't be hidden
31237     },
31238     
31239     show : function(){
31240         // center panel can't be hidden
31241     },
31242     
31243     getMinWidth: function(){
31244         return this.minWidth;
31245     },
31246     
31247     getMinHeight: function(){
31248         return this.minHeight;
31249     }
31250 });
31251
31252
31253 Roo.NorthLayoutRegion = function(mgr, config){
31254     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31255     if(this.split){
31256         this.split.placement = Roo.SplitBar.TOP;
31257         this.split.orientation = Roo.SplitBar.VERTICAL;
31258         this.split.el.addClass("x-layout-split-v");
31259     }
31260     var size = config.initialSize || config.height;
31261     if(typeof size != "undefined"){
31262         this.el.setHeight(size);
31263     }
31264 };
31265 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31266     orientation: Roo.SplitBar.VERTICAL,
31267     getBox : function(){
31268         if(this.collapsed){
31269             return this.collapsedEl.getBox();
31270         }
31271         var box = this.el.getBox();
31272         if(this.split){
31273             box.height += this.split.el.getHeight();
31274         }
31275         return box;
31276     },
31277     
31278     updateBox : function(box){
31279         if(this.split && !this.collapsed){
31280             box.height -= this.split.el.getHeight();
31281             this.split.el.setLeft(box.x);
31282             this.split.el.setTop(box.y+box.height);
31283             this.split.el.setWidth(box.width);
31284         }
31285         if(this.collapsed){
31286             this.updateBody(box.width, null);
31287         }
31288         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31289     }
31290 });
31291
31292 Roo.SouthLayoutRegion = function(mgr, config){
31293     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31294     if(this.split){
31295         this.split.placement = Roo.SplitBar.BOTTOM;
31296         this.split.orientation = Roo.SplitBar.VERTICAL;
31297         this.split.el.addClass("x-layout-split-v");
31298     }
31299     var size = config.initialSize || config.height;
31300     if(typeof size != "undefined"){
31301         this.el.setHeight(size);
31302     }
31303 };
31304 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31305     orientation: Roo.SplitBar.VERTICAL,
31306     getBox : function(){
31307         if(this.collapsed){
31308             return this.collapsedEl.getBox();
31309         }
31310         var box = this.el.getBox();
31311         if(this.split){
31312             var sh = this.split.el.getHeight();
31313             box.height += sh;
31314             box.y -= sh;
31315         }
31316         return box;
31317     },
31318     
31319     updateBox : function(box){
31320         if(this.split && !this.collapsed){
31321             var sh = this.split.el.getHeight();
31322             box.height -= sh;
31323             box.y += sh;
31324             this.split.el.setLeft(box.x);
31325             this.split.el.setTop(box.y-sh);
31326             this.split.el.setWidth(box.width);
31327         }
31328         if(this.collapsed){
31329             this.updateBody(box.width, null);
31330         }
31331         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31332     }
31333 });
31334
31335 Roo.EastLayoutRegion = function(mgr, config){
31336     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31337     if(this.split){
31338         this.split.placement = Roo.SplitBar.RIGHT;
31339         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31340         this.split.el.addClass("x-layout-split-h");
31341     }
31342     var size = config.initialSize || config.width;
31343     if(typeof size != "undefined"){
31344         this.el.setWidth(size);
31345     }
31346 };
31347 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31348     orientation: Roo.SplitBar.HORIZONTAL,
31349     getBox : function(){
31350         if(this.collapsed){
31351             return this.collapsedEl.getBox();
31352         }
31353         var box = this.el.getBox();
31354         if(this.split){
31355             var sw = this.split.el.getWidth();
31356             box.width += sw;
31357             box.x -= sw;
31358         }
31359         return box;
31360     },
31361
31362     updateBox : function(box){
31363         if(this.split && !this.collapsed){
31364             var sw = this.split.el.getWidth();
31365             box.width -= sw;
31366             this.split.el.setLeft(box.x);
31367             this.split.el.setTop(box.y);
31368             this.split.el.setHeight(box.height);
31369             box.x += sw;
31370         }
31371         if(this.collapsed){
31372             this.updateBody(null, box.height);
31373         }
31374         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31375     }
31376 });
31377
31378 Roo.WestLayoutRegion = function(mgr, config){
31379     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31380     if(this.split){
31381         this.split.placement = Roo.SplitBar.LEFT;
31382         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31383         this.split.el.addClass("x-layout-split-h");
31384     }
31385     var size = config.initialSize || config.width;
31386     if(typeof size != "undefined"){
31387         this.el.setWidth(size);
31388     }
31389 };
31390 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31391     orientation: Roo.SplitBar.HORIZONTAL,
31392     getBox : function(){
31393         if(this.collapsed){
31394             return this.collapsedEl.getBox();
31395         }
31396         var box = this.el.getBox();
31397         if(this.split){
31398             box.width += this.split.el.getWidth();
31399         }
31400         return box;
31401     },
31402     
31403     updateBox : function(box){
31404         if(this.split && !this.collapsed){
31405             var sw = this.split.el.getWidth();
31406             box.width -= sw;
31407             this.split.el.setLeft(box.x+box.width);
31408             this.split.el.setTop(box.y);
31409             this.split.el.setHeight(box.height);
31410         }
31411         if(this.collapsed){
31412             this.updateBody(null, box.height);
31413         }
31414         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31415     }
31416 });
31417 /*
31418  * Based on:
31419  * Ext JS Library 1.1.1
31420  * Copyright(c) 2006-2007, Ext JS, LLC.
31421  *
31422  * Originally Released Under LGPL - original licence link has changed is not relivant.
31423  *
31424  * Fork - LGPL
31425  * <script type="text/javascript">
31426  */
31427  
31428  
31429 /*
31430  * Private internal class for reading and applying state
31431  */
31432 Roo.LayoutStateManager = function(layout){
31433      // default empty state
31434      this.state = {
31435         north: {},
31436         south: {},
31437         east: {},
31438         west: {}       
31439     };
31440 };
31441
31442 Roo.LayoutStateManager.prototype = {
31443     init : function(layout, provider){
31444         this.provider = provider;
31445         var state = provider.get(layout.id+"-layout-state");
31446         if(state){
31447             var wasUpdating = layout.isUpdating();
31448             if(!wasUpdating){
31449                 layout.beginUpdate();
31450             }
31451             for(var key in state){
31452                 if(typeof state[key] != "function"){
31453                     var rstate = state[key];
31454                     var r = layout.getRegion(key);
31455                     if(r && rstate){
31456                         if(rstate.size){
31457                             r.resizeTo(rstate.size);
31458                         }
31459                         if(rstate.collapsed == true){
31460                             r.collapse(true);
31461                         }else{
31462                             r.expand(null, true);
31463                         }
31464                     }
31465                 }
31466             }
31467             if(!wasUpdating){
31468                 layout.endUpdate();
31469             }
31470             this.state = state; 
31471         }
31472         this.layout = layout;
31473         layout.on("regionresized", this.onRegionResized, this);
31474         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31475         layout.on("regionexpanded", this.onRegionExpanded, this);
31476     },
31477     
31478     storeState : function(){
31479         this.provider.set(this.layout.id+"-layout-state", this.state);
31480     },
31481     
31482     onRegionResized : function(region, newSize){
31483         this.state[region.getPosition()].size = newSize;
31484         this.storeState();
31485     },
31486     
31487     onRegionCollapsed : function(region){
31488         this.state[region.getPosition()].collapsed = true;
31489         this.storeState();
31490     },
31491     
31492     onRegionExpanded : function(region){
31493         this.state[region.getPosition()].collapsed = false;
31494         this.storeState();
31495     }
31496 };/*
31497  * Based on:
31498  * Ext JS Library 1.1.1
31499  * Copyright(c) 2006-2007, Ext JS, LLC.
31500  *
31501  * Originally Released Under LGPL - original licence link has changed is not relivant.
31502  *
31503  * Fork - LGPL
31504  * <script type="text/javascript">
31505  */
31506 /**
31507  * @class Roo.ContentPanel
31508  * @extends Roo.util.Observable
31509  * A basic ContentPanel element.
31510  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31511  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31512  * @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
31513  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31514  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31515  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31516  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31517  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31518  * @cfg {String} title          The title for this panel
31519  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31520  * @cfg {String} url            Calls {@link #setUrl} with this value
31521  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31522  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31523  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31524  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31525
31526  * @constructor
31527  * Create a new ContentPanel.
31528  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31529  * @param {String/Object} config A string to set only the title or a config object
31530  * @param {String} content (optional) Set the HTML content for this panel
31531  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31532  */
31533 Roo.ContentPanel = function(el, config, content){
31534     
31535      
31536     /*
31537     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31538         config = el;
31539         el = Roo.id();
31540     }
31541     if (config && config.parentLayout) { 
31542         el = config.parentLayout.el.createChild(); 
31543     }
31544     */
31545     if(el.autoCreate){ // xtype is available if this is called from factory
31546         config = el;
31547         el = Roo.id();
31548     }
31549     this.el = Roo.get(el);
31550     if(!this.el && config && config.autoCreate){
31551         if(typeof config.autoCreate == "object"){
31552             if(!config.autoCreate.id){
31553                 config.autoCreate.id = config.id||el;
31554             }
31555             this.el = Roo.DomHelper.append(document.body,
31556                         config.autoCreate, true);
31557         }else{
31558             this.el = Roo.DomHelper.append(document.body,
31559                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31560         }
31561     }
31562     this.closable = false;
31563     this.loaded = false;
31564     this.active = false;
31565     if(typeof config == "string"){
31566         this.title = config;
31567     }else{
31568         Roo.apply(this, config);
31569     }
31570     
31571     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31572         this.wrapEl = this.el.wrap();
31573         this.toolbar.container = this.el.insertSibling(false, 'before');
31574         this.toolbar = new Roo.Toolbar(this.toolbar);
31575     }
31576     
31577     // xtype created footer. - not sure if will work as we normally have to render first..
31578     if (this.footer && !this.footer.el && this.footer.xtype) {
31579         if (!this.wrapEl) {
31580             this.wrapEl = this.el.wrap();
31581         }
31582     
31583         this.footer.container = this.wrapEl.createChild();
31584          
31585         this.footer = Roo.factory(this.footer, Roo);
31586         
31587     }
31588     
31589     if(this.resizeEl){
31590         this.resizeEl = Roo.get(this.resizeEl, true);
31591     }else{
31592         this.resizeEl = this.el;
31593     }
31594     // handle view.xtype
31595     
31596  
31597     
31598     
31599     this.addEvents({
31600         /**
31601          * @event activate
31602          * Fires when this panel is activated. 
31603          * @param {Roo.ContentPanel} this
31604          */
31605         "activate" : true,
31606         /**
31607          * @event deactivate
31608          * Fires when this panel is activated. 
31609          * @param {Roo.ContentPanel} this
31610          */
31611         "deactivate" : true,
31612
31613         /**
31614          * @event resize
31615          * Fires when this panel is resized if fitToFrame is true.
31616          * @param {Roo.ContentPanel} this
31617          * @param {Number} width The width after any component adjustments
31618          * @param {Number} height The height after any component adjustments
31619          */
31620         "resize" : true,
31621         
31622          /**
31623          * @event render
31624          * Fires when this tab is created
31625          * @param {Roo.ContentPanel} this
31626          */
31627         "render" : true
31628          
31629         
31630     });
31631     
31632
31633     
31634     
31635     if(this.autoScroll){
31636         this.resizeEl.setStyle("overflow", "auto");
31637     } else {
31638         // fix randome scrolling
31639         this.el.on('scroll', function() {
31640             Roo.log('fix random scolling');
31641             this.scrollTo('top',0); 
31642         });
31643     }
31644     content = content || this.content;
31645     if(content){
31646         this.setContent(content);
31647     }
31648     if(config && config.url){
31649         this.setUrl(this.url, this.params, this.loadOnce);
31650     }
31651     
31652     
31653     
31654     Roo.ContentPanel.superclass.constructor.call(this);
31655     
31656     if (this.view && typeof(this.view.xtype) != 'undefined') {
31657         this.view.el = this.el.appendChild(document.createElement("div"));
31658         this.view = Roo.factory(this.view); 
31659         this.view.render  &&  this.view.render(false, '');  
31660     }
31661     
31662     
31663     this.fireEvent('render', this);
31664 };
31665
31666 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31667     tabTip:'',
31668     setRegion : function(region){
31669         this.region = region;
31670         if(region){
31671            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31672         }else{
31673            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31674         } 
31675     },
31676     
31677     /**
31678      * Returns the toolbar for this Panel if one was configured. 
31679      * @return {Roo.Toolbar} 
31680      */
31681     getToolbar : function(){
31682         return this.toolbar;
31683     },
31684     
31685     setActiveState : function(active){
31686         this.active = active;
31687         if(!active){
31688             this.fireEvent("deactivate", this);
31689         }else{
31690             this.fireEvent("activate", this);
31691         }
31692     },
31693     /**
31694      * Updates this panel's element
31695      * @param {String} content The new content
31696      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31697     */
31698     setContent : function(content, loadScripts){
31699         this.el.update(content, loadScripts);
31700     },
31701
31702     ignoreResize : function(w, h){
31703         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31704             return true;
31705         }else{
31706             this.lastSize = {width: w, height: h};
31707             return false;
31708         }
31709     },
31710     /**
31711      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31712      * @return {Roo.UpdateManager} The UpdateManager
31713      */
31714     getUpdateManager : function(){
31715         return this.el.getUpdateManager();
31716     },
31717      /**
31718      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31719      * @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:
31720 <pre><code>
31721 panel.load({
31722     url: "your-url.php",
31723     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31724     callback: yourFunction,
31725     scope: yourObject, //(optional scope)
31726     discardUrl: false,
31727     nocache: false,
31728     text: "Loading...",
31729     timeout: 30,
31730     scripts: false
31731 });
31732 </code></pre>
31733      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31734      * 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.
31735      * @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}
31736      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31737      * @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.
31738      * @return {Roo.ContentPanel} this
31739      */
31740     load : function(){
31741         var um = this.el.getUpdateManager();
31742         um.update.apply(um, arguments);
31743         return this;
31744     },
31745
31746
31747     /**
31748      * 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.
31749      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31750      * @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)
31751      * @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)
31752      * @return {Roo.UpdateManager} The UpdateManager
31753      */
31754     setUrl : function(url, params, loadOnce){
31755         if(this.refreshDelegate){
31756             this.removeListener("activate", this.refreshDelegate);
31757         }
31758         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31759         this.on("activate", this.refreshDelegate);
31760         return this.el.getUpdateManager();
31761     },
31762     
31763     _handleRefresh : function(url, params, loadOnce){
31764         if(!loadOnce || !this.loaded){
31765             var updater = this.el.getUpdateManager();
31766             updater.update(url, params, this._setLoaded.createDelegate(this));
31767         }
31768     },
31769     
31770     _setLoaded : function(){
31771         this.loaded = true;
31772     }, 
31773     
31774     /**
31775      * Returns this panel's id
31776      * @return {String} 
31777      */
31778     getId : function(){
31779         return this.el.id;
31780     },
31781     
31782     /** 
31783      * Returns this panel's element - used by regiosn to add.
31784      * @return {Roo.Element} 
31785      */
31786     getEl : function(){
31787         return this.wrapEl || this.el;
31788     },
31789     
31790     adjustForComponents : function(width, height)
31791     {
31792         //Roo.log('adjustForComponents ');
31793         if(this.resizeEl != this.el){
31794             width -= this.el.getFrameWidth('lr');
31795             height -= this.el.getFrameWidth('tb');
31796         }
31797         if(this.toolbar){
31798             var te = this.toolbar.getEl();
31799             height -= te.getHeight();
31800             te.setWidth(width);
31801         }
31802         if(this.footer){
31803             var te = this.footer.getEl();
31804             //Roo.log("footer:" + te.getHeight());
31805             
31806             height -= te.getHeight();
31807             te.setWidth(width);
31808         }
31809         
31810         
31811         if(this.adjustments){
31812             width += this.adjustments[0];
31813             height += this.adjustments[1];
31814         }
31815         return {"width": width, "height": height};
31816     },
31817     
31818     setSize : function(width, height){
31819         if(this.fitToFrame && !this.ignoreResize(width, height)){
31820             if(this.fitContainer && this.resizeEl != this.el){
31821                 this.el.setSize(width, height);
31822             }
31823             var size = this.adjustForComponents(width, height);
31824             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31825             this.fireEvent('resize', this, size.width, size.height);
31826         }
31827     },
31828     
31829     /**
31830      * Returns this panel's title
31831      * @return {String} 
31832      */
31833     getTitle : function(){
31834         return this.title;
31835     },
31836     
31837     /**
31838      * Set this panel's title
31839      * @param {String} title
31840      */
31841     setTitle : function(title){
31842         this.title = title;
31843         if(this.region){
31844             this.region.updatePanelTitle(this, title);
31845         }
31846     },
31847     
31848     /**
31849      * Returns true is this panel was configured to be closable
31850      * @return {Boolean} 
31851      */
31852     isClosable : function(){
31853         return this.closable;
31854     },
31855     
31856     beforeSlide : function(){
31857         this.el.clip();
31858         this.resizeEl.clip();
31859     },
31860     
31861     afterSlide : function(){
31862         this.el.unclip();
31863         this.resizeEl.unclip();
31864     },
31865     
31866     /**
31867      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31868      *   Will fail silently if the {@link #setUrl} method has not been called.
31869      *   This does not activate the panel, just updates its content.
31870      */
31871     refresh : function(){
31872         if(this.refreshDelegate){
31873            this.loaded = false;
31874            this.refreshDelegate();
31875         }
31876     },
31877     
31878     /**
31879      * Destroys this panel
31880      */
31881     destroy : function(){
31882         this.el.removeAllListeners();
31883         var tempEl = document.createElement("span");
31884         tempEl.appendChild(this.el.dom);
31885         tempEl.innerHTML = "";
31886         this.el.remove();
31887         this.el = null;
31888     },
31889     
31890     /**
31891      * form - if the content panel contains a form - this is a reference to it.
31892      * @type {Roo.form.Form}
31893      */
31894     form : false,
31895     /**
31896      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31897      *    This contains a reference to it.
31898      * @type {Roo.View}
31899      */
31900     view : false,
31901     
31902       /**
31903      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31904      * <pre><code>
31905
31906 layout.addxtype({
31907        xtype : 'Form',
31908        items: [ .... ]
31909    }
31910 );
31911
31912 </code></pre>
31913      * @param {Object} cfg Xtype definition of item to add.
31914      */
31915     
31916     addxtype : function(cfg) {
31917         // add form..
31918         if (cfg.xtype.match(/^Form$/)) {
31919             
31920             var el;
31921             //if (this.footer) {
31922             //    el = this.footer.container.insertSibling(false, 'before');
31923             //} else {
31924                 el = this.el.createChild();
31925             //}
31926
31927             this.form = new  Roo.form.Form(cfg);
31928             
31929             
31930             if ( this.form.allItems.length) {
31931                 this.form.render(el.dom);
31932             }
31933             return this.form;
31934         }
31935         // should only have one of theses..
31936         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31937             // views.. should not be just added - used named prop 'view''
31938             
31939             cfg.el = this.el.appendChild(document.createElement("div"));
31940             // factory?
31941             
31942             var ret = new Roo.factory(cfg);
31943              
31944              ret.render && ret.render(false, ''); // render blank..
31945             this.view = ret;
31946             return ret;
31947         }
31948         return false;
31949     }
31950 });
31951
31952 /**
31953  * @class Roo.GridPanel
31954  * @extends Roo.ContentPanel
31955  * @constructor
31956  * Create a new GridPanel.
31957  * @param {Roo.grid.Grid} grid The grid for this panel
31958  * @param {String/Object} config A string to set only the panel's title, or a config object
31959  */
31960 Roo.GridPanel = function(grid, config){
31961     
31962   
31963     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31964         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31965         
31966     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31967     
31968     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31969     
31970     if(this.toolbar){
31971         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31972     }
31973     // xtype created footer. - not sure if will work as we normally have to render first..
31974     if (this.footer && !this.footer.el && this.footer.xtype) {
31975         
31976         this.footer.container = this.grid.getView().getFooterPanel(true);
31977         this.footer.dataSource = this.grid.dataSource;
31978         this.footer = Roo.factory(this.footer, Roo);
31979         
31980     }
31981     
31982     grid.monitorWindowResize = false; // turn off autosizing
31983     grid.autoHeight = false;
31984     grid.autoWidth = false;
31985     this.grid = grid;
31986     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31987 };
31988
31989 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31990     getId : function(){
31991         return this.grid.id;
31992     },
31993     
31994     /**
31995      * Returns the grid for this panel
31996      * @return {Roo.grid.Grid} 
31997      */
31998     getGrid : function(){
31999         return this.grid;    
32000     },
32001     
32002     setSize : function(width, height){
32003         if(!this.ignoreResize(width, height)){
32004             var grid = this.grid;
32005             var size = this.adjustForComponents(width, height);
32006             grid.getGridEl().setSize(size.width, size.height);
32007             grid.autoSize();
32008         }
32009     },
32010     
32011     beforeSlide : function(){
32012         this.grid.getView().scroller.clip();
32013     },
32014     
32015     afterSlide : function(){
32016         this.grid.getView().scroller.unclip();
32017     },
32018     
32019     destroy : function(){
32020         this.grid.destroy();
32021         delete this.grid;
32022         Roo.GridPanel.superclass.destroy.call(this); 
32023     }
32024 });
32025
32026
32027 /**
32028  * @class Roo.NestedLayoutPanel
32029  * @extends Roo.ContentPanel
32030  * @constructor
32031  * Create a new NestedLayoutPanel.
32032  * 
32033  * 
32034  * @param {Roo.BorderLayout} layout The layout for this panel
32035  * @param {String/Object} config A string to set only the title or a config object
32036  */
32037 Roo.NestedLayoutPanel = function(layout, config)
32038 {
32039     // construct with only one argument..
32040     /* FIXME - implement nicer consturctors
32041     if (layout.layout) {
32042         config = layout;
32043         layout = config.layout;
32044         delete config.layout;
32045     }
32046     if (layout.xtype && !layout.getEl) {
32047         // then layout needs constructing..
32048         layout = Roo.factory(layout, Roo);
32049     }
32050     */
32051     
32052     
32053     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32054     
32055     layout.monitorWindowResize = false; // turn off autosizing
32056     this.layout = layout;
32057     this.layout.getEl().addClass("x-layout-nested-layout");
32058     
32059     
32060     
32061     
32062 };
32063
32064 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32065
32066     setSize : function(width, height){
32067         if(!this.ignoreResize(width, height)){
32068             var size = this.adjustForComponents(width, height);
32069             var el = this.layout.getEl();
32070             el.setSize(size.width, size.height);
32071             var touch = el.dom.offsetWidth;
32072             this.layout.layout();
32073             // ie requires a double layout on the first pass
32074             if(Roo.isIE && !this.initialized){
32075                 this.initialized = true;
32076                 this.layout.layout();
32077             }
32078         }
32079     },
32080     
32081     // activate all subpanels if not currently active..
32082     
32083     setActiveState : function(active){
32084         this.active = active;
32085         if(!active){
32086             this.fireEvent("deactivate", this);
32087             return;
32088         }
32089         
32090         this.fireEvent("activate", this);
32091         // not sure if this should happen before or after..
32092         if (!this.layout) {
32093             return; // should not happen..
32094         }
32095         var reg = false;
32096         for (var r in this.layout.regions) {
32097             reg = this.layout.getRegion(r);
32098             if (reg.getActivePanel()) {
32099                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32100                 reg.setActivePanel(reg.getActivePanel());
32101                 continue;
32102             }
32103             if (!reg.panels.length) {
32104                 continue;
32105             }
32106             reg.showPanel(reg.getPanel(0));
32107         }
32108         
32109         
32110         
32111         
32112     },
32113     
32114     /**
32115      * Returns the nested BorderLayout for this panel
32116      * @return {Roo.BorderLayout} 
32117      */
32118     getLayout : function(){
32119         return this.layout;
32120     },
32121     
32122      /**
32123      * Adds a xtype elements to the layout of the nested panel
32124      * <pre><code>
32125
32126 panel.addxtype({
32127        xtype : 'ContentPanel',
32128        region: 'west',
32129        items: [ .... ]
32130    }
32131 );
32132
32133 panel.addxtype({
32134         xtype : 'NestedLayoutPanel',
32135         region: 'west',
32136         layout: {
32137            center: { },
32138            west: { }   
32139         },
32140         items : [ ... list of content panels or nested layout panels.. ]
32141    }
32142 );
32143 </code></pre>
32144      * @param {Object} cfg Xtype definition of item to add.
32145      */
32146     addxtype : function(cfg) {
32147         return this.layout.addxtype(cfg);
32148     
32149     }
32150 });
32151
32152 Roo.ScrollPanel = function(el, config, content){
32153     config = config || {};
32154     config.fitToFrame = true;
32155     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32156     
32157     this.el.dom.style.overflow = "hidden";
32158     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32159     this.el.removeClass("x-layout-inactive-content");
32160     this.el.on("mousewheel", this.onWheel, this);
32161
32162     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32163     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32164     up.unselectable(); down.unselectable();
32165     up.on("click", this.scrollUp, this);
32166     down.on("click", this.scrollDown, this);
32167     up.addClassOnOver("x-scroller-btn-over");
32168     down.addClassOnOver("x-scroller-btn-over");
32169     up.addClassOnClick("x-scroller-btn-click");
32170     down.addClassOnClick("x-scroller-btn-click");
32171     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32172
32173     this.resizeEl = this.el;
32174     this.el = wrap; this.up = up; this.down = down;
32175 };
32176
32177 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32178     increment : 100,
32179     wheelIncrement : 5,
32180     scrollUp : function(){
32181         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32182     },
32183
32184     scrollDown : function(){
32185         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32186     },
32187
32188     afterScroll : function(){
32189         var el = this.resizeEl;
32190         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32191         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32192         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32193     },
32194
32195     setSize : function(){
32196         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32197         this.afterScroll();
32198     },
32199
32200     onWheel : function(e){
32201         var d = e.getWheelDelta();
32202         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32203         this.afterScroll();
32204         e.stopEvent();
32205     },
32206
32207     setContent : function(content, loadScripts){
32208         this.resizeEl.update(content, loadScripts);
32209     }
32210
32211 });
32212
32213
32214
32215
32216
32217
32218
32219
32220
32221 /**
32222  * @class Roo.TreePanel
32223  * @extends Roo.ContentPanel
32224  * @constructor
32225  * Create a new TreePanel. - defaults to fit/scoll contents.
32226  * @param {String/Object} config A string to set only the panel's title, or a config object
32227  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32228  */
32229 Roo.TreePanel = function(config){
32230     var el = config.el;
32231     var tree = config.tree;
32232     delete config.tree; 
32233     delete config.el; // hopefull!
32234     
32235     // wrapper for IE7 strict & safari scroll issue
32236     
32237     var treeEl = el.createChild();
32238     config.resizeEl = treeEl;
32239     
32240     
32241     
32242     Roo.TreePanel.superclass.constructor.call(this, el, config);
32243  
32244  
32245     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32246     //console.log(tree);
32247     this.on('activate', function()
32248     {
32249         if (this.tree.rendered) {
32250             return;
32251         }
32252         //console.log('render tree');
32253         this.tree.render();
32254     });
32255     // this should not be needed.. - it's actually the 'el' that resizes?
32256     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32257     
32258     //this.on('resize',  function (cp, w, h) {
32259     //        this.tree.innerCt.setWidth(w);
32260     //        this.tree.innerCt.setHeight(h);
32261     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32262     //});
32263
32264         
32265     
32266 };
32267
32268 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32269     fitToFrame : true,
32270     autoScroll : true
32271 });
32272
32273
32274
32275
32276
32277
32278
32279
32280
32281
32282
32283 /*
32284  * Based on:
32285  * Ext JS Library 1.1.1
32286  * Copyright(c) 2006-2007, Ext JS, LLC.
32287  *
32288  * Originally Released Under LGPL - original licence link has changed is not relivant.
32289  *
32290  * Fork - LGPL
32291  * <script type="text/javascript">
32292  */
32293  
32294
32295 /**
32296  * @class Roo.ReaderLayout
32297  * @extends Roo.BorderLayout
32298  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32299  * center region containing two nested regions (a top one for a list view and one for item preview below),
32300  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32301  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32302  * expedites the setup of the overall layout and regions for this common application style.
32303  * Example:
32304  <pre><code>
32305 var reader = new Roo.ReaderLayout();
32306 var CP = Roo.ContentPanel;  // shortcut for adding
32307
32308 reader.beginUpdate();
32309 reader.add("north", new CP("north", "North"));
32310 reader.add("west", new CP("west", {title: "West"}));
32311 reader.add("east", new CP("east", {title: "East"}));
32312
32313 reader.regions.listView.add(new CP("listView", "List"));
32314 reader.regions.preview.add(new CP("preview", "Preview"));
32315 reader.endUpdate();
32316 </code></pre>
32317 * @constructor
32318 * Create a new ReaderLayout
32319 * @param {Object} config Configuration options
32320 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32321 * document.body if omitted)
32322 */
32323 Roo.ReaderLayout = function(config, renderTo){
32324     var c = config || {size:{}};
32325     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32326         north: c.north !== false ? Roo.apply({
32327             split:false,
32328             initialSize: 32,
32329             titlebar: false
32330         }, c.north) : false,
32331         west: c.west !== false ? Roo.apply({
32332             split:true,
32333             initialSize: 200,
32334             minSize: 175,
32335             maxSize: 400,
32336             titlebar: true,
32337             collapsible: true,
32338             animate: true,
32339             margins:{left:5,right:0,bottom:5,top:5},
32340             cmargins:{left:5,right:5,bottom:5,top:5}
32341         }, c.west) : false,
32342         east: c.east !== false ? Roo.apply({
32343             split:true,
32344             initialSize: 200,
32345             minSize: 175,
32346             maxSize: 400,
32347             titlebar: true,
32348             collapsible: true,
32349             animate: true,
32350             margins:{left:0,right:5,bottom:5,top:5},
32351             cmargins:{left:5,right:5,bottom:5,top:5}
32352         }, c.east) : false,
32353         center: Roo.apply({
32354             tabPosition: 'top',
32355             autoScroll:false,
32356             closeOnTab: true,
32357             titlebar:false,
32358             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32359         }, c.center)
32360     });
32361
32362     this.el.addClass('x-reader');
32363
32364     this.beginUpdate();
32365
32366     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32367         south: c.preview !== false ? Roo.apply({
32368             split:true,
32369             initialSize: 200,
32370             minSize: 100,
32371             autoScroll:true,
32372             collapsible:true,
32373             titlebar: true,
32374             cmargins:{top:5,left:0, right:0, bottom:0}
32375         }, c.preview) : false,
32376         center: Roo.apply({
32377             autoScroll:false,
32378             titlebar:false,
32379             minHeight:200
32380         }, c.listView)
32381     });
32382     this.add('center', new Roo.NestedLayoutPanel(inner,
32383             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32384
32385     this.endUpdate();
32386
32387     this.regions.preview = inner.getRegion('south');
32388     this.regions.listView = inner.getRegion('center');
32389 };
32390
32391 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32392  * Based on:
32393  * Ext JS Library 1.1.1
32394  * Copyright(c) 2006-2007, Ext JS, LLC.
32395  *
32396  * Originally Released Under LGPL - original licence link has changed is not relivant.
32397  *
32398  * Fork - LGPL
32399  * <script type="text/javascript">
32400  */
32401  
32402 /**
32403  * @class Roo.grid.Grid
32404  * @extends Roo.util.Observable
32405  * This class represents the primary interface of a component based grid control.
32406  * <br><br>Usage:<pre><code>
32407  var grid = new Roo.grid.Grid("my-container-id", {
32408      ds: myDataStore,
32409      cm: myColModel,
32410      selModel: mySelectionModel,
32411      autoSizeColumns: true,
32412      monitorWindowResize: false,
32413      trackMouseOver: true
32414  });
32415  // set any options
32416  grid.render();
32417  * </code></pre>
32418  * <b>Common Problems:</b><br/>
32419  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32420  * element will correct this<br/>
32421  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32422  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32423  * are unpredictable.<br/>
32424  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32425  * grid to calculate dimensions/offsets.<br/>
32426   * @constructor
32427  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32428  * The container MUST have some type of size defined for the grid to fill. The container will be
32429  * automatically set to position relative if it isn't already.
32430  * @param {Object} config A config object that sets properties on this grid.
32431  */
32432 Roo.grid.Grid = function(container, config){
32433         // initialize the container
32434         this.container = Roo.get(container);
32435         this.container.update("");
32436         this.container.setStyle("overflow", "hidden");
32437     this.container.addClass('x-grid-container');
32438
32439     this.id = this.container.id;
32440
32441     Roo.apply(this, config);
32442     // check and correct shorthanded configs
32443     if(this.ds){
32444         this.dataSource = this.ds;
32445         delete this.ds;
32446     }
32447     if(this.cm){
32448         this.colModel = this.cm;
32449         delete this.cm;
32450     }
32451     if(this.sm){
32452         this.selModel = this.sm;
32453         delete this.sm;
32454     }
32455
32456     if (this.selModel) {
32457         this.selModel = Roo.factory(this.selModel, Roo.grid);
32458         this.sm = this.selModel;
32459         this.sm.xmodule = this.xmodule || false;
32460     }
32461     if (typeof(this.colModel.config) == 'undefined') {
32462         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32463         this.cm = this.colModel;
32464         this.cm.xmodule = this.xmodule || false;
32465     }
32466     if (this.dataSource) {
32467         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32468         this.ds = this.dataSource;
32469         this.ds.xmodule = this.xmodule || false;
32470          
32471     }
32472     
32473     
32474     
32475     if(this.width){
32476         this.container.setWidth(this.width);
32477     }
32478
32479     if(this.height){
32480         this.container.setHeight(this.height);
32481     }
32482     /** @private */
32483         this.addEvents({
32484         // raw events
32485         /**
32486          * @event click
32487          * The raw click event for the entire grid.
32488          * @param {Roo.EventObject} e
32489          */
32490         "click" : true,
32491         /**
32492          * @event dblclick
32493          * The raw dblclick event for the entire grid.
32494          * @param {Roo.EventObject} e
32495          */
32496         "dblclick" : true,
32497         /**
32498          * @event contextmenu
32499          * The raw contextmenu event for the entire grid.
32500          * @param {Roo.EventObject} e
32501          */
32502         "contextmenu" : true,
32503         /**
32504          * @event mousedown
32505          * The raw mousedown event for the entire grid.
32506          * @param {Roo.EventObject} e
32507          */
32508         "mousedown" : true,
32509         /**
32510          * @event mouseup
32511          * The raw mouseup event for the entire grid.
32512          * @param {Roo.EventObject} e
32513          */
32514         "mouseup" : true,
32515         /**
32516          * @event mouseover
32517          * The raw mouseover event for the entire grid.
32518          * @param {Roo.EventObject} e
32519          */
32520         "mouseover" : true,
32521         /**
32522          * @event mouseout
32523          * The raw mouseout event for the entire grid.
32524          * @param {Roo.EventObject} e
32525          */
32526         "mouseout" : true,
32527         /**
32528          * @event keypress
32529          * The raw keypress event for the entire grid.
32530          * @param {Roo.EventObject} e
32531          */
32532         "keypress" : true,
32533         /**
32534          * @event keydown
32535          * The raw keydown event for the entire grid.
32536          * @param {Roo.EventObject} e
32537          */
32538         "keydown" : true,
32539
32540         // custom events
32541
32542         /**
32543          * @event cellclick
32544          * Fires when a cell is clicked
32545          * @param {Grid} this
32546          * @param {Number} rowIndex
32547          * @param {Number} columnIndex
32548          * @param {Roo.EventObject} e
32549          */
32550         "cellclick" : true,
32551         /**
32552          * @event celldblclick
32553          * Fires when a cell is double clicked
32554          * @param {Grid} this
32555          * @param {Number} rowIndex
32556          * @param {Number} columnIndex
32557          * @param {Roo.EventObject} e
32558          */
32559         "celldblclick" : true,
32560         /**
32561          * @event rowclick
32562          * Fires when a row is clicked
32563          * @param {Grid} this
32564          * @param {Number} rowIndex
32565          * @param {Roo.EventObject} e
32566          */
32567         "rowclick" : true,
32568         /**
32569          * @event rowdblclick
32570          * Fires when a row is double clicked
32571          * @param {Grid} this
32572          * @param {Number} rowIndex
32573          * @param {Roo.EventObject} e
32574          */
32575         "rowdblclick" : true,
32576         /**
32577          * @event headerclick
32578          * Fires when a header is clicked
32579          * @param {Grid} this
32580          * @param {Number} columnIndex
32581          * @param {Roo.EventObject} e
32582          */
32583         "headerclick" : true,
32584         /**
32585          * @event headerdblclick
32586          * Fires when a header cell is double clicked
32587          * @param {Grid} this
32588          * @param {Number} columnIndex
32589          * @param {Roo.EventObject} e
32590          */
32591         "headerdblclick" : true,
32592         /**
32593          * @event rowcontextmenu
32594          * Fires when a row is right clicked
32595          * @param {Grid} this
32596          * @param {Number} rowIndex
32597          * @param {Roo.EventObject} e
32598          */
32599         "rowcontextmenu" : true,
32600         /**
32601          * @event cellcontextmenu
32602          * Fires when a cell is right clicked
32603          * @param {Grid} this
32604          * @param {Number} rowIndex
32605          * @param {Number} cellIndex
32606          * @param {Roo.EventObject} e
32607          */
32608          "cellcontextmenu" : true,
32609         /**
32610          * @event headercontextmenu
32611          * Fires when a header is right clicked
32612          * @param {Grid} this
32613          * @param {Number} columnIndex
32614          * @param {Roo.EventObject} e
32615          */
32616         "headercontextmenu" : true,
32617         /**
32618          * @event bodyscroll
32619          * Fires when the body element is scrolled
32620          * @param {Number} scrollLeft
32621          * @param {Number} scrollTop
32622          */
32623         "bodyscroll" : true,
32624         /**
32625          * @event columnresize
32626          * Fires when the user resizes a column
32627          * @param {Number} columnIndex
32628          * @param {Number} newSize
32629          */
32630         "columnresize" : true,
32631         /**
32632          * @event columnmove
32633          * Fires when the user moves a column
32634          * @param {Number} oldIndex
32635          * @param {Number} newIndex
32636          */
32637         "columnmove" : true,
32638         /**
32639          * @event startdrag
32640          * Fires when row(s) start being dragged
32641          * @param {Grid} this
32642          * @param {Roo.GridDD} dd The drag drop object
32643          * @param {event} e The raw browser event
32644          */
32645         "startdrag" : true,
32646         /**
32647          * @event enddrag
32648          * Fires when a drag operation is complete
32649          * @param {Grid} this
32650          * @param {Roo.GridDD} dd The drag drop object
32651          * @param {event} e The raw browser event
32652          */
32653         "enddrag" : true,
32654         /**
32655          * @event dragdrop
32656          * Fires when dragged row(s) are dropped on a valid DD target
32657          * @param {Grid} this
32658          * @param {Roo.GridDD} dd The drag drop object
32659          * @param {String} targetId The target drag drop object
32660          * @param {event} e The raw browser event
32661          */
32662         "dragdrop" : true,
32663         /**
32664          * @event dragover
32665          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32666          * @param {Grid} this
32667          * @param {Roo.GridDD} dd The drag drop object
32668          * @param {String} targetId The target drag drop object
32669          * @param {event} e The raw browser event
32670          */
32671         "dragover" : true,
32672         /**
32673          * @event dragenter
32674          *  Fires when the dragged row(s) first cross another DD target while being dragged
32675          * @param {Grid} this
32676          * @param {Roo.GridDD} dd The drag drop object
32677          * @param {String} targetId The target drag drop object
32678          * @param {event} e The raw browser event
32679          */
32680         "dragenter" : true,
32681         /**
32682          * @event dragout
32683          * Fires when the dragged row(s) leave another DD target while being dragged
32684          * @param {Grid} this
32685          * @param {Roo.GridDD} dd The drag drop object
32686          * @param {String} targetId The target drag drop object
32687          * @param {event} e The raw browser event
32688          */
32689         "dragout" : true,
32690         /**
32691          * @event rowclass
32692          * Fires when a row is rendered, so you can change add a style to it.
32693          * @param {GridView} gridview   The grid view
32694          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32695          */
32696         'rowclass' : true,
32697
32698         /**
32699          * @event render
32700          * Fires when the grid is rendered
32701          * @param {Grid} grid
32702          */
32703         'render' : true
32704     });
32705
32706     Roo.grid.Grid.superclass.constructor.call(this);
32707 };
32708 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32709     
32710     /**
32711      * @cfg {String} ddGroup - drag drop group.
32712      */
32713
32714     /**
32715      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32716      */
32717     minColumnWidth : 25,
32718
32719     /**
32720      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32721      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32722      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32723      */
32724     autoSizeColumns : false,
32725
32726     /**
32727      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32728      */
32729     autoSizeHeaders : true,
32730
32731     /**
32732      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32733      */
32734     monitorWindowResize : true,
32735
32736     /**
32737      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32738      * rows measured to get a columns size. Default is 0 (all rows).
32739      */
32740     maxRowsToMeasure : 0,
32741
32742     /**
32743      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32744      */
32745     trackMouseOver : true,
32746
32747     /**
32748     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32749     */
32750     
32751     /**
32752     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32753     */
32754     enableDragDrop : false,
32755     
32756     /**
32757     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32758     */
32759     enableColumnMove : true,
32760     
32761     /**
32762     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32763     */
32764     enableColumnHide : true,
32765     
32766     /**
32767     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32768     */
32769     enableRowHeightSync : false,
32770     
32771     /**
32772     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32773     */
32774     stripeRows : true,
32775     
32776     /**
32777     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32778     */
32779     autoHeight : false,
32780
32781     /**
32782      * @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.
32783      */
32784     autoExpandColumn : false,
32785
32786     /**
32787     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32788     * Default is 50.
32789     */
32790     autoExpandMin : 50,
32791
32792     /**
32793     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32794     */
32795     autoExpandMax : 1000,
32796
32797     /**
32798     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32799     */
32800     view : null,
32801
32802     /**
32803     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32804     */
32805     loadMask : false,
32806     /**
32807     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32808     */
32809     dropTarget: false,
32810     
32811    
32812     
32813     // private
32814     rendered : false,
32815
32816     /**
32817     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32818     * of a fixed width. Default is false.
32819     */
32820     /**
32821     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32822     */
32823     /**
32824      * Called once after all setup has been completed and the grid is ready to be rendered.
32825      * @return {Roo.grid.Grid} this
32826      */
32827     render : function()
32828     {
32829         var c = this.container;
32830         // try to detect autoHeight/width mode
32831         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32832             this.autoHeight = true;
32833         }
32834         var view = this.getView();
32835         view.init(this);
32836
32837         c.on("click", this.onClick, this);
32838         c.on("dblclick", this.onDblClick, this);
32839         c.on("contextmenu", this.onContextMenu, this);
32840         c.on("keydown", this.onKeyDown, this);
32841         if (Roo.isTouch) {
32842             c.on("touchstart", this.onTouchStart, this);
32843         }
32844
32845         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32846
32847         this.getSelectionModel().init(this);
32848
32849         view.render();
32850
32851         if(this.loadMask){
32852             this.loadMask = new Roo.LoadMask(this.container,
32853                     Roo.apply({store:this.dataSource}, this.loadMask));
32854         }
32855         
32856         
32857         if (this.toolbar && this.toolbar.xtype) {
32858             this.toolbar.container = this.getView().getHeaderPanel(true);
32859             this.toolbar = new Roo.Toolbar(this.toolbar);
32860         }
32861         if (this.footer && this.footer.xtype) {
32862             this.footer.dataSource = this.getDataSource();
32863             this.footer.container = this.getView().getFooterPanel(true);
32864             this.footer = Roo.factory(this.footer, Roo);
32865         }
32866         if (this.dropTarget && this.dropTarget.xtype) {
32867             delete this.dropTarget.xtype;
32868             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32869         }
32870         
32871         
32872         this.rendered = true;
32873         this.fireEvent('render', this);
32874         return this;
32875     },
32876
32877         /**
32878          * Reconfigures the grid to use a different Store and Column Model.
32879          * The View will be bound to the new objects and refreshed.
32880          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32881          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32882          */
32883     reconfigure : function(dataSource, colModel){
32884         if(this.loadMask){
32885             this.loadMask.destroy();
32886             this.loadMask = new Roo.LoadMask(this.container,
32887                     Roo.apply({store:dataSource}, this.loadMask));
32888         }
32889         this.view.bind(dataSource, colModel);
32890         this.dataSource = dataSource;
32891         this.colModel = colModel;
32892         this.view.refresh(true);
32893     },
32894
32895     // private
32896     onKeyDown : function(e){
32897         this.fireEvent("keydown", e);
32898     },
32899
32900     /**
32901      * Destroy this grid.
32902      * @param {Boolean} removeEl True to remove the element
32903      */
32904     destroy : function(removeEl, keepListeners){
32905         if(this.loadMask){
32906             this.loadMask.destroy();
32907         }
32908         var c = this.container;
32909         c.removeAllListeners();
32910         this.view.destroy();
32911         this.colModel.purgeListeners();
32912         if(!keepListeners){
32913             this.purgeListeners();
32914         }
32915         c.update("");
32916         if(removeEl === true){
32917             c.remove();
32918         }
32919     },
32920
32921     // private
32922     processEvent : function(name, e){
32923         // does this fire select???
32924         //Roo.log('grid:processEvent '  + name);
32925         
32926         if (name != 'touchstart' ) {
32927             this.fireEvent(name, e);    
32928         }
32929         
32930         var t = e.getTarget();
32931         var v = this.view;
32932         var header = v.findHeaderIndex(t);
32933         if(header !== false){
32934             var ename = name == 'touchstart' ? 'click' : name;
32935              
32936             this.fireEvent("header" + ename, this, header, e);
32937         }else{
32938             var row = v.findRowIndex(t);
32939             var cell = v.findCellIndex(t);
32940             if (name == 'touchstart') {
32941                 // first touch is always a click.
32942                 // hopefull this happens after selection is updated.?
32943                 name = false;
32944                 
32945                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32946                     var cs = this.selModel.getSelectedCell();
32947                     if (row == cs[0] && cell == cs[1]){
32948                         name = 'dblclick';
32949                     }
32950                 }
32951                 if (typeof(this.selModel.getSelections) != 'undefined') {
32952                     var cs = this.selModel.getSelections();
32953                     var ds = this.dataSource;
32954                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32955                         name = 'dblclick';
32956                     }
32957                 }
32958                 if (!name) {
32959                     return;
32960                 }
32961             }
32962             
32963             
32964             if(row !== false){
32965                 this.fireEvent("row" + name, this, row, e);
32966                 if(cell !== false){
32967                     this.fireEvent("cell" + name, this, row, cell, e);
32968                 }
32969             }
32970         }
32971     },
32972
32973     // private
32974     onClick : function(e){
32975         this.processEvent("click", e);
32976     },
32977    // private
32978     onTouchStart : function(e){
32979         this.processEvent("touchstart", e);
32980     },
32981
32982     // private
32983     onContextMenu : function(e, t){
32984         this.processEvent("contextmenu", e);
32985     },
32986
32987     // private
32988     onDblClick : function(e){
32989         this.processEvent("dblclick", e);
32990     },
32991
32992     // private
32993     walkCells : function(row, col, step, fn, scope){
32994         var cm = this.colModel, clen = cm.getColumnCount();
32995         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32996         if(step < 0){
32997             if(col < 0){
32998                 row--;
32999                 first = false;
33000             }
33001             while(row >= 0){
33002                 if(!first){
33003                     col = clen-1;
33004                 }
33005                 first = false;
33006                 while(col >= 0){
33007                     if(fn.call(scope || this, row, col, cm) === true){
33008                         return [row, col];
33009                     }
33010                     col--;
33011                 }
33012                 row--;
33013             }
33014         } else {
33015             if(col >= clen){
33016                 row++;
33017                 first = false;
33018             }
33019             while(row < rlen){
33020                 if(!first){
33021                     col = 0;
33022                 }
33023                 first = false;
33024                 while(col < clen){
33025                     if(fn.call(scope || this, row, col, cm) === true){
33026                         return [row, col];
33027                     }
33028                     col++;
33029                 }
33030                 row++;
33031             }
33032         }
33033         return null;
33034     },
33035
33036     // private
33037     getSelections : function(){
33038         return this.selModel.getSelections();
33039     },
33040
33041     /**
33042      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33043      * but if manual update is required this method will initiate it.
33044      */
33045     autoSize : function(){
33046         if(this.rendered){
33047             this.view.layout();
33048             if(this.view.adjustForScroll){
33049                 this.view.adjustForScroll();
33050             }
33051         }
33052     },
33053
33054     /**
33055      * Returns the grid's underlying element.
33056      * @return {Element} The element
33057      */
33058     getGridEl : function(){
33059         return this.container;
33060     },
33061
33062     // private for compatibility, overridden by editor grid
33063     stopEditing : function(){},
33064
33065     /**
33066      * Returns the grid's SelectionModel.
33067      * @return {SelectionModel}
33068      */
33069     getSelectionModel : function(){
33070         if(!this.selModel){
33071             this.selModel = new Roo.grid.RowSelectionModel();
33072         }
33073         return this.selModel;
33074     },
33075
33076     /**
33077      * Returns the grid's DataSource.
33078      * @return {DataSource}
33079      */
33080     getDataSource : function(){
33081         return this.dataSource;
33082     },
33083
33084     /**
33085      * Returns the grid's ColumnModel.
33086      * @return {ColumnModel}
33087      */
33088     getColumnModel : function(){
33089         return this.colModel;
33090     },
33091
33092     /**
33093      * Returns the grid's GridView object.
33094      * @return {GridView}
33095      */
33096     getView : function(){
33097         if(!this.view){
33098             this.view = new Roo.grid.GridView(this.viewConfig);
33099         }
33100         return this.view;
33101     },
33102     /**
33103      * Called to get grid's drag proxy text, by default returns this.ddText.
33104      * @return {String}
33105      */
33106     getDragDropText : function(){
33107         var count = this.selModel.getCount();
33108         return String.format(this.ddText, count, count == 1 ? '' : 's');
33109     }
33110 });
33111 /**
33112  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33113  * %0 is replaced with the number of selected rows.
33114  * @type String
33115  */
33116 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33117  * Based on:
33118  * Ext JS Library 1.1.1
33119  * Copyright(c) 2006-2007, Ext JS, LLC.
33120  *
33121  * Originally Released Under LGPL - original licence link has changed is not relivant.
33122  *
33123  * Fork - LGPL
33124  * <script type="text/javascript">
33125  */
33126  
33127 Roo.grid.AbstractGridView = function(){
33128         this.grid = null;
33129         
33130         this.events = {
33131             "beforerowremoved" : true,
33132             "beforerowsinserted" : true,
33133             "beforerefresh" : true,
33134             "rowremoved" : true,
33135             "rowsinserted" : true,
33136             "rowupdated" : true,
33137             "refresh" : true
33138         };
33139     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33140 };
33141
33142 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33143     rowClass : "x-grid-row",
33144     cellClass : "x-grid-cell",
33145     tdClass : "x-grid-td",
33146     hdClass : "x-grid-hd",
33147     splitClass : "x-grid-hd-split",
33148     
33149     init: function(grid){
33150         this.grid = grid;
33151                 var cid = this.grid.getGridEl().id;
33152         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33153         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33154         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33155         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33156         },
33157         
33158     getColumnRenderers : function(){
33159         var renderers = [];
33160         var cm = this.grid.colModel;
33161         var colCount = cm.getColumnCount();
33162         for(var i = 0; i < colCount; i++){
33163             renderers[i] = cm.getRenderer(i);
33164         }
33165         return renderers;
33166     },
33167     
33168     getColumnIds : function(){
33169         var ids = [];
33170         var cm = this.grid.colModel;
33171         var colCount = cm.getColumnCount();
33172         for(var i = 0; i < colCount; i++){
33173             ids[i] = cm.getColumnId(i);
33174         }
33175         return ids;
33176     },
33177     
33178     getDataIndexes : function(){
33179         if(!this.indexMap){
33180             this.indexMap = this.buildIndexMap();
33181         }
33182         return this.indexMap.colToData;
33183     },
33184     
33185     getColumnIndexByDataIndex : function(dataIndex){
33186         if(!this.indexMap){
33187             this.indexMap = this.buildIndexMap();
33188         }
33189         return this.indexMap.dataToCol[dataIndex];
33190     },
33191     
33192     /**
33193      * Set a css style for a column dynamically. 
33194      * @param {Number} colIndex The index of the column
33195      * @param {String} name The css property name
33196      * @param {String} value The css value
33197      */
33198     setCSSStyle : function(colIndex, name, value){
33199         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33200         Roo.util.CSS.updateRule(selector, name, value);
33201     },
33202     
33203     generateRules : function(cm){
33204         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33205         Roo.util.CSS.removeStyleSheet(rulesId);
33206         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33207             var cid = cm.getColumnId(i);
33208             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33209                          this.tdSelector, cid, " {\n}\n",
33210                          this.hdSelector, cid, " {\n}\n",
33211                          this.splitSelector, cid, " {\n}\n");
33212         }
33213         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33214     }
33215 });/*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225
33226 // private
33227 // This is a support class used internally by the Grid components
33228 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33229     this.grid = grid;
33230     this.view = grid.getView();
33231     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33232     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33233     if(hd2){
33234         this.setHandleElId(Roo.id(hd));
33235         this.setOuterHandleElId(Roo.id(hd2));
33236     }
33237     this.scroll = false;
33238 };
33239 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33240     maxDragWidth: 120,
33241     getDragData : function(e){
33242         var t = Roo.lib.Event.getTarget(e);
33243         var h = this.view.findHeaderCell(t);
33244         if(h){
33245             return {ddel: h.firstChild, header:h};
33246         }
33247         return false;
33248     },
33249
33250     onInitDrag : function(e){
33251         this.view.headersDisabled = true;
33252         var clone = this.dragData.ddel.cloneNode(true);
33253         clone.id = Roo.id();
33254         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33255         this.proxy.update(clone);
33256         return true;
33257     },
33258
33259     afterValidDrop : function(){
33260         var v = this.view;
33261         setTimeout(function(){
33262             v.headersDisabled = false;
33263         }, 50);
33264     },
33265
33266     afterInvalidDrop : function(){
33267         var v = this.view;
33268         setTimeout(function(){
33269             v.headersDisabled = false;
33270         }, 50);
33271     }
33272 });
33273 /*
33274  * Based on:
33275  * Ext JS Library 1.1.1
33276  * Copyright(c) 2006-2007, Ext JS, LLC.
33277  *
33278  * Originally Released Under LGPL - original licence link has changed is not relivant.
33279  *
33280  * Fork - LGPL
33281  * <script type="text/javascript">
33282  */
33283 // private
33284 // This is a support class used internally by the Grid components
33285 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33286     this.grid = grid;
33287     this.view = grid.getView();
33288     // split the proxies so they don't interfere with mouse events
33289     this.proxyTop = Roo.DomHelper.append(document.body, {
33290         cls:"col-move-top", html:"&#160;"
33291     }, true);
33292     this.proxyBottom = Roo.DomHelper.append(document.body, {
33293         cls:"col-move-bottom", html:"&#160;"
33294     }, true);
33295     this.proxyTop.hide = this.proxyBottom.hide = function(){
33296         this.setLeftTop(-100,-100);
33297         this.setStyle("visibility", "hidden");
33298     };
33299     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33300     // temporarily disabled
33301     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33302     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33303 };
33304 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33305     proxyOffsets : [-4, -9],
33306     fly: Roo.Element.fly,
33307
33308     getTargetFromEvent : function(e){
33309         var t = Roo.lib.Event.getTarget(e);
33310         var cindex = this.view.findCellIndex(t);
33311         if(cindex !== false){
33312             return this.view.getHeaderCell(cindex);
33313         }
33314         return null;
33315     },
33316
33317     nextVisible : function(h){
33318         var v = this.view, cm = this.grid.colModel;
33319         h = h.nextSibling;
33320         while(h){
33321             if(!cm.isHidden(v.getCellIndex(h))){
33322                 return h;
33323             }
33324             h = h.nextSibling;
33325         }
33326         return null;
33327     },
33328
33329     prevVisible : function(h){
33330         var v = this.view, cm = this.grid.colModel;
33331         h = h.prevSibling;
33332         while(h){
33333             if(!cm.isHidden(v.getCellIndex(h))){
33334                 return h;
33335             }
33336             h = h.prevSibling;
33337         }
33338         return null;
33339     },
33340
33341     positionIndicator : function(h, n, e){
33342         var x = Roo.lib.Event.getPageX(e);
33343         var r = Roo.lib.Dom.getRegion(n.firstChild);
33344         var px, pt, py = r.top + this.proxyOffsets[1];
33345         if((r.right - x) <= (r.right-r.left)/2){
33346             px = r.right+this.view.borderWidth;
33347             pt = "after";
33348         }else{
33349             px = r.left;
33350             pt = "before";
33351         }
33352         var oldIndex = this.view.getCellIndex(h);
33353         var newIndex = this.view.getCellIndex(n);
33354
33355         if(this.grid.colModel.isFixed(newIndex)){
33356             return false;
33357         }
33358
33359         var locked = this.grid.colModel.isLocked(newIndex);
33360
33361         if(pt == "after"){
33362             newIndex++;
33363         }
33364         if(oldIndex < newIndex){
33365             newIndex--;
33366         }
33367         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33368             return false;
33369         }
33370         px +=  this.proxyOffsets[0];
33371         this.proxyTop.setLeftTop(px, py);
33372         this.proxyTop.show();
33373         if(!this.bottomOffset){
33374             this.bottomOffset = this.view.mainHd.getHeight();
33375         }
33376         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33377         this.proxyBottom.show();
33378         return pt;
33379     },
33380
33381     onNodeEnter : function(n, dd, e, data){
33382         if(data.header != n){
33383             this.positionIndicator(data.header, n, e);
33384         }
33385     },
33386
33387     onNodeOver : function(n, dd, e, data){
33388         var result = false;
33389         if(data.header != n){
33390             result = this.positionIndicator(data.header, n, e);
33391         }
33392         if(!result){
33393             this.proxyTop.hide();
33394             this.proxyBottom.hide();
33395         }
33396         return result ? this.dropAllowed : this.dropNotAllowed;
33397     },
33398
33399     onNodeOut : function(n, dd, e, data){
33400         this.proxyTop.hide();
33401         this.proxyBottom.hide();
33402     },
33403
33404     onNodeDrop : function(n, dd, e, data){
33405         var h = data.header;
33406         if(h != n){
33407             var cm = this.grid.colModel;
33408             var x = Roo.lib.Event.getPageX(e);
33409             var r = Roo.lib.Dom.getRegion(n.firstChild);
33410             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33411             var oldIndex = this.view.getCellIndex(h);
33412             var newIndex = this.view.getCellIndex(n);
33413             var locked = cm.isLocked(newIndex);
33414             if(pt == "after"){
33415                 newIndex++;
33416             }
33417             if(oldIndex < newIndex){
33418                 newIndex--;
33419             }
33420             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33421                 return false;
33422             }
33423             cm.setLocked(oldIndex, locked, true);
33424             cm.moveColumn(oldIndex, newIndex);
33425             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33426             return true;
33427         }
33428         return false;
33429     }
33430 });
33431 /*
33432  * Based on:
33433  * Ext JS Library 1.1.1
33434  * Copyright(c) 2006-2007, Ext JS, LLC.
33435  *
33436  * Originally Released Under LGPL - original licence link has changed is not relivant.
33437  *
33438  * Fork - LGPL
33439  * <script type="text/javascript">
33440  */
33441   
33442 /**
33443  * @class Roo.grid.GridView
33444  * @extends Roo.util.Observable
33445  *
33446  * @constructor
33447  * @param {Object} config
33448  */
33449 Roo.grid.GridView = function(config){
33450     Roo.grid.GridView.superclass.constructor.call(this);
33451     this.el = null;
33452
33453     Roo.apply(this, config);
33454 };
33455
33456 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33457
33458     unselectable :  'unselectable="on"',
33459     unselectableCls :  'x-unselectable',
33460     
33461     
33462     rowClass : "x-grid-row",
33463
33464     cellClass : "x-grid-col",
33465
33466     tdClass : "x-grid-td",
33467
33468     hdClass : "x-grid-hd",
33469
33470     splitClass : "x-grid-split",
33471
33472     sortClasses : ["sort-asc", "sort-desc"],
33473
33474     enableMoveAnim : false,
33475
33476     hlColor: "C3DAF9",
33477
33478     dh : Roo.DomHelper,
33479
33480     fly : Roo.Element.fly,
33481
33482     css : Roo.util.CSS,
33483
33484     borderWidth: 1,
33485
33486     splitOffset: 3,
33487
33488     scrollIncrement : 22,
33489
33490     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33491
33492     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33493
33494     bind : function(ds, cm){
33495         if(this.ds){
33496             this.ds.un("load", this.onLoad, this);
33497             this.ds.un("datachanged", this.onDataChange, this);
33498             this.ds.un("add", this.onAdd, this);
33499             this.ds.un("remove", this.onRemove, this);
33500             this.ds.un("update", this.onUpdate, this);
33501             this.ds.un("clear", this.onClear, this);
33502         }
33503         if(ds){
33504             ds.on("load", this.onLoad, this);
33505             ds.on("datachanged", this.onDataChange, this);
33506             ds.on("add", this.onAdd, this);
33507             ds.on("remove", this.onRemove, this);
33508             ds.on("update", this.onUpdate, this);
33509             ds.on("clear", this.onClear, this);
33510         }
33511         this.ds = ds;
33512
33513         if(this.cm){
33514             this.cm.un("widthchange", this.onColWidthChange, this);
33515             this.cm.un("headerchange", this.onHeaderChange, this);
33516             this.cm.un("hiddenchange", this.onHiddenChange, this);
33517             this.cm.un("columnmoved", this.onColumnMove, this);
33518             this.cm.un("columnlockchange", this.onColumnLock, this);
33519         }
33520         if(cm){
33521             this.generateRules(cm);
33522             cm.on("widthchange", this.onColWidthChange, this);
33523             cm.on("headerchange", this.onHeaderChange, this);
33524             cm.on("hiddenchange", this.onHiddenChange, this);
33525             cm.on("columnmoved", this.onColumnMove, this);
33526             cm.on("columnlockchange", this.onColumnLock, this);
33527         }
33528         this.cm = cm;
33529     },
33530
33531     init: function(grid){
33532         Roo.grid.GridView.superclass.init.call(this, grid);
33533
33534         this.bind(grid.dataSource, grid.colModel);
33535
33536         grid.on("headerclick", this.handleHeaderClick, this);
33537
33538         if(grid.trackMouseOver){
33539             grid.on("mouseover", this.onRowOver, this);
33540             grid.on("mouseout", this.onRowOut, this);
33541         }
33542         grid.cancelTextSelection = function(){};
33543         this.gridId = grid.id;
33544
33545         var tpls = this.templates || {};
33546
33547         if(!tpls.master){
33548             tpls.master = new Roo.Template(
33549                '<div class="x-grid" hidefocus="true">',
33550                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33551                   '<div class="x-grid-topbar"></div>',
33552                   '<div class="x-grid-scroller"><div></div></div>',
33553                   '<div class="x-grid-locked">',
33554                       '<div class="x-grid-header">{lockedHeader}</div>',
33555                       '<div class="x-grid-body">{lockedBody}</div>',
33556                   "</div>",
33557                   '<div class="x-grid-viewport">',
33558                       '<div class="x-grid-header">{header}</div>',
33559                       '<div class="x-grid-body">{body}</div>',
33560                   "</div>",
33561                   '<div class="x-grid-bottombar"></div>',
33562                  
33563                   '<div class="x-grid-resize-proxy">&#160;</div>',
33564                "</div>"
33565             );
33566             tpls.master.disableformats = true;
33567         }
33568
33569         if(!tpls.header){
33570             tpls.header = new Roo.Template(
33571                '<table border="0" cellspacing="0" cellpadding="0">',
33572                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33573                "</table>{splits}"
33574             );
33575             tpls.header.disableformats = true;
33576         }
33577         tpls.header.compile();
33578
33579         if(!tpls.hcell){
33580             tpls.hcell = new Roo.Template(
33581                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33582                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33583                 "</div></td>"
33584              );
33585              tpls.hcell.disableFormats = true;
33586         }
33587         tpls.hcell.compile();
33588
33589         if(!tpls.hsplit){
33590             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33591                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33592             tpls.hsplit.disableFormats = true;
33593         }
33594         tpls.hsplit.compile();
33595
33596         if(!tpls.body){
33597             tpls.body = new Roo.Template(
33598                '<table border="0" cellspacing="0" cellpadding="0">',
33599                "<tbody>{rows}</tbody>",
33600                "</table>"
33601             );
33602             tpls.body.disableFormats = true;
33603         }
33604         tpls.body.compile();
33605
33606         if(!tpls.row){
33607             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33608             tpls.row.disableFormats = true;
33609         }
33610         tpls.row.compile();
33611
33612         if(!tpls.cell){
33613             tpls.cell = new Roo.Template(
33614                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33615                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33616                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33617                 "</td>"
33618             );
33619             tpls.cell.disableFormats = true;
33620         }
33621         tpls.cell.compile();
33622
33623         this.templates = tpls;
33624     },
33625
33626     // remap these for backwards compat
33627     onColWidthChange : function(){
33628         this.updateColumns.apply(this, arguments);
33629     },
33630     onHeaderChange : function(){
33631         this.updateHeaders.apply(this, arguments);
33632     }, 
33633     onHiddenChange : function(){
33634         this.handleHiddenChange.apply(this, arguments);
33635     },
33636     onColumnMove : function(){
33637         this.handleColumnMove.apply(this, arguments);
33638     },
33639     onColumnLock : function(){
33640         this.handleLockChange.apply(this, arguments);
33641     },
33642
33643     onDataChange : function(){
33644         this.refresh();
33645         this.updateHeaderSortState();
33646     },
33647
33648     onClear : function(){
33649         this.refresh();
33650     },
33651
33652     onUpdate : function(ds, record){
33653         this.refreshRow(record);
33654     },
33655
33656     refreshRow : function(record){
33657         var ds = this.ds, index;
33658         if(typeof record == 'number'){
33659             index = record;
33660             record = ds.getAt(index);
33661         }else{
33662             index = ds.indexOf(record);
33663         }
33664         this.insertRows(ds, index, index, true);
33665         this.onRemove(ds, record, index+1, true);
33666         this.syncRowHeights(index, index);
33667         this.layout();
33668         this.fireEvent("rowupdated", this, index, record);
33669     },
33670
33671     onAdd : function(ds, records, index){
33672         this.insertRows(ds, index, index + (records.length-1));
33673     },
33674
33675     onRemove : function(ds, record, index, isUpdate){
33676         if(isUpdate !== true){
33677             this.fireEvent("beforerowremoved", this, index, record);
33678         }
33679         var bt = this.getBodyTable(), lt = this.getLockedTable();
33680         if(bt.rows[index]){
33681             bt.firstChild.removeChild(bt.rows[index]);
33682         }
33683         if(lt.rows[index]){
33684             lt.firstChild.removeChild(lt.rows[index]);
33685         }
33686         if(isUpdate !== true){
33687             this.stripeRows(index);
33688             this.syncRowHeights(index, index);
33689             this.layout();
33690             this.fireEvent("rowremoved", this, index, record);
33691         }
33692     },
33693
33694     onLoad : function(){
33695         this.scrollToTop();
33696     },
33697
33698     /**
33699      * Scrolls the grid to the top
33700      */
33701     scrollToTop : function(){
33702         if(this.scroller){
33703             this.scroller.dom.scrollTop = 0;
33704             this.syncScroll();
33705         }
33706     },
33707
33708     /**
33709      * Gets a panel in the header of the grid that can be used for toolbars etc.
33710      * After modifying the contents of this panel a call to grid.autoSize() may be
33711      * required to register any changes in size.
33712      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33713      * @return Roo.Element
33714      */
33715     getHeaderPanel : function(doShow){
33716         if(doShow){
33717             this.headerPanel.show();
33718         }
33719         return this.headerPanel;
33720     },
33721
33722     /**
33723      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33724      * After modifying the contents of this panel a call to grid.autoSize() may be
33725      * required to register any changes in size.
33726      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33727      * @return Roo.Element
33728      */
33729     getFooterPanel : function(doShow){
33730         if(doShow){
33731             this.footerPanel.show();
33732         }
33733         return this.footerPanel;
33734     },
33735
33736     initElements : function(){
33737         var E = Roo.Element;
33738         var el = this.grid.getGridEl().dom.firstChild;
33739         var cs = el.childNodes;
33740
33741         this.el = new E(el);
33742         
33743          this.focusEl = new E(el.firstChild);
33744         this.focusEl.swallowEvent("click", true);
33745         
33746         this.headerPanel = new E(cs[1]);
33747         this.headerPanel.enableDisplayMode("block");
33748
33749         this.scroller = new E(cs[2]);
33750         this.scrollSizer = new E(this.scroller.dom.firstChild);
33751
33752         this.lockedWrap = new E(cs[3]);
33753         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33754         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33755
33756         this.mainWrap = new E(cs[4]);
33757         this.mainHd = new E(this.mainWrap.dom.firstChild);
33758         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33759
33760         this.footerPanel = new E(cs[5]);
33761         this.footerPanel.enableDisplayMode("block");
33762
33763         this.resizeProxy = new E(cs[6]);
33764
33765         this.headerSelector = String.format(
33766            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33767            this.lockedHd.id, this.mainHd.id
33768         );
33769
33770         this.splitterSelector = String.format(
33771            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33772            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33773         );
33774     },
33775     idToCssName : function(s)
33776     {
33777         return s.replace(/[^a-z0-9]+/ig, '-');
33778     },
33779
33780     getHeaderCell : function(index){
33781         return Roo.DomQuery.select(this.headerSelector)[index];
33782     },
33783
33784     getHeaderCellMeasure : function(index){
33785         return this.getHeaderCell(index).firstChild;
33786     },
33787
33788     getHeaderCellText : function(index){
33789         return this.getHeaderCell(index).firstChild.firstChild;
33790     },
33791
33792     getLockedTable : function(){
33793         return this.lockedBody.dom.firstChild;
33794     },
33795
33796     getBodyTable : function(){
33797         return this.mainBody.dom.firstChild;
33798     },
33799
33800     getLockedRow : function(index){
33801         return this.getLockedTable().rows[index];
33802     },
33803
33804     getRow : function(index){
33805         return this.getBodyTable().rows[index];
33806     },
33807
33808     getRowComposite : function(index){
33809         if(!this.rowEl){
33810             this.rowEl = new Roo.CompositeElementLite();
33811         }
33812         var els = [], lrow, mrow;
33813         if(lrow = this.getLockedRow(index)){
33814             els.push(lrow);
33815         }
33816         if(mrow = this.getRow(index)){
33817             els.push(mrow);
33818         }
33819         this.rowEl.elements = els;
33820         return this.rowEl;
33821     },
33822     /**
33823      * Gets the 'td' of the cell
33824      * 
33825      * @param {Integer} rowIndex row to select
33826      * @param {Integer} colIndex column to select
33827      * 
33828      * @return {Object} 
33829      */
33830     getCell : function(rowIndex, colIndex){
33831         var locked = this.cm.getLockedCount();
33832         var source;
33833         if(colIndex < locked){
33834             source = this.lockedBody.dom.firstChild;
33835         }else{
33836             source = this.mainBody.dom.firstChild;
33837             colIndex -= locked;
33838         }
33839         return source.rows[rowIndex].childNodes[colIndex];
33840     },
33841
33842     getCellText : function(rowIndex, colIndex){
33843         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33844     },
33845
33846     getCellBox : function(cell){
33847         var b = this.fly(cell).getBox();
33848         if(Roo.isOpera){ // opera fails to report the Y
33849             b.y = cell.offsetTop + this.mainBody.getY();
33850         }
33851         return b;
33852     },
33853
33854     getCellIndex : function(cell){
33855         var id = String(cell.className).match(this.cellRE);
33856         if(id){
33857             return parseInt(id[1], 10);
33858         }
33859         return 0;
33860     },
33861
33862     findHeaderIndex : function(n){
33863         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33864         return r ? this.getCellIndex(r) : false;
33865     },
33866
33867     findHeaderCell : function(n){
33868         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33869         return r ? r : false;
33870     },
33871
33872     findRowIndex : function(n){
33873         if(!n){
33874             return false;
33875         }
33876         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33877         return r ? r.rowIndex : false;
33878     },
33879
33880     findCellIndex : function(node){
33881         var stop = this.el.dom;
33882         while(node && node != stop){
33883             if(this.findRE.test(node.className)){
33884                 return this.getCellIndex(node);
33885             }
33886             node = node.parentNode;
33887         }
33888         return false;
33889     },
33890
33891     getColumnId : function(index){
33892         return this.cm.getColumnId(index);
33893     },
33894
33895     getSplitters : function()
33896     {
33897         if(this.splitterSelector){
33898            return Roo.DomQuery.select(this.splitterSelector);
33899         }else{
33900             return null;
33901       }
33902     },
33903
33904     getSplitter : function(index){
33905         return this.getSplitters()[index];
33906     },
33907
33908     onRowOver : function(e, t){
33909         var row;
33910         if((row = this.findRowIndex(t)) !== false){
33911             this.getRowComposite(row).addClass("x-grid-row-over");
33912         }
33913     },
33914
33915     onRowOut : function(e, t){
33916         var row;
33917         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33918             this.getRowComposite(row).removeClass("x-grid-row-over");
33919         }
33920     },
33921
33922     renderHeaders : function(){
33923         var cm = this.cm;
33924         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33925         var cb = [], lb = [], sb = [], lsb = [], p = {};
33926         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33927             p.cellId = "x-grid-hd-0-" + i;
33928             p.splitId = "x-grid-csplit-0-" + i;
33929             p.id = cm.getColumnId(i);
33930             p.value = cm.getColumnHeader(i) || "";
33931             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33932             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33933             if(!cm.isLocked(i)){
33934                 cb[cb.length] = ct.apply(p);
33935                 sb[sb.length] = st.apply(p);
33936             }else{
33937                 lb[lb.length] = ct.apply(p);
33938                 lsb[lsb.length] = st.apply(p);
33939             }
33940         }
33941         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33942                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33943     },
33944
33945     updateHeaders : function(){
33946         var html = this.renderHeaders();
33947         this.lockedHd.update(html[0]);
33948         this.mainHd.update(html[1]);
33949     },
33950
33951     /**
33952      * Focuses the specified row.
33953      * @param {Number} row The row index
33954      */
33955     focusRow : function(row)
33956     {
33957         //Roo.log('GridView.focusRow');
33958         var x = this.scroller.dom.scrollLeft;
33959         this.focusCell(row, 0, false);
33960         this.scroller.dom.scrollLeft = x;
33961     },
33962
33963     /**
33964      * Focuses the specified cell.
33965      * @param {Number} row The row index
33966      * @param {Number} col The column index
33967      * @param {Boolean} hscroll false to disable horizontal scrolling
33968      */
33969     focusCell : function(row, col, hscroll)
33970     {
33971         //Roo.log('GridView.focusCell');
33972         var el = this.ensureVisible(row, col, hscroll);
33973         this.focusEl.alignTo(el, "tl-tl");
33974         if(Roo.isGecko){
33975             this.focusEl.focus();
33976         }else{
33977             this.focusEl.focus.defer(1, this.focusEl);
33978         }
33979     },
33980
33981     /**
33982      * Scrolls the specified cell into view
33983      * @param {Number} row The row index
33984      * @param {Number} col The column index
33985      * @param {Boolean} hscroll false to disable horizontal scrolling
33986      */
33987     ensureVisible : function(row, col, hscroll)
33988     {
33989         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33990         //return null; //disable for testing.
33991         if(typeof row != "number"){
33992             row = row.rowIndex;
33993         }
33994         if(row < 0 && row >= this.ds.getCount()){
33995             return  null;
33996         }
33997         col = (col !== undefined ? col : 0);
33998         var cm = this.grid.colModel;
33999         while(cm.isHidden(col)){
34000             col++;
34001         }
34002
34003         var el = this.getCell(row, col);
34004         if(!el){
34005             return null;
34006         }
34007         var c = this.scroller.dom;
34008
34009         var ctop = parseInt(el.offsetTop, 10);
34010         var cleft = parseInt(el.offsetLeft, 10);
34011         var cbot = ctop + el.offsetHeight;
34012         var cright = cleft + el.offsetWidth;
34013         
34014         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34015         var stop = parseInt(c.scrollTop, 10);
34016         var sleft = parseInt(c.scrollLeft, 10);
34017         var sbot = stop + ch;
34018         var sright = sleft + c.clientWidth;
34019         /*
34020         Roo.log('GridView.ensureVisible:' +
34021                 ' ctop:' + ctop +
34022                 ' c.clientHeight:' + c.clientHeight +
34023                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34024                 ' stop:' + stop +
34025                 ' cbot:' + cbot +
34026                 ' sbot:' + sbot +
34027                 ' ch:' + ch  
34028                 );
34029         */
34030         if(ctop < stop){
34031              c.scrollTop = ctop;
34032             //Roo.log("set scrolltop to ctop DISABLE?");
34033         }else if(cbot > sbot){
34034             //Roo.log("set scrolltop to cbot-ch");
34035             c.scrollTop = cbot-ch;
34036         }
34037         
34038         if(hscroll !== false){
34039             if(cleft < sleft){
34040                 c.scrollLeft = cleft;
34041             }else if(cright > sright){
34042                 c.scrollLeft = cright-c.clientWidth;
34043             }
34044         }
34045          
34046         return el;
34047     },
34048
34049     updateColumns : function(){
34050         this.grid.stopEditing();
34051         var cm = this.grid.colModel, colIds = this.getColumnIds();
34052         //var totalWidth = cm.getTotalWidth();
34053         var pos = 0;
34054         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34055             //if(cm.isHidden(i)) continue;
34056             var w = cm.getColumnWidth(i);
34057             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34058             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34059         }
34060         this.updateSplitters();
34061     },
34062
34063     generateRules : function(cm){
34064         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34065         Roo.util.CSS.removeStyleSheet(rulesId);
34066         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34067             var cid = cm.getColumnId(i);
34068             var align = '';
34069             if(cm.config[i].align){
34070                 align = 'text-align:'+cm.config[i].align+';';
34071             }
34072             var hidden = '';
34073             if(cm.isHidden(i)){
34074                 hidden = 'display:none;';
34075             }
34076             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34077             ruleBuf.push(
34078                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34079                     this.hdSelector, cid, " {\n", align, width, "}\n",
34080                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34081                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34082         }
34083         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34084     },
34085
34086     updateSplitters : function(){
34087         var cm = this.cm, s = this.getSplitters();
34088         if(s){ // splitters not created yet
34089             var pos = 0, locked = true;
34090             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34091                 if(cm.isHidden(i)) {
34092                     continue;
34093                 }
34094                 var w = cm.getColumnWidth(i); // make sure it's a number
34095                 if(!cm.isLocked(i) && locked){
34096                     pos = 0;
34097                     locked = false;
34098                 }
34099                 pos += w;
34100                 s[i].style.left = (pos-this.splitOffset) + "px";
34101             }
34102         }
34103     },
34104
34105     handleHiddenChange : function(colModel, colIndex, hidden){
34106         if(hidden){
34107             this.hideColumn(colIndex);
34108         }else{
34109             this.unhideColumn(colIndex);
34110         }
34111     },
34112
34113     hideColumn : function(colIndex){
34114         var cid = this.getColumnId(colIndex);
34115         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34116         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34117         if(Roo.isSafari){
34118             this.updateHeaders();
34119         }
34120         this.updateSplitters();
34121         this.layout();
34122     },
34123
34124     unhideColumn : function(colIndex){
34125         var cid = this.getColumnId(colIndex);
34126         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34127         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34128
34129         if(Roo.isSafari){
34130             this.updateHeaders();
34131         }
34132         this.updateSplitters();
34133         this.layout();
34134     },
34135
34136     insertRows : function(dm, firstRow, lastRow, isUpdate){
34137         if(firstRow == 0 && lastRow == dm.getCount()-1){
34138             this.refresh();
34139         }else{
34140             if(!isUpdate){
34141                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34142             }
34143             var s = this.getScrollState();
34144             var markup = this.renderRows(firstRow, lastRow);
34145             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34146             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34147             this.restoreScroll(s);
34148             if(!isUpdate){
34149                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34150                 this.syncRowHeights(firstRow, lastRow);
34151                 this.stripeRows(firstRow);
34152                 this.layout();
34153             }
34154         }
34155     },
34156
34157     bufferRows : function(markup, target, index){
34158         var before = null, trows = target.rows, tbody = target.tBodies[0];
34159         if(index < trows.length){
34160             before = trows[index];
34161         }
34162         var b = document.createElement("div");
34163         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34164         var rows = b.firstChild.rows;
34165         for(var i = 0, len = rows.length; i < len; i++){
34166             if(before){
34167                 tbody.insertBefore(rows[0], before);
34168             }else{
34169                 tbody.appendChild(rows[0]);
34170             }
34171         }
34172         b.innerHTML = "";
34173         b = null;
34174     },
34175
34176     deleteRows : function(dm, firstRow, lastRow){
34177         if(dm.getRowCount()<1){
34178             this.fireEvent("beforerefresh", this);
34179             this.mainBody.update("");
34180             this.lockedBody.update("");
34181             this.fireEvent("refresh", this);
34182         }else{
34183             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34184             var bt = this.getBodyTable();
34185             var tbody = bt.firstChild;
34186             var rows = bt.rows;
34187             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34188                 tbody.removeChild(rows[firstRow]);
34189             }
34190             this.stripeRows(firstRow);
34191             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34192         }
34193     },
34194
34195     updateRows : function(dataSource, firstRow, lastRow){
34196         var s = this.getScrollState();
34197         this.refresh();
34198         this.restoreScroll(s);
34199     },
34200
34201     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34202         if(!noRefresh){
34203            this.refresh();
34204         }
34205         this.updateHeaderSortState();
34206     },
34207
34208     getScrollState : function(){
34209         
34210         var sb = this.scroller.dom;
34211         return {left: sb.scrollLeft, top: sb.scrollTop};
34212     },
34213
34214     stripeRows : function(startRow){
34215         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34216             return;
34217         }
34218         startRow = startRow || 0;
34219         var rows = this.getBodyTable().rows;
34220         var lrows = this.getLockedTable().rows;
34221         var cls = ' x-grid-row-alt ';
34222         for(var i = startRow, len = rows.length; i < len; i++){
34223             var row = rows[i], lrow = lrows[i];
34224             var isAlt = ((i+1) % 2 == 0);
34225             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34226             if(isAlt == hasAlt){
34227                 continue;
34228             }
34229             if(isAlt){
34230                 row.className += " x-grid-row-alt";
34231             }else{
34232                 row.className = row.className.replace("x-grid-row-alt", "");
34233             }
34234             if(lrow){
34235                 lrow.className = row.className;
34236             }
34237         }
34238     },
34239
34240     restoreScroll : function(state){
34241         //Roo.log('GridView.restoreScroll');
34242         var sb = this.scroller.dom;
34243         sb.scrollLeft = state.left;
34244         sb.scrollTop = state.top;
34245         this.syncScroll();
34246     },
34247
34248     syncScroll : function(){
34249         //Roo.log('GridView.syncScroll');
34250         var sb = this.scroller.dom;
34251         var sh = this.mainHd.dom;
34252         var bs = this.mainBody.dom;
34253         var lv = this.lockedBody.dom;
34254         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34255         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34256     },
34257
34258     handleScroll : function(e){
34259         this.syncScroll();
34260         var sb = this.scroller.dom;
34261         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34262         e.stopEvent();
34263     },
34264
34265     handleWheel : function(e){
34266         var d = e.getWheelDelta();
34267         this.scroller.dom.scrollTop -= d*22;
34268         // set this here to prevent jumpy scrolling on large tables
34269         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34270         e.stopEvent();
34271     },
34272
34273     renderRows : function(startRow, endRow){
34274         // pull in all the crap needed to render rows
34275         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34276         var colCount = cm.getColumnCount();
34277
34278         if(ds.getCount() < 1){
34279             return ["", ""];
34280         }
34281
34282         // build a map for all the columns
34283         var cs = [];
34284         for(var i = 0; i < colCount; i++){
34285             var name = cm.getDataIndex(i);
34286             cs[i] = {
34287                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34288                 renderer : cm.getRenderer(i),
34289                 id : cm.getColumnId(i),
34290                 locked : cm.isLocked(i),
34291                 has_editor : cm.isCellEditable(i)
34292             };
34293         }
34294
34295         startRow = startRow || 0;
34296         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34297
34298         // records to render
34299         var rs = ds.getRange(startRow, endRow);
34300
34301         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34302     },
34303
34304     // As much as I hate to duplicate code, this was branched because FireFox really hates
34305     // [].join("") on strings. The performance difference was substantial enough to
34306     // branch this function
34307     doRender : Roo.isGecko ?
34308             function(cs, rs, ds, startRow, colCount, stripe){
34309                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34310                 // buffers
34311                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34312                 
34313                 var hasListener = this.grid.hasListener('rowclass');
34314                 var rowcfg = {};
34315                 for(var j = 0, len = rs.length; j < len; j++){
34316                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34317                     for(var i = 0; i < colCount; i++){
34318                         c = cs[i];
34319                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34320                         p.id = c.id;
34321                         p.css = p.attr = "";
34322                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34323                         if(p.value == undefined || p.value === "") {
34324                             p.value = "&#160;";
34325                         }
34326                         if(c.has_editor){
34327                             p.css += ' x-grid-editable-cell';
34328                         }
34329                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34330                             p.css +=  ' x-grid-dirty-cell';
34331                         }
34332                         var markup = ct.apply(p);
34333                         if(!c.locked){
34334                             cb+= markup;
34335                         }else{
34336                             lcb+= markup;
34337                         }
34338                     }
34339                     var alt = [];
34340                     if(stripe && ((rowIndex+1) % 2 == 0)){
34341                         alt.push("x-grid-row-alt")
34342                     }
34343                     if(r.dirty){
34344                         alt.push(  " x-grid-dirty-row");
34345                     }
34346                     rp.cells = lcb;
34347                     if(this.getRowClass){
34348                         alt.push(this.getRowClass(r, rowIndex));
34349                     }
34350                     if (hasListener) {
34351                         rowcfg = {
34352                              
34353                             record: r,
34354                             rowIndex : rowIndex,
34355                             rowClass : ''
34356                         };
34357                         this.grid.fireEvent('rowclass', this, rowcfg);
34358                         alt.push(rowcfg.rowClass);
34359                     }
34360                     rp.alt = alt.join(" ");
34361                     lbuf+= rt.apply(rp);
34362                     rp.cells = cb;
34363                     buf+=  rt.apply(rp);
34364                 }
34365                 return [lbuf, buf];
34366             } :
34367             function(cs, rs, ds, startRow, colCount, stripe){
34368                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34369                 // buffers
34370                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34371                 var hasListener = this.grid.hasListener('rowclass');
34372  
34373                 var rowcfg = {};
34374                 for(var j = 0, len = rs.length; j < len; j++){
34375                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34376                     for(var i = 0; i < colCount; i++){
34377                         c = cs[i];
34378                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34379                         p.id = c.id;
34380                         p.css = p.attr = "";
34381                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34382                         if(p.value == undefined || p.value === "") {
34383                             p.value = "&#160;";
34384                         }
34385                         //Roo.log(c);
34386                          if(c.has_editor){
34387                             p.css += ' x-grid-editable-cell';
34388                         }
34389                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34390                             p.css += ' x-grid-dirty-cell' 
34391                         }
34392                         
34393                         var markup = ct.apply(p);
34394                         if(!c.locked){
34395                             cb[cb.length] = markup;
34396                         }else{
34397                             lcb[lcb.length] = markup;
34398                         }
34399                     }
34400                     var alt = [];
34401                     if(stripe && ((rowIndex+1) % 2 == 0)){
34402                         alt.push( "x-grid-row-alt");
34403                     }
34404                     if(r.dirty){
34405                         alt.push(" x-grid-dirty-row");
34406                     }
34407                     rp.cells = lcb;
34408                     if(this.getRowClass){
34409                         alt.push( this.getRowClass(r, rowIndex));
34410                     }
34411                     if (hasListener) {
34412                         rowcfg = {
34413                              
34414                             record: r,
34415                             rowIndex : rowIndex,
34416                             rowClass : ''
34417                         };
34418                         this.grid.fireEvent('rowclass', this, rowcfg);
34419                         alt.push(rowcfg.rowClass);
34420                     }
34421                     
34422                     rp.alt = alt.join(" ");
34423                     rp.cells = lcb.join("");
34424                     lbuf[lbuf.length] = rt.apply(rp);
34425                     rp.cells = cb.join("");
34426                     buf[buf.length] =  rt.apply(rp);
34427                 }
34428                 return [lbuf.join(""), buf.join("")];
34429             },
34430
34431     renderBody : function(){
34432         var markup = this.renderRows();
34433         var bt = this.templates.body;
34434         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34435     },
34436
34437     /**
34438      * Refreshes the grid
34439      * @param {Boolean} headersToo
34440      */
34441     refresh : function(headersToo){
34442         this.fireEvent("beforerefresh", this);
34443         this.grid.stopEditing();
34444         var result = this.renderBody();
34445         this.lockedBody.update(result[0]);
34446         this.mainBody.update(result[1]);
34447         if(headersToo === true){
34448             this.updateHeaders();
34449             this.updateColumns();
34450             this.updateSplitters();
34451             this.updateHeaderSortState();
34452         }
34453         this.syncRowHeights();
34454         this.layout();
34455         this.fireEvent("refresh", this);
34456     },
34457
34458     handleColumnMove : function(cm, oldIndex, newIndex){
34459         this.indexMap = null;
34460         var s = this.getScrollState();
34461         this.refresh(true);
34462         this.restoreScroll(s);
34463         this.afterMove(newIndex);
34464     },
34465
34466     afterMove : function(colIndex){
34467         if(this.enableMoveAnim && Roo.enableFx){
34468             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34469         }
34470         // if multisort - fix sortOrder, and reload..
34471         if (this.grid.dataSource.multiSort) {
34472             // the we can call sort again..
34473             var dm = this.grid.dataSource;
34474             var cm = this.grid.colModel;
34475             var so = [];
34476             for(var i = 0; i < cm.config.length; i++ ) {
34477                 
34478                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34479                     continue; // dont' bother, it's not in sort list or being set.
34480                 }
34481                 
34482                 so.push(cm.config[i].dataIndex);
34483             };
34484             dm.sortOrder = so;
34485             dm.load(dm.lastOptions);
34486             
34487             
34488         }
34489         
34490     },
34491
34492     updateCell : function(dm, rowIndex, dataIndex){
34493         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34494         if(typeof colIndex == "undefined"){ // not present in grid
34495             return;
34496         }
34497         var cm = this.grid.colModel;
34498         var cell = this.getCell(rowIndex, colIndex);
34499         var cellText = this.getCellText(rowIndex, colIndex);
34500
34501         var p = {
34502             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34503             id : cm.getColumnId(colIndex),
34504             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34505         };
34506         var renderer = cm.getRenderer(colIndex);
34507         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34508         if(typeof val == "undefined" || val === "") {
34509             val = "&#160;";
34510         }
34511         cellText.innerHTML = val;
34512         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34513         this.syncRowHeights(rowIndex, rowIndex);
34514     },
34515
34516     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34517         var maxWidth = 0;
34518         if(this.grid.autoSizeHeaders){
34519             var h = this.getHeaderCellMeasure(colIndex);
34520             maxWidth = Math.max(maxWidth, h.scrollWidth);
34521         }
34522         var tb, index;
34523         if(this.cm.isLocked(colIndex)){
34524             tb = this.getLockedTable();
34525             index = colIndex;
34526         }else{
34527             tb = this.getBodyTable();
34528             index = colIndex - this.cm.getLockedCount();
34529         }
34530         if(tb && tb.rows){
34531             var rows = tb.rows;
34532             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34533             for(var i = 0; i < stopIndex; i++){
34534                 var cell = rows[i].childNodes[index].firstChild;
34535                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34536             }
34537         }
34538         return maxWidth + /*margin for error in IE*/ 5;
34539     },
34540     /**
34541      * Autofit a column to its content.
34542      * @param {Number} colIndex
34543      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34544      */
34545      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34546          if(this.cm.isHidden(colIndex)){
34547              return; // can't calc a hidden column
34548          }
34549         if(forceMinSize){
34550             var cid = this.cm.getColumnId(colIndex);
34551             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34552            if(this.grid.autoSizeHeaders){
34553                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34554            }
34555         }
34556         var newWidth = this.calcColumnWidth(colIndex);
34557         this.cm.setColumnWidth(colIndex,
34558             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34559         if(!suppressEvent){
34560             this.grid.fireEvent("columnresize", colIndex, newWidth);
34561         }
34562     },
34563
34564     /**
34565      * Autofits all columns to their content and then expands to fit any extra space in the grid
34566      */
34567      autoSizeColumns : function(){
34568         var cm = this.grid.colModel;
34569         var colCount = cm.getColumnCount();
34570         for(var i = 0; i < colCount; i++){
34571             this.autoSizeColumn(i, true, true);
34572         }
34573         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34574             this.fitColumns();
34575         }else{
34576             this.updateColumns();
34577             this.layout();
34578         }
34579     },
34580
34581     /**
34582      * Autofits all columns to the grid's width proportionate with their current size
34583      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34584      */
34585     fitColumns : function(reserveScrollSpace){
34586         var cm = this.grid.colModel;
34587         var colCount = cm.getColumnCount();
34588         var cols = [];
34589         var width = 0;
34590         var i, w;
34591         for (i = 0; i < colCount; i++){
34592             if(!cm.isHidden(i) && !cm.isFixed(i)){
34593                 w = cm.getColumnWidth(i);
34594                 cols.push(i);
34595                 cols.push(w);
34596                 width += w;
34597             }
34598         }
34599         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34600         if(reserveScrollSpace){
34601             avail -= 17;
34602         }
34603         var frac = (avail - cm.getTotalWidth())/width;
34604         while (cols.length){
34605             w = cols.pop();
34606             i = cols.pop();
34607             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34608         }
34609         this.updateColumns();
34610         this.layout();
34611     },
34612
34613     onRowSelect : function(rowIndex){
34614         var row = this.getRowComposite(rowIndex);
34615         row.addClass("x-grid-row-selected");
34616     },
34617
34618     onRowDeselect : function(rowIndex){
34619         var row = this.getRowComposite(rowIndex);
34620         row.removeClass("x-grid-row-selected");
34621     },
34622
34623     onCellSelect : function(row, col){
34624         var cell = this.getCell(row, col);
34625         if(cell){
34626             Roo.fly(cell).addClass("x-grid-cell-selected");
34627         }
34628     },
34629
34630     onCellDeselect : function(row, col){
34631         var cell = this.getCell(row, col);
34632         if(cell){
34633             Roo.fly(cell).removeClass("x-grid-cell-selected");
34634         }
34635     },
34636
34637     updateHeaderSortState : function(){
34638         
34639         // sort state can be single { field: xxx, direction : yyy}
34640         // or   { xxx=>ASC , yyy : DESC ..... }
34641         
34642         var mstate = {};
34643         if (!this.ds.multiSort) { 
34644             var state = this.ds.getSortState();
34645             if(!state){
34646                 return;
34647             }
34648             mstate[state.field] = state.direction;
34649             // FIXME... - this is not used here.. but might be elsewhere..
34650             this.sortState = state;
34651             
34652         } else {
34653             mstate = this.ds.sortToggle;
34654         }
34655         //remove existing sort classes..
34656         
34657         var sc = this.sortClasses;
34658         var hds = this.el.select(this.headerSelector).removeClass(sc);
34659         
34660         for(var f in mstate) {
34661         
34662             var sortColumn = this.cm.findColumnIndex(f);
34663             
34664             if(sortColumn != -1){
34665                 var sortDir = mstate[f];        
34666                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34667             }
34668         }
34669         
34670          
34671         
34672     },
34673
34674
34675     handleHeaderClick : function(g, index,e){
34676         
34677         Roo.log("header click");
34678         
34679         if (Roo.isTouch) {
34680             // touch events on header are handled by context
34681             this.handleHdCtx(g,index,e);
34682             return;
34683         }
34684         
34685         
34686         if(this.headersDisabled){
34687             return;
34688         }
34689         var dm = g.dataSource, cm = g.colModel;
34690         if(!cm.isSortable(index)){
34691             return;
34692         }
34693         g.stopEditing();
34694         
34695         if (dm.multiSort) {
34696             // update the sortOrder
34697             var so = [];
34698             for(var i = 0; i < cm.config.length; i++ ) {
34699                 
34700                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34701                     continue; // dont' bother, it's not in sort list or being set.
34702                 }
34703                 
34704                 so.push(cm.config[i].dataIndex);
34705             };
34706             dm.sortOrder = so;
34707         }
34708         
34709         
34710         dm.sort(cm.getDataIndex(index));
34711     },
34712
34713
34714     destroy : function(){
34715         if(this.colMenu){
34716             this.colMenu.removeAll();
34717             Roo.menu.MenuMgr.unregister(this.colMenu);
34718             this.colMenu.getEl().remove();
34719             delete this.colMenu;
34720         }
34721         if(this.hmenu){
34722             this.hmenu.removeAll();
34723             Roo.menu.MenuMgr.unregister(this.hmenu);
34724             this.hmenu.getEl().remove();
34725             delete this.hmenu;
34726         }
34727         if(this.grid.enableColumnMove){
34728             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34729             if(dds){
34730                 for(var dd in dds){
34731                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34732                         var elid = dds[dd].dragElId;
34733                         dds[dd].unreg();
34734                         Roo.get(elid).remove();
34735                     } else if(dds[dd].config.isTarget){
34736                         dds[dd].proxyTop.remove();
34737                         dds[dd].proxyBottom.remove();
34738                         dds[dd].unreg();
34739                     }
34740                     if(Roo.dd.DDM.locationCache[dd]){
34741                         delete Roo.dd.DDM.locationCache[dd];
34742                     }
34743                 }
34744                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34745             }
34746         }
34747         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34748         this.bind(null, null);
34749         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34750     },
34751
34752     handleLockChange : function(){
34753         this.refresh(true);
34754     },
34755
34756     onDenyColumnLock : function(){
34757
34758     },
34759
34760     onDenyColumnHide : function(){
34761
34762     },
34763
34764     handleHdMenuClick : function(item){
34765         var index = this.hdCtxIndex;
34766         var cm = this.cm, ds = this.ds;
34767         switch(item.id){
34768             case "asc":
34769                 ds.sort(cm.getDataIndex(index), "ASC");
34770                 break;
34771             case "desc":
34772                 ds.sort(cm.getDataIndex(index), "DESC");
34773                 break;
34774             case "lock":
34775                 var lc = cm.getLockedCount();
34776                 if(cm.getColumnCount(true) <= lc+1){
34777                     this.onDenyColumnLock();
34778                     return;
34779                 }
34780                 if(lc != index){
34781                     cm.setLocked(index, true, true);
34782                     cm.moveColumn(index, lc);
34783                     this.grid.fireEvent("columnmove", index, lc);
34784                 }else{
34785                     cm.setLocked(index, true);
34786                 }
34787             break;
34788             case "unlock":
34789                 var lc = cm.getLockedCount();
34790                 if((lc-1) != index){
34791                     cm.setLocked(index, false, true);
34792                     cm.moveColumn(index, lc-1);
34793                     this.grid.fireEvent("columnmove", index, lc-1);
34794                 }else{
34795                     cm.setLocked(index, false);
34796                 }
34797             break;
34798             case 'wider': // used to expand cols on touch..
34799             case 'narrow':
34800                 var cw = cm.getColumnWidth(index);
34801                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34802                 cw = Math.max(0, cw);
34803                 cw = Math.min(cw,4000);
34804                 cm.setColumnWidth(index, cw);
34805                 break;
34806                 
34807             default:
34808                 index = cm.getIndexById(item.id.substr(4));
34809                 if(index != -1){
34810                     if(item.checked && cm.getColumnCount(true) <= 1){
34811                         this.onDenyColumnHide();
34812                         return false;
34813                     }
34814                     cm.setHidden(index, item.checked);
34815                 }
34816         }
34817         return true;
34818     },
34819
34820     beforeColMenuShow : function(){
34821         var cm = this.cm,  colCount = cm.getColumnCount();
34822         this.colMenu.removeAll();
34823         for(var i = 0; i < colCount; i++){
34824             this.colMenu.add(new Roo.menu.CheckItem({
34825                 id: "col-"+cm.getColumnId(i),
34826                 text: cm.getColumnHeader(i),
34827                 checked: !cm.isHidden(i),
34828                 hideOnClick:false
34829             }));
34830         }
34831     },
34832
34833     handleHdCtx : function(g, index, e){
34834         e.stopEvent();
34835         var hd = this.getHeaderCell(index);
34836         this.hdCtxIndex = index;
34837         var ms = this.hmenu.items, cm = this.cm;
34838         ms.get("asc").setDisabled(!cm.isSortable(index));
34839         ms.get("desc").setDisabled(!cm.isSortable(index));
34840         if(this.grid.enableColLock !== false){
34841             ms.get("lock").setDisabled(cm.isLocked(index));
34842             ms.get("unlock").setDisabled(!cm.isLocked(index));
34843         }
34844         this.hmenu.show(hd, "tl-bl");
34845     },
34846
34847     handleHdOver : function(e){
34848         var hd = this.findHeaderCell(e.getTarget());
34849         if(hd && !this.headersDisabled){
34850             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34851                this.fly(hd).addClass("x-grid-hd-over");
34852             }
34853         }
34854     },
34855
34856     handleHdOut : function(e){
34857         var hd = this.findHeaderCell(e.getTarget());
34858         if(hd){
34859             this.fly(hd).removeClass("x-grid-hd-over");
34860         }
34861     },
34862
34863     handleSplitDblClick : function(e, t){
34864         var i = this.getCellIndex(t);
34865         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34866             this.autoSizeColumn(i, true);
34867             this.layout();
34868         }
34869     },
34870
34871     render : function(){
34872
34873         var cm = this.cm;
34874         var colCount = cm.getColumnCount();
34875
34876         if(this.grid.monitorWindowResize === true){
34877             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34878         }
34879         var header = this.renderHeaders();
34880         var body = this.templates.body.apply({rows:""});
34881         var html = this.templates.master.apply({
34882             lockedBody: body,
34883             body: body,
34884             lockedHeader: header[0],
34885             header: header[1]
34886         });
34887
34888         //this.updateColumns();
34889
34890         this.grid.getGridEl().dom.innerHTML = html;
34891
34892         this.initElements();
34893         
34894         // a kludge to fix the random scolling effect in webkit
34895         this.el.on("scroll", function() {
34896             this.el.dom.scrollTop=0; // hopefully not recursive..
34897         },this);
34898
34899         this.scroller.on("scroll", this.handleScroll, this);
34900         this.lockedBody.on("mousewheel", this.handleWheel, this);
34901         this.mainBody.on("mousewheel", this.handleWheel, this);
34902
34903         this.mainHd.on("mouseover", this.handleHdOver, this);
34904         this.mainHd.on("mouseout", this.handleHdOut, this);
34905         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34906                 {delegate: "."+this.splitClass});
34907
34908         this.lockedHd.on("mouseover", this.handleHdOver, this);
34909         this.lockedHd.on("mouseout", this.handleHdOut, this);
34910         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34911                 {delegate: "."+this.splitClass});
34912
34913         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34914             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34915         }
34916
34917         this.updateSplitters();
34918
34919         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34920             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34921             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34922         }
34923
34924         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34925             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34926             this.hmenu.add(
34927                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34928                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34929             );
34930             if(this.grid.enableColLock !== false){
34931                 this.hmenu.add('-',
34932                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34933                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34934                 );
34935             }
34936             if (Roo.isTouch) {
34937                  this.hmenu.add('-',
34938                     {id:"wider", text: this.columnsWiderText},
34939                     {id:"narrow", text: this.columnsNarrowText }
34940                 );
34941                 
34942                  
34943             }
34944             
34945             if(this.grid.enableColumnHide !== false){
34946
34947                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34948                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34949                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34950
34951                 this.hmenu.add('-',
34952                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34953                 );
34954             }
34955             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34956
34957             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34958         }
34959
34960         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34961             this.dd = new Roo.grid.GridDragZone(this.grid, {
34962                 ddGroup : this.grid.ddGroup || 'GridDD'
34963             });
34964             
34965         }
34966
34967         /*
34968         for(var i = 0; i < colCount; i++){
34969             if(cm.isHidden(i)){
34970                 this.hideColumn(i);
34971             }
34972             if(cm.config[i].align){
34973                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34974                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34975             }
34976         }*/
34977         
34978         this.updateHeaderSortState();
34979
34980         this.beforeInitialResize();
34981         this.layout(true);
34982
34983         // two part rendering gives faster view to the user
34984         this.renderPhase2.defer(1, this);
34985     },
34986
34987     renderPhase2 : function(){
34988         // render the rows now
34989         this.refresh();
34990         if(this.grid.autoSizeColumns){
34991             this.autoSizeColumns();
34992         }
34993     },
34994
34995     beforeInitialResize : function(){
34996
34997     },
34998
34999     onColumnSplitterMoved : function(i, w){
35000         this.userResized = true;
35001         var cm = this.grid.colModel;
35002         cm.setColumnWidth(i, w, true);
35003         var cid = cm.getColumnId(i);
35004         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35005         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35006         this.updateSplitters();
35007         this.layout();
35008         this.grid.fireEvent("columnresize", i, w);
35009     },
35010
35011     syncRowHeights : function(startIndex, endIndex){
35012         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35013             startIndex = startIndex || 0;
35014             var mrows = this.getBodyTable().rows;
35015             var lrows = this.getLockedTable().rows;
35016             var len = mrows.length-1;
35017             endIndex = Math.min(endIndex || len, len);
35018             for(var i = startIndex; i <= endIndex; i++){
35019                 var m = mrows[i], l = lrows[i];
35020                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35021                 m.style.height = l.style.height = h + "px";
35022             }
35023         }
35024     },
35025
35026     layout : function(initialRender, is2ndPass){
35027         var g = this.grid;
35028         var auto = g.autoHeight;
35029         var scrollOffset = 16;
35030         var c = g.getGridEl(), cm = this.cm,
35031                 expandCol = g.autoExpandColumn,
35032                 gv = this;
35033         //c.beginMeasure();
35034
35035         if(!c.dom.offsetWidth){ // display:none?
35036             if(initialRender){
35037                 this.lockedWrap.show();
35038                 this.mainWrap.show();
35039             }
35040             return;
35041         }
35042
35043         var hasLock = this.cm.isLocked(0);
35044
35045         var tbh = this.headerPanel.getHeight();
35046         var bbh = this.footerPanel.getHeight();
35047
35048         if(auto){
35049             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35050             var newHeight = ch + c.getBorderWidth("tb");
35051             if(g.maxHeight){
35052                 newHeight = Math.min(g.maxHeight, newHeight);
35053             }
35054             c.setHeight(newHeight);
35055         }
35056
35057         if(g.autoWidth){
35058             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35059         }
35060
35061         var s = this.scroller;
35062
35063         var csize = c.getSize(true);
35064
35065         this.el.setSize(csize.width, csize.height);
35066
35067         this.headerPanel.setWidth(csize.width);
35068         this.footerPanel.setWidth(csize.width);
35069
35070         var hdHeight = this.mainHd.getHeight();
35071         var vw = csize.width;
35072         var vh = csize.height - (tbh + bbh);
35073
35074         s.setSize(vw, vh);
35075
35076         var bt = this.getBodyTable();
35077         
35078         if(cm.getLockedCount() == cm.config.length){
35079             bt = this.getLockedTable();
35080         }
35081         
35082         var ltWidth = hasLock ?
35083                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35084
35085         var scrollHeight = bt.offsetHeight;
35086         var scrollWidth = ltWidth + bt.offsetWidth;
35087         var vscroll = false, hscroll = false;
35088
35089         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35090
35091         var lw = this.lockedWrap, mw = this.mainWrap;
35092         var lb = this.lockedBody, mb = this.mainBody;
35093
35094         setTimeout(function(){
35095             var t = s.dom.offsetTop;
35096             var w = s.dom.clientWidth,
35097                 h = s.dom.clientHeight;
35098
35099             lw.setTop(t);
35100             lw.setSize(ltWidth, h);
35101
35102             mw.setLeftTop(ltWidth, t);
35103             mw.setSize(w-ltWidth, h);
35104
35105             lb.setHeight(h-hdHeight);
35106             mb.setHeight(h-hdHeight);
35107
35108             if(is2ndPass !== true && !gv.userResized && expandCol){
35109                 // high speed resize without full column calculation
35110                 
35111                 var ci = cm.getIndexById(expandCol);
35112                 if (ci < 0) {
35113                     ci = cm.findColumnIndex(expandCol);
35114                 }
35115                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35116                 var expandId = cm.getColumnId(ci);
35117                 var  tw = cm.getTotalWidth(false);
35118                 var currentWidth = cm.getColumnWidth(ci);
35119                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35120                 if(currentWidth != cw){
35121                     cm.setColumnWidth(ci, cw, true);
35122                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35123                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35124                     gv.updateSplitters();
35125                     gv.layout(false, true);
35126                 }
35127             }
35128
35129             if(initialRender){
35130                 lw.show();
35131                 mw.show();
35132             }
35133             //c.endMeasure();
35134         }, 10);
35135     },
35136
35137     onWindowResize : function(){
35138         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35139             return;
35140         }
35141         this.layout();
35142     },
35143
35144     appendFooter : function(parentEl){
35145         return null;
35146     },
35147
35148     sortAscText : "Sort Ascending",
35149     sortDescText : "Sort Descending",
35150     lockText : "Lock Column",
35151     unlockText : "Unlock Column",
35152     columnsText : "Columns",
35153  
35154     columnsWiderText : "Wider",
35155     columnsNarrowText : "Thinner"
35156 });
35157
35158
35159 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35160     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35161     this.proxy.el.addClass('x-grid3-col-dd');
35162 };
35163
35164 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35165     handleMouseDown : function(e){
35166
35167     },
35168
35169     callHandleMouseDown : function(e){
35170         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35171     }
35172 });
35173 /*
35174  * Based on:
35175  * Ext JS Library 1.1.1
35176  * Copyright(c) 2006-2007, Ext JS, LLC.
35177  *
35178  * Originally Released Under LGPL - original licence link has changed is not relivant.
35179  *
35180  * Fork - LGPL
35181  * <script type="text/javascript">
35182  */
35183  
35184 // private
35185 // This is a support class used internally by the Grid components
35186 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35187     this.grid = grid;
35188     this.view = grid.getView();
35189     this.proxy = this.view.resizeProxy;
35190     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35191         "gridSplitters" + this.grid.getGridEl().id, {
35192         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35193     });
35194     this.setHandleElId(Roo.id(hd));
35195     this.setOuterHandleElId(Roo.id(hd2));
35196     this.scroll = false;
35197 };
35198 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35199     fly: Roo.Element.fly,
35200
35201     b4StartDrag : function(x, y){
35202         this.view.headersDisabled = true;
35203         this.proxy.setHeight(this.view.mainWrap.getHeight());
35204         var w = this.cm.getColumnWidth(this.cellIndex);
35205         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35206         this.resetConstraints();
35207         this.setXConstraint(minw, 1000);
35208         this.setYConstraint(0, 0);
35209         this.minX = x - minw;
35210         this.maxX = x + 1000;
35211         this.startPos = x;
35212         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35213     },
35214
35215
35216     handleMouseDown : function(e){
35217         ev = Roo.EventObject.setEvent(e);
35218         var t = this.fly(ev.getTarget());
35219         if(t.hasClass("x-grid-split")){
35220             this.cellIndex = this.view.getCellIndex(t.dom);
35221             this.split = t.dom;
35222             this.cm = this.grid.colModel;
35223             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35224                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35225             }
35226         }
35227     },
35228
35229     endDrag : function(e){
35230         this.view.headersDisabled = false;
35231         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35232         var diff = endX - this.startPos;
35233         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35234     },
35235
35236     autoOffset : function(){
35237         this.setDelta(0,0);
35238     }
35239 });/*
35240  * Based on:
35241  * Ext JS Library 1.1.1
35242  * Copyright(c) 2006-2007, Ext JS, LLC.
35243  *
35244  * Originally Released Under LGPL - original licence link has changed is not relivant.
35245  *
35246  * Fork - LGPL
35247  * <script type="text/javascript">
35248  */
35249  
35250 // private
35251 // This is a support class used internally by the Grid components
35252 Roo.grid.GridDragZone = function(grid, config){
35253     this.view = grid.getView();
35254     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35255     if(this.view.lockedBody){
35256         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35257         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35258     }
35259     this.scroll = false;
35260     this.grid = grid;
35261     this.ddel = document.createElement('div');
35262     this.ddel.className = 'x-grid-dd-wrap';
35263 };
35264
35265 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35266     ddGroup : "GridDD",
35267
35268     getDragData : function(e){
35269         var t = Roo.lib.Event.getTarget(e);
35270         var rowIndex = this.view.findRowIndex(t);
35271         var sm = this.grid.selModel;
35272             
35273         //Roo.log(rowIndex);
35274         
35275         if (sm.getSelectedCell) {
35276             // cell selection..
35277             if (!sm.getSelectedCell()) {
35278                 return false;
35279             }
35280             if (rowIndex != sm.getSelectedCell()[0]) {
35281                 return false;
35282             }
35283         
35284         }
35285         
35286         if(rowIndex !== false){
35287             
35288             // if editorgrid.. 
35289             
35290             
35291             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35292                
35293             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35294               //  
35295             //}
35296             if (e.hasModifier()){
35297                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35298             }
35299             
35300             Roo.log("getDragData");
35301             
35302             return {
35303                 grid: this.grid,
35304                 ddel: this.ddel,
35305                 rowIndex: rowIndex,
35306                 selections:sm.getSelections ? sm.getSelections() : (
35307                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35308                 )
35309             };
35310         }
35311         return false;
35312     },
35313
35314     onInitDrag : function(e){
35315         var data = this.dragData;
35316         this.ddel.innerHTML = this.grid.getDragDropText();
35317         this.proxy.update(this.ddel);
35318         // fire start drag?
35319     },
35320
35321     afterRepair : function(){
35322         this.dragging = false;
35323     },
35324
35325     getRepairXY : function(e, data){
35326         return false;
35327     },
35328
35329     onEndDrag : function(data, e){
35330         // fire end drag?
35331     },
35332
35333     onValidDrop : function(dd, e, id){
35334         // fire drag drop?
35335         this.hideProxy();
35336     },
35337
35338     beforeInvalidDrop : function(e, id){
35339
35340     }
35341 });/*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351  
35352
35353 /**
35354  * @class Roo.grid.ColumnModel
35355  * @extends Roo.util.Observable
35356  * This is the default implementation of a ColumnModel used by the Grid. It defines
35357  * the columns in the grid.
35358  * <br>Usage:<br>
35359  <pre><code>
35360  var colModel = new Roo.grid.ColumnModel([
35361         {header: "Ticker", width: 60, sortable: true, locked: true},
35362         {header: "Company Name", width: 150, sortable: true},
35363         {header: "Market Cap.", width: 100, sortable: true},
35364         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35365         {header: "Employees", width: 100, sortable: true, resizable: false}
35366  ]);
35367  </code></pre>
35368  * <p>
35369  
35370  * The config options listed for this class are options which may appear in each
35371  * individual column definition.
35372  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35373  * @constructor
35374  * @param {Object} config An Array of column config objects. See this class's
35375  * config objects for details.
35376 */
35377 Roo.grid.ColumnModel = function(config){
35378         /**
35379      * The config passed into the constructor
35380      */
35381     this.config = config;
35382     this.lookup = {};
35383
35384     // if no id, create one
35385     // if the column does not have a dataIndex mapping,
35386     // map it to the order it is in the config
35387     for(var i = 0, len = config.length; i < len; i++){
35388         var c = config[i];
35389         if(typeof c.dataIndex == "undefined"){
35390             c.dataIndex = i;
35391         }
35392         if(typeof c.renderer == "string"){
35393             c.renderer = Roo.util.Format[c.renderer];
35394         }
35395         if(typeof c.id == "undefined"){
35396             c.id = Roo.id();
35397         }
35398         if(c.editor && c.editor.xtype){
35399             c.editor  = Roo.factory(c.editor, Roo.grid);
35400         }
35401         if(c.editor && c.editor.isFormField){
35402             c.editor = new Roo.grid.GridEditor(c.editor);
35403         }
35404         this.lookup[c.id] = c;
35405     }
35406
35407     /**
35408      * The width of columns which have no width specified (defaults to 100)
35409      * @type Number
35410      */
35411     this.defaultWidth = 100;
35412
35413     /**
35414      * Default sortable of columns which have no sortable specified (defaults to false)
35415      * @type Boolean
35416      */
35417     this.defaultSortable = false;
35418
35419     this.addEvents({
35420         /**
35421              * @event widthchange
35422              * Fires when the width of a column changes.
35423              * @param {ColumnModel} this
35424              * @param {Number} columnIndex The column index
35425              * @param {Number} newWidth The new width
35426              */
35427             "widthchange": true,
35428         /**
35429              * @event headerchange
35430              * Fires when the text of a header changes.
35431              * @param {ColumnModel} this
35432              * @param {Number} columnIndex The column index
35433              * @param {Number} newText The new header text
35434              */
35435             "headerchange": true,
35436         /**
35437              * @event hiddenchange
35438              * Fires when a column is hidden or "unhidden".
35439              * @param {ColumnModel} this
35440              * @param {Number} columnIndex The column index
35441              * @param {Boolean} hidden true if hidden, false otherwise
35442              */
35443             "hiddenchange": true,
35444             /**
35445          * @event columnmoved
35446          * Fires when a column is moved.
35447          * @param {ColumnModel} this
35448          * @param {Number} oldIndex
35449          * @param {Number} newIndex
35450          */
35451         "columnmoved" : true,
35452         /**
35453          * @event columlockchange
35454          * Fires when a column's locked state is changed
35455          * @param {ColumnModel} this
35456          * @param {Number} colIndex
35457          * @param {Boolean} locked true if locked
35458          */
35459         "columnlockchange" : true
35460     });
35461     Roo.grid.ColumnModel.superclass.constructor.call(this);
35462 };
35463 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35464     /**
35465      * @cfg {String} header The header text to display in the Grid view.
35466      */
35467     /**
35468      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35469      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35470      * specified, the column's index is used as an index into the Record's data Array.
35471      */
35472     /**
35473      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35474      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35475      */
35476     /**
35477      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35478      * Defaults to the value of the {@link #defaultSortable} property.
35479      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35480      */
35481     /**
35482      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35483      */
35484     /**
35485      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35486      */
35487     /**
35488      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35489      */
35490     /**
35491      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35492      */
35493     /**
35494      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35495      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35496      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35497      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35498      */
35499        /**
35500      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35501      */
35502     /**
35503      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35504      */
35505     /**
35506      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35507      */
35508     /**
35509      * @cfg {String} cursor (Optional)
35510      */
35511     /**
35512      * @cfg {String} tooltip (Optional)
35513      */
35514     /**
35515      * @cfg {Number} xs (Optional)
35516      */
35517     /**
35518      * @cfg {Number} sm (Optional)
35519      */
35520     /**
35521      * @cfg {Number} md (Optional)
35522      */
35523     /**
35524      * @cfg {Number} lg (Optional)
35525      */
35526     /**
35527      * Returns the id of the column at the specified index.
35528      * @param {Number} index The column index
35529      * @return {String} the id
35530      */
35531     getColumnId : function(index){
35532         return this.config[index].id;
35533     },
35534
35535     /**
35536      * Returns the column for a specified id.
35537      * @param {String} id The column id
35538      * @return {Object} the column
35539      */
35540     getColumnById : function(id){
35541         return this.lookup[id];
35542     },
35543
35544     
35545     /**
35546      * Returns the column for a specified dataIndex.
35547      * @param {String} dataIndex The column dataIndex
35548      * @return {Object|Boolean} the column or false if not found
35549      */
35550     getColumnByDataIndex: function(dataIndex){
35551         var index = this.findColumnIndex(dataIndex);
35552         return index > -1 ? this.config[index] : false;
35553     },
35554     
35555     /**
35556      * Returns the index for a specified column id.
35557      * @param {String} id The column id
35558      * @return {Number} the index, or -1 if not found
35559      */
35560     getIndexById : function(id){
35561         for(var i = 0, len = this.config.length; i < len; i++){
35562             if(this.config[i].id == id){
35563                 return i;
35564             }
35565         }
35566         return -1;
35567     },
35568     
35569     /**
35570      * Returns the index for a specified column dataIndex.
35571      * @param {String} dataIndex The column dataIndex
35572      * @return {Number} the index, or -1 if not found
35573      */
35574     
35575     findColumnIndex : function(dataIndex){
35576         for(var i = 0, len = this.config.length; i < len; i++){
35577             if(this.config[i].dataIndex == dataIndex){
35578                 return i;
35579             }
35580         }
35581         return -1;
35582     },
35583     
35584     
35585     moveColumn : function(oldIndex, newIndex){
35586         var c = this.config[oldIndex];
35587         this.config.splice(oldIndex, 1);
35588         this.config.splice(newIndex, 0, c);
35589         this.dataMap = null;
35590         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35591     },
35592
35593     isLocked : function(colIndex){
35594         return this.config[colIndex].locked === true;
35595     },
35596
35597     setLocked : function(colIndex, value, suppressEvent){
35598         if(this.isLocked(colIndex) == value){
35599             return;
35600         }
35601         this.config[colIndex].locked = value;
35602         if(!suppressEvent){
35603             this.fireEvent("columnlockchange", this, colIndex, value);
35604         }
35605     },
35606
35607     getTotalLockedWidth : function(){
35608         var totalWidth = 0;
35609         for(var i = 0; i < this.config.length; i++){
35610             if(this.isLocked(i) && !this.isHidden(i)){
35611                 this.totalWidth += this.getColumnWidth(i);
35612             }
35613         }
35614         return totalWidth;
35615     },
35616
35617     getLockedCount : function(){
35618         for(var i = 0, len = this.config.length; i < len; i++){
35619             if(!this.isLocked(i)){
35620                 return i;
35621             }
35622         }
35623         
35624         return this.config.length;
35625     },
35626
35627     /**
35628      * Returns the number of columns.
35629      * @return {Number}
35630      */
35631     getColumnCount : function(visibleOnly){
35632         if(visibleOnly === true){
35633             var c = 0;
35634             for(var i = 0, len = this.config.length; i < len; i++){
35635                 if(!this.isHidden(i)){
35636                     c++;
35637                 }
35638             }
35639             return c;
35640         }
35641         return this.config.length;
35642     },
35643
35644     /**
35645      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35646      * @param {Function} fn
35647      * @param {Object} scope (optional)
35648      * @return {Array} result
35649      */
35650     getColumnsBy : function(fn, scope){
35651         var r = [];
35652         for(var i = 0, len = this.config.length; i < len; i++){
35653             var c = this.config[i];
35654             if(fn.call(scope||this, c, i) === true){
35655                 r[r.length] = c;
35656             }
35657         }
35658         return r;
35659     },
35660
35661     /**
35662      * Returns true if the specified column is sortable.
35663      * @param {Number} col The column index
35664      * @return {Boolean}
35665      */
35666     isSortable : function(col){
35667         if(typeof this.config[col].sortable == "undefined"){
35668             return this.defaultSortable;
35669         }
35670         return this.config[col].sortable;
35671     },
35672
35673     /**
35674      * Returns the rendering (formatting) function defined for the column.
35675      * @param {Number} col The column index.
35676      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35677      */
35678     getRenderer : function(col){
35679         if(!this.config[col].renderer){
35680             return Roo.grid.ColumnModel.defaultRenderer;
35681         }
35682         return this.config[col].renderer;
35683     },
35684
35685     /**
35686      * Sets the rendering (formatting) function for a column.
35687      * @param {Number} col The column index
35688      * @param {Function} fn The function to use to process the cell's raw data
35689      * to return HTML markup for the grid view. The render function is called with
35690      * the following parameters:<ul>
35691      * <li>Data value.</li>
35692      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35693      * <li>css A CSS style string to apply to the table cell.</li>
35694      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35695      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35696      * <li>Row index</li>
35697      * <li>Column index</li>
35698      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35699      */
35700     setRenderer : function(col, fn){
35701         this.config[col].renderer = fn;
35702     },
35703
35704     /**
35705      * Returns the width for the specified column.
35706      * @param {Number} col The column index
35707      * @return {Number}
35708      */
35709     getColumnWidth : function(col){
35710         return this.config[col].width * 1 || this.defaultWidth;
35711     },
35712
35713     /**
35714      * Sets the width for a column.
35715      * @param {Number} col The column index
35716      * @param {Number} width The new width
35717      */
35718     setColumnWidth : function(col, width, suppressEvent){
35719         this.config[col].width = width;
35720         this.totalWidth = null;
35721         if(!suppressEvent){
35722              this.fireEvent("widthchange", this, col, width);
35723         }
35724     },
35725
35726     /**
35727      * Returns the total width of all columns.
35728      * @param {Boolean} includeHidden True to include hidden column widths
35729      * @return {Number}
35730      */
35731     getTotalWidth : function(includeHidden){
35732         if(!this.totalWidth){
35733             this.totalWidth = 0;
35734             for(var i = 0, len = this.config.length; i < len; i++){
35735                 if(includeHidden || !this.isHidden(i)){
35736                     this.totalWidth += this.getColumnWidth(i);
35737                 }
35738             }
35739         }
35740         return this.totalWidth;
35741     },
35742
35743     /**
35744      * Returns the header for the specified column.
35745      * @param {Number} col The column index
35746      * @return {String}
35747      */
35748     getColumnHeader : function(col){
35749         return this.config[col].header;
35750     },
35751
35752     /**
35753      * Sets the header for a column.
35754      * @param {Number} col The column index
35755      * @param {String} header The new header
35756      */
35757     setColumnHeader : function(col, header){
35758         this.config[col].header = header;
35759         this.fireEvent("headerchange", this, col, header);
35760     },
35761
35762     /**
35763      * Returns the tooltip for the specified column.
35764      * @param {Number} col The column index
35765      * @return {String}
35766      */
35767     getColumnTooltip : function(col){
35768             return this.config[col].tooltip;
35769     },
35770     /**
35771      * Sets the tooltip for a column.
35772      * @param {Number} col The column index
35773      * @param {String} tooltip The new tooltip
35774      */
35775     setColumnTooltip : function(col, tooltip){
35776             this.config[col].tooltip = tooltip;
35777     },
35778
35779     /**
35780      * Returns the dataIndex for the specified column.
35781      * @param {Number} col The column index
35782      * @return {Number}
35783      */
35784     getDataIndex : function(col){
35785         return this.config[col].dataIndex;
35786     },
35787
35788     /**
35789      * Sets the dataIndex for a column.
35790      * @param {Number} col The column index
35791      * @param {Number} dataIndex The new dataIndex
35792      */
35793     setDataIndex : function(col, dataIndex){
35794         this.config[col].dataIndex = dataIndex;
35795     },
35796
35797     
35798     
35799     /**
35800      * Returns true if the cell is editable.
35801      * @param {Number} colIndex The column index
35802      * @param {Number} rowIndex The row index - this is nto actually used..?
35803      * @return {Boolean}
35804      */
35805     isCellEditable : function(colIndex, rowIndex){
35806         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35807     },
35808
35809     /**
35810      * Returns the editor defined for the cell/column.
35811      * return false or null to disable editing.
35812      * @param {Number} colIndex The column index
35813      * @param {Number} rowIndex The row index
35814      * @return {Object}
35815      */
35816     getCellEditor : function(colIndex, rowIndex){
35817         return this.config[colIndex].editor;
35818     },
35819
35820     /**
35821      * Sets if a column is editable.
35822      * @param {Number} col The column index
35823      * @param {Boolean} editable True if the column is editable
35824      */
35825     setEditable : function(col, editable){
35826         this.config[col].editable = editable;
35827     },
35828
35829
35830     /**
35831      * Returns true if the column is hidden.
35832      * @param {Number} colIndex The column index
35833      * @return {Boolean}
35834      */
35835     isHidden : function(colIndex){
35836         return this.config[colIndex].hidden;
35837     },
35838
35839
35840     /**
35841      * Returns true if the column width cannot be changed
35842      */
35843     isFixed : function(colIndex){
35844         return this.config[colIndex].fixed;
35845     },
35846
35847     /**
35848      * Returns true if the column can be resized
35849      * @return {Boolean}
35850      */
35851     isResizable : function(colIndex){
35852         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35853     },
35854     /**
35855      * Sets if a column is hidden.
35856      * @param {Number} colIndex The column index
35857      * @param {Boolean} hidden True if the column is hidden
35858      */
35859     setHidden : function(colIndex, hidden){
35860         this.config[colIndex].hidden = hidden;
35861         this.totalWidth = null;
35862         this.fireEvent("hiddenchange", this, colIndex, hidden);
35863     },
35864
35865     /**
35866      * Sets the editor for a column.
35867      * @param {Number} col The column index
35868      * @param {Object} editor The editor object
35869      */
35870     setEditor : function(col, editor){
35871         this.config[col].editor = editor;
35872     }
35873 });
35874
35875 Roo.grid.ColumnModel.defaultRenderer = function(value)
35876 {
35877     if(typeof value == "object") {
35878         return value;
35879     }
35880         if(typeof value == "string" && value.length < 1){
35881             return "&#160;";
35882         }
35883     
35884         return String.format("{0}", value);
35885 };
35886
35887 // Alias for backwards compatibility
35888 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35889 /*
35890  * Based on:
35891  * Ext JS Library 1.1.1
35892  * Copyright(c) 2006-2007, Ext JS, LLC.
35893  *
35894  * Originally Released Under LGPL - original licence link has changed is not relivant.
35895  *
35896  * Fork - LGPL
35897  * <script type="text/javascript">
35898  */
35899
35900 /**
35901  * @class Roo.grid.AbstractSelectionModel
35902  * @extends Roo.util.Observable
35903  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35904  * implemented by descendant classes.  This class should not be directly instantiated.
35905  * @constructor
35906  */
35907 Roo.grid.AbstractSelectionModel = function(){
35908     this.locked = false;
35909     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35910 };
35911
35912 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35913     /** @ignore Called by the grid automatically. Do not call directly. */
35914     init : function(grid){
35915         this.grid = grid;
35916         this.initEvents();
35917     },
35918
35919     /**
35920      * Locks the selections.
35921      */
35922     lock : function(){
35923         this.locked = true;
35924     },
35925
35926     /**
35927      * Unlocks the selections.
35928      */
35929     unlock : function(){
35930         this.locked = false;
35931     },
35932
35933     /**
35934      * Returns true if the selections are locked.
35935      * @return {Boolean}
35936      */
35937     isLocked : function(){
35938         return this.locked;
35939     }
35940 });/*
35941  * Based on:
35942  * Ext JS Library 1.1.1
35943  * Copyright(c) 2006-2007, Ext JS, LLC.
35944  *
35945  * Originally Released Under LGPL - original licence link has changed is not relivant.
35946  *
35947  * Fork - LGPL
35948  * <script type="text/javascript">
35949  */
35950 /**
35951  * @extends Roo.grid.AbstractSelectionModel
35952  * @class Roo.grid.RowSelectionModel
35953  * The default SelectionModel used by {@link Roo.grid.Grid}.
35954  * It supports multiple selections and keyboard selection/navigation. 
35955  * @constructor
35956  * @param {Object} config
35957  */
35958 Roo.grid.RowSelectionModel = function(config){
35959     Roo.apply(this, config);
35960     this.selections = new Roo.util.MixedCollection(false, function(o){
35961         return o.id;
35962     });
35963
35964     this.last = false;
35965     this.lastActive = false;
35966
35967     this.addEvents({
35968         /**
35969              * @event selectionchange
35970              * Fires when the selection changes
35971              * @param {SelectionModel} this
35972              */
35973             "selectionchange" : true,
35974         /**
35975              * @event afterselectionchange
35976              * Fires after the selection changes (eg. by key press or clicking)
35977              * @param {SelectionModel} this
35978              */
35979             "afterselectionchange" : true,
35980         /**
35981              * @event beforerowselect
35982              * Fires when a row is selected being selected, return false to cancel.
35983              * @param {SelectionModel} this
35984              * @param {Number} rowIndex The selected index
35985              * @param {Boolean} keepExisting False if other selections will be cleared
35986              */
35987             "beforerowselect" : true,
35988         /**
35989              * @event rowselect
35990              * Fires when a row is selected.
35991              * @param {SelectionModel} this
35992              * @param {Number} rowIndex The selected index
35993              * @param {Roo.data.Record} r The record
35994              */
35995             "rowselect" : true,
35996         /**
35997              * @event rowdeselect
35998              * Fires when a row is deselected.
35999              * @param {SelectionModel} this
36000              * @param {Number} rowIndex The selected index
36001              */
36002         "rowdeselect" : true
36003     });
36004     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36005     this.locked = false;
36006 };
36007
36008 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36009     /**
36010      * @cfg {Boolean} singleSelect
36011      * True to allow selection of only one row at a time (defaults to false)
36012      */
36013     singleSelect : false,
36014
36015     // private
36016     initEvents : function(){
36017
36018         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36019             this.grid.on("mousedown", this.handleMouseDown, this);
36020         }else{ // allow click to work like normal
36021             this.grid.on("rowclick", this.handleDragableRowClick, this);
36022         }
36023
36024         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36025             "up" : function(e){
36026                 if(!e.shiftKey){
36027                     this.selectPrevious(e.shiftKey);
36028                 }else if(this.last !== false && this.lastActive !== false){
36029                     var last = this.last;
36030                     this.selectRange(this.last,  this.lastActive-1);
36031                     this.grid.getView().focusRow(this.lastActive);
36032                     if(last !== false){
36033                         this.last = last;
36034                     }
36035                 }else{
36036                     this.selectFirstRow();
36037                 }
36038                 this.fireEvent("afterselectionchange", this);
36039             },
36040             "down" : function(e){
36041                 if(!e.shiftKey){
36042                     this.selectNext(e.shiftKey);
36043                 }else if(this.last !== false && this.lastActive !== false){
36044                     var last = this.last;
36045                     this.selectRange(this.last,  this.lastActive+1);
36046                     this.grid.getView().focusRow(this.lastActive);
36047                     if(last !== false){
36048                         this.last = last;
36049                     }
36050                 }else{
36051                     this.selectFirstRow();
36052                 }
36053                 this.fireEvent("afterselectionchange", this);
36054             },
36055             scope: this
36056         });
36057
36058         var view = this.grid.view;
36059         view.on("refresh", this.onRefresh, this);
36060         view.on("rowupdated", this.onRowUpdated, this);
36061         view.on("rowremoved", this.onRemove, this);
36062     },
36063
36064     // private
36065     onRefresh : function(){
36066         var ds = this.grid.dataSource, i, v = this.grid.view;
36067         var s = this.selections;
36068         s.each(function(r){
36069             if((i = ds.indexOfId(r.id)) != -1){
36070                 v.onRowSelect(i);
36071                 s.add(ds.getAt(i)); // updating the selection relate data
36072             }else{
36073                 s.remove(r);
36074             }
36075         });
36076     },
36077
36078     // private
36079     onRemove : function(v, index, r){
36080         this.selections.remove(r);
36081     },
36082
36083     // private
36084     onRowUpdated : function(v, index, r){
36085         if(this.isSelected(r)){
36086             v.onRowSelect(index);
36087         }
36088     },
36089
36090     /**
36091      * Select records.
36092      * @param {Array} records The records to select
36093      * @param {Boolean} keepExisting (optional) True to keep existing selections
36094      */
36095     selectRecords : function(records, keepExisting){
36096         if(!keepExisting){
36097             this.clearSelections();
36098         }
36099         var ds = this.grid.dataSource;
36100         for(var i = 0, len = records.length; i < len; i++){
36101             this.selectRow(ds.indexOf(records[i]), true);
36102         }
36103     },
36104
36105     /**
36106      * Gets the number of selected rows.
36107      * @return {Number}
36108      */
36109     getCount : function(){
36110         return this.selections.length;
36111     },
36112
36113     /**
36114      * Selects the first row in the grid.
36115      */
36116     selectFirstRow : function(){
36117         this.selectRow(0);
36118     },
36119
36120     /**
36121      * Select the last row.
36122      * @param {Boolean} keepExisting (optional) True to keep existing selections
36123      */
36124     selectLastRow : function(keepExisting){
36125         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36126     },
36127
36128     /**
36129      * Selects the row immediately following the last selected row.
36130      * @param {Boolean} keepExisting (optional) True to keep existing selections
36131      */
36132     selectNext : function(keepExisting){
36133         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36134             this.selectRow(this.last+1, keepExisting);
36135             this.grid.getView().focusRow(this.last);
36136         }
36137     },
36138
36139     /**
36140      * Selects the row that precedes the last selected row.
36141      * @param {Boolean} keepExisting (optional) True to keep existing selections
36142      */
36143     selectPrevious : function(keepExisting){
36144         if(this.last){
36145             this.selectRow(this.last-1, keepExisting);
36146             this.grid.getView().focusRow(this.last);
36147         }
36148     },
36149
36150     /**
36151      * Returns the selected records
36152      * @return {Array} Array of selected records
36153      */
36154     getSelections : function(){
36155         return [].concat(this.selections.items);
36156     },
36157
36158     /**
36159      * Returns the first selected record.
36160      * @return {Record}
36161      */
36162     getSelected : function(){
36163         return this.selections.itemAt(0);
36164     },
36165
36166
36167     /**
36168      * Clears all selections.
36169      */
36170     clearSelections : function(fast){
36171         if(this.locked) {
36172             return;
36173         }
36174         if(fast !== true){
36175             var ds = this.grid.dataSource;
36176             var s = this.selections;
36177             s.each(function(r){
36178                 this.deselectRow(ds.indexOfId(r.id));
36179             }, this);
36180             s.clear();
36181         }else{
36182             this.selections.clear();
36183         }
36184         this.last = false;
36185     },
36186
36187
36188     /**
36189      * Selects all rows.
36190      */
36191     selectAll : function(){
36192         if(this.locked) {
36193             return;
36194         }
36195         this.selections.clear();
36196         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36197             this.selectRow(i, true);
36198         }
36199     },
36200
36201     /**
36202      * Returns True if there is a selection.
36203      * @return {Boolean}
36204      */
36205     hasSelection : function(){
36206         return this.selections.length > 0;
36207     },
36208
36209     /**
36210      * Returns True if the specified row is selected.
36211      * @param {Number/Record} record The record or index of the record to check
36212      * @return {Boolean}
36213      */
36214     isSelected : function(index){
36215         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36216         return (r && this.selections.key(r.id) ? true : false);
36217     },
36218
36219     /**
36220      * Returns True if the specified record id is selected.
36221      * @param {String} id The id of record to check
36222      * @return {Boolean}
36223      */
36224     isIdSelected : function(id){
36225         return (this.selections.key(id) ? true : false);
36226     },
36227
36228     // private
36229     handleMouseDown : function(e, t){
36230         var view = this.grid.getView(), rowIndex;
36231         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36232             return;
36233         };
36234         if(e.shiftKey && this.last !== false){
36235             var last = this.last;
36236             this.selectRange(last, rowIndex, e.ctrlKey);
36237             this.last = last; // reset the last
36238             view.focusRow(rowIndex);
36239         }else{
36240             var isSelected = this.isSelected(rowIndex);
36241             if(e.button !== 0 && isSelected){
36242                 view.focusRow(rowIndex);
36243             }else if(e.ctrlKey && isSelected){
36244                 this.deselectRow(rowIndex);
36245             }else if(!isSelected){
36246                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36247                 view.focusRow(rowIndex);
36248             }
36249         }
36250         this.fireEvent("afterselectionchange", this);
36251     },
36252     // private
36253     handleDragableRowClick :  function(grid, rowIndex, e) 
36254     {
36255         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36256             this.selectRow(rowIndex, false);
36257             grid.view.focusRow(rowIndex);
36258              this.fireEvent("afterselectionchange", this);
36259         }
36260     },
36261     
36262     /**
36263      * Selects multiple rows.
36264      * @param {Array} rows Array of the indexes of the row to select
36265      * @param {Boolean} keepExisting (optional) True to keep existing selections
36266      */
36267     selectRows : function(rows, keepExisting){
36268         if(!keepExisting){
36269             this.clearSelections();
36270         }
36271         for(var i = 0, len = rows.length; i < len; i++){
36272             this.selectRow(rows[i], true);
36273         }
36274     },
36275
36276     /**
36277      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36278      * @param {Number} startRow The index of the first row in the range
36279      * @param {Number} endRow The index of the last row in the range
36280      * @param {Boolean} keepExisting (optional) True to retain existing selections
36281      */
36282     selectRange : function(startRow, endRow, keepExisting){
36283         if(this.locked) {
36284             return;
36285         }
36286         if(!keepExisting){
36287             this.clearSelections();
36288         }
36289         if(startRow <= endRow){
36290             for(var i = startRow; i <= endRow; i++){
36291                 this.selectRow(i, true);
36292             }
36293         }else{
36294             for(var i = startRow; i >= endRow; i--){
36295                 this.selectRow(i, true);
36296             }
36297         }
36298     },
36299
36300     /**
36301      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36302      * @param {Number} startRow The index of the first row in the range
36303      * @param {Number} endRow The index of the last row in the range
36304      */
36305     deselectRange : function(startRow, endRow, preventViewNotify){
36306         if(this.locked) {
36307             return;
36308         }
36309         for(var i = startRow; i <= endRow; i++){
36310             this.deselectRow(i, preventViewNotify);
36311         }
36312     },
36313
36314     /**
36315      * Selects a row.
36316      * @param {Number} row The index of the row to select
36317      * @param {Boolean} keepExisting (optional) True to keep existing selections
36318      */
36319     selectRow : function(index, keepExisting, preventViewNotify){
36320         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36321             return;
36322         }
36323         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36324             if(!keepExisting || this.singleSelect){
36325                 this.clearSelections();
36326             }
36327             var r = this.grid.dataSource.getAt(index);
36328             this.selections.add(r);
36329             this.last = this.lastActive = index;
36330             if(!preventViewNotify){
36331                 this.grid.getView().onRowSelect(index);
36332             }
36333             this.fireEvent("rowselect", this, index, r);
36334             this.fireEvent("selectionchange", this);
36335         }
36336     },
36337
36338     /**
36339      * Deselects a row.
36340      * @param {Number} row The index of the row to deselect
36341      */
36342     deselectRow : function(index, preventViewNotify){
36343         if(this.locked) {
36344             return;
36345         }
36346         if(this.last == index){
36347             this.last = false;
36348         }
36349         if(this.lastActive == index){
36350             this.lastActive = false;
36351         }
36352         var r = this.grid.dataSource.getAt(index);
36353         this.selections.remove(r);
36354         if(!preventViewNotify){
36355             this.grid.getView().onRowDeselect(index);
36356         }
36357         this.fireEvent("rowdeselect", this, index);
36358         this.fireEvent("selectionchange", this);
36359     },
36360
36361     // private
36362     restoreLast : function(){
36363         if(this._last){
36364             this.last = this._last;
36365         }
36366     },
36367
36368     // private
36369     acceptsNav : function(row, col, cm){
36370         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36371     },
36372
36373     // private
36374     onEditorKey : function(field, e){
36375         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36376         if(k == e.TAB){
36377             e.stopEvent();
36378             ed.completeEdit();
36379             if(e.shiftKey){
36380                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36381             }else{
36382                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36383             }
36384         }else if(k == e.ENTER && !e.ctrlKey){
36385             e.stopEvent();
36386             ed.completeEdit();
36387             if(e.shiftKey){
36388                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36389             }else{
36390                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36391             }
36392         }else if(k == e.ESC){
36393             ed.cancelEdit();
36394         }
36395         if(newCell){
36396             g.startEditing(newCell[0], newCell[1]);
36397         }
36398     }
36399 });/*
36400  * Based on:
36401  * Ext JS Library 1.1.1
36402  * Copyright(c) 2006-2007, Ext JS, LLC.
36403  *
36404  * Originally Released Under LGPL - original licence link has changed is not relivant.
36405  *
36406  * Fork - LGPL
36407  * <script type="text/javascript">
36408  */
36409 /**
36410  * @class Roo.grid.CellSelectionModel
36411  * @extends Roo.grid.AbstractSelectionModel
36412  * This class provides the basic implementation for cell selection in a grid.
36413  * @constructor
36414  * @param {Object} config The object containing the configuration of this model.
36415  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36416  */
36417 Roo.grid.CellSelectionModel = function(config){
36418     Roo.apply(this, config);
36419
36420     this.selection = null;
36421
36422     this.addEvents({
36423         /**
36424              * @event beforerowselect
36425              * Fires before a cell is selected.
36426              * @param {SelectionModel} this
36427              * @param {Number} rowIndex The selected row index
36428              * @param {Number} colIndex The selected cell index
36429              */
36430             "beforecellselect" : true,
36431         /**
36432              * @event cellselect
36433              * Fires when a cell is selected.
36434              * @param {SelectionModel} this
36435              * @param {Number} rowIndex The selected row index
36436              * @param {Number} colIndex The selected cell index
36437              */
36438             "cellselect" : true,
36439         /**
36440              * @event selectionchange
36441              * Fires when the active selection changes.
36442              * @param {SelectionModel} this
36443              * @param {Object} selection null for no selection or an object (o) with two properties
36444                 <ul>
36445                 <li>o.record: the record object for the row the selection is in</li>
36446                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36447                 </ul>
36448              */
36449             "selectionchange" : true,
36450         /**
36451              * @event tabend
36452              * Fires when the tab (or enter) was pressed on the last editable cell
36453              * You can use this to trigger add new row.
36454              * @param {SelectionModel} this
36455              */
36456             "tabend" : true,
36457          /**
36458              * @event beforeeditnext
36459              * Fires before the next editable sell is made active
36460              * You can use this to skip to another cell or fire the tabend
36461              *    if you set cell to false
36462              * @param {Object} eventdata object : { cell : [ row, col ] } 
36463              */
36464             "beforeeditnext" : true
36465     });
36466     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36467 };
36468
36469 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36470     
36471     enter_is_tab: false,
36472
36473     /** @ignore */
36474     initEvents : function(){
36475         this.grid.on("mousedown", this.handleMouseDown, this);
36476         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36477         var view = this.grid.view;
36478         view.on("refresh", this.onViewChange, this);
36479         view.on("rowupdated", this.onRowUpdated, this);
36480         view.on("beforerowremoved", this.clearSelections, this);
36481         view.on("beforerowsinserted", this.clearSelections, this);
36482         if(this.grid.isEditor){
36483             this.grid.on("beforeedit", this.beforeEdit,  this);
36484         }
36485     },
36486
36487         //private
36488     beforeEdit : function(e){
36489         this.select(e.row, e.column, false, true, e.record);
36490     },
36491
36492         //private
36493     onRowUpdated : function(v, index, r){
36494         if(this.selection && this.selection.record == r){
36495             v.onCellSelect(index, this.selection.cell[1]);
36496         }
36497     },
36498
36499         //private
36500     onViewChange : function(){
36501         this.clearSelections(true);
36502     },
36503
36504         /**
36505          * Returns the currently selected cell,.
36506          * @return {Array} The selected cell (row, column) or null if none selected.
36507          */
36508     getSelectedCell : function(){
36509         return this.selection ? this.selection.cell : null;
36510     },
36511
36512     /**
36513      * Clears all selections.
36514      * @param {Boolean} true to prevent the gridview from being notified about the change.
36515      */
36516     clearSelections : function(preventNotify){
36517         var s = this.selection;
36518         if(s){
36519             if(preventNotify !== true){
36520                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36521             }
36522             this.selection = null;
36523             this.fireEvent("selectionchange", this, null);
36524         }
36525     },
36526
36527     /**
36528      * Returns true if there is a selection.
36529      * @return {Boolean}
36530      */
36531     hasSelection : function(){
36532         return this.selection ? true : false;
36533     },
36534
36535     /** @ignore */
36536     handleMouseDown : function(e, t){
36537         var v = this.grid.getView();
36538         if(this.isLocked()){
36539             return;
36540         };
36541         var row = v.findRowIndex(t);
36542         var cell = v.findCellIndex(t);
36543         if(row !== false && cell !== false){
36544             this.select(row, cell);
36545         }
36546     },
36547
36548     /**
36549      * Selects a cell.
36550      * @param {Number} rowIndex
36551      * @param {Number} collIndex
36552      */
36553     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36554         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36555             this.clearSelections();
36556             r = r || this.grid.dataSource.getAt(rowIndex);
36557             this.selection = {
36558                 record : r,
36559                 cell : [rowIndex, colIndex]
36560             };
36561             if(!preventViewNotify){
36562                 var v = this.grid.getView();
36563                 v.onCellSelect(rowIndex, colIndex);
36564                 if(preventFocus !== true){
36565                     v.focusCell(rowIndex, colIndex);
36566                 }
36567             }
36568             this.fireEvent("cellselect", this, rowIndex, colIndex);
36569             this.fireEvent("selectionchange", this, this.selection);
36570         }
36571     },
36572
36573         //private
36574     isSelectable : function(rowIndex, colIndex, cm){
36575         return !cm.isHidden(colIndex);
36576     },
36577
36578     /** @ignore */
36579     handleKeyDown : function(e){
36580         //Roo.log('Cell Sel Model handleKeyDown');
36581         if(!e.isNavKeyPress()){
36582             return;
36583         }
36584         var g = this.grid, s = this.selection;
36585         if(!s){
36586             e.stopEvent();
36587             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36588             if(cell){
36589                 this.select(cell[0], cell[1]);
36590             }
36591             return;
36592         }
36593         var sm = this;
36594         var walk = function(row, col, step){
36595             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36596         };
36597         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36598         var newCell;
36599
36600       
36601
36602         switch(k){
36603             case e.TAB:
36604                 // handled by onEditorKey
36605                 if (g.isEditor && g.editing) {
36606                     return;
36607                 }
36608                 if(e.shiftKey) {
36609                     newCell = walk(r, c-1, -1);
36610                 } else {
36611                     newCell = walk(r, c+1, 1);
36612                 }
36613                 break;
36614             
36615             case e.DOWN:
36616                newCell = walk(r+1, c, 1);
36617                 break;
36618             
36619             case e.UP:
36620                 newCell = walk(r-1, c, -1);
36621                 break;
36622             
36623             case e.RIGHT:
36624                 newCell = walk(r, c+1, 1);
36625                 break;
36626             
36627             case e.LEFT:
36628                 newCell = walk(r, c-1, -1);
36629                 break;
36630             
36631             case e.ENTER:
36632                 
36633                 if(g.isEditor && !g.editing){
36634                    g.startEditing(r, c);
36635                    e.stopEvent();
36636                    return;
36637                 }
36638                 
36639                 
36640              break;
36641         };
36642         if(newCell){
36643             this.select(newCell[0], newCell[1]);
36644             e.stopEvent();
36645             
36646         }
36647     },
36648
36649     acceptsNav : function(row, col, cm){
36650         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36651     },
36652     /**
36653      * Selects a cell.
36654      * @param {Number} field (not used) - as it's normally used as a listener
36655      * @param {Number} e - event - fake it by using
36656      *
36657      * var e = Roo.EventObjectImpl.prototype;
36658      * e.keyCode = e.TAB
36659      *
36660      * 
36661      */
36662     onEditorKey : function(field, e){
36663         
36664         var k = e.getKey(),
36665             newCell,
36666             g = this.grid,
36667             ed = g.activeEditor,
36668             forward = false;
36669         ///Roo.log('onEditorKey' + k);
36670         
36671         
36672         if (this.enter_is_tab && k == e.ENTER) {
36673             k = e.TAB;
36674         }
36675         
36676         if(k == e.TAB){
36677             if(e.shiftKey){
36678                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36679             }else{
36680                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36681                 forward = true;
36682             }
36683             
36684             e.stopEvent();
36685             
36686         } else if(k == e.ENTER &&  !e.ctrlKey){
36687             ed.completeEdit();
36688             e.stopEvent();
36689             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36690         
36691                 } else if(k == e.ESC){
36692             ed.cancelEdit();
36693         }
36694                 
36695         if (newCell) {
36696             var ecall = { cell : newCell, forward : forward };
36697             this.fireEvent('beforeeditnext', ecall );
36698             newCell = ecall.cell;
36699                         forward = ecall.forward;
36700         }
36701                 
36702         if(newCell){
36703             //Roo.log('next cell after edit');
36704             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36705         } else if (forward) {
36706             // tabbed past last
36707             this.fireEvent.defer(100, this, ['tabend',this]);
36708         }
36709     }
36710 });/*
36711  * Based on:
36712  * Ext JS Library 1.1.1
36713  * Copyright(c) 2006-2007, Ext JS, LLC.
36714  *
36715  * Originally Released Under LGPL - original licence link has changed is not relivant.
36716  *
36717  * Fork - LGPL
36718  * <script type="text/javascript">
36719  */
36720  
36721 /**
36722  * @class Roo.grid.EditorGrid
36723  * @extends Roo.grid.Grid
36724  * Class for creating and editable grid.
36725  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36726  * The container MUST have some type of size defined for the grid to fill. The container will be 
36727  * automatically set to position relative if it isn't already.
36728  * @param {Object} dataSource The data model to bind to
36729  * @param {Object} colModel The column model with info about this grid's columns
36730  */
36731 Roo.grid.EditorGrid = function(container, config){
36732     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36733     this.getGridEl().addClass("xedit-grid");
36734
36735     if(!this.selModel){
36736         this.selModel = new Roo.grid.CellSelectionModel();
36737     }
36738
36739     this.activeEditor = null;
36740
36741         this.addEvents({
36742             /**
36743              * @event beforeedit
36744              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36745              * <ul style="padding:5px;padding-left:16px;">
36746              * <li>grid - This grid</li>
36747              * <li>record - The record being edited</li>
36748              * <li>field - The field name being edited</li>
36749              * <li>value - The value for the field being edited.</li>
36750              * <li>row - The grid row index</li>
36751              * <li>column - The grid column index</li>
36752              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36753              * </ul>
36754              * @param {Object} e An edit event (see above for description)
36755              */
36756             "beforeedit" : true,
36757             /**
36758              * @event afteredit
36759              * Fires after a cell is edited. <br />
36760              * <ul style="padding:5px;padding-left:16px;">
36761              * <li>grid - This grid</li>
36762              * <li>record - The record being edited</li>
36763              * <li>field - The field name being edited</li>
36764              * <li>value - The value being set</li>
36765              * <li>originalValue - The original value for the field, before the edit.</li>
36766              * <li>row - The grid row index</li>
36767              * <li>column - The grid column index</li>
36768              * </ul>
36769              * @param {Object} e An edit event (see above for description)
36770              */
36771             "afteredit" : true,
36772             /**
36773              * @event validateedit
36774              * Fires after a cell is edited, but before the value is set in the record. 
36775          * You can use this to modify the value being set in the field, Return false
36776              * to cancel the change. The edit event object has the following properties <br />
36777              * <ul style="padding:5px;padding-left:16px;">
36778          * <li>editor - This editor</li>
36779              * <li>grid - This grid</li>
36780              * <li>record - The record being edited</li>
36781              * <li>field - The field name being edited</li>
36782              * <li>value - The value being set</li>
36783              * <li>originalValue - The original value for the field, before the edit.</li>
36784              * <li>row - The grid row index</li>
36785              * <li>column - The grid column index</li>
36786              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36787              * </ul>
36788              * @param {Object} e An edit event (see above for description)
36789              */
36790             "validateedit" : true
36791         });
36792     this.on("bodyscroll", this.stopEditing,  this);
36793     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36794 };
36795
36796 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36797     /**
36798      * @cfg {Number} clicksToEdit
36799      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36800      */
36801     clicksToEdit: 2,
36802
36803     // private
36804     isEditor : true,
36805     // private
36806     trackMouseOver: false, // causes very odd FF errors
36807
36808     onCellDblClick : function(g, row, col){
36809         this.startEditing(row, col);
36810     },
36811
36812     onEditComplete : function(ed, value, startValue){
36813         this.editing = false;
36814         this.activeEditor = null;
36815         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36816         var r = ed.record;
36817         var field = this.colModel.getDataIndex(ed.col);
36818         var e = {
36819             grid: this,
36820             record: r,
36821             field: field,
36822             originalValue: startValue,
36823             value: value,
36824             row: ed.row,
36825             column: ed.col,
36826             cancel:false,
36827             editor: ed
36828         };
36829         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36830         cell.show();
36831           
36832         if(String(value) !== String(startValue)){
36833             
36834             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36835                 r.set(field, e.value);
36836                 // if we are dealing with a combo box..
36837                 // then we also set the 'name' colum to be the displayField
36838                 if (ed.field.displayField && ed.field.name) {
36839                     r.set(ed.field.name, ed.field.el.dom.value);
36840                 }
36841                 
36842                 delete e.cancel; //?? why!!!
36843                 this.fireEvent("afteredit", e);
36844             }
36845         } else {
36846             this.fireEvent("afteredit", e); // always fire it!
36847         }
36848         this.view.focusCell(ed.row, ed.col);
36849     },
36850
36851     /**
36852      * Starts editing the specified for the specified row/column
36853      * @param {Number} rowIndex
36854      * @param {Number} colIndex
36855      */
36856     startEditing : function(row, col){
36857         this.stopEditing();
36858         if(this.colModel.isCellEditable(col, row)){
36859             this.view.ensureVisible(row, col, true);
36860           
36861             var r = this.dataSource.getAt(row);
36862             var field = this.colModel.getDataIndex(col);
36863             var cell = Roo.get(this.view.getCell(row,col));
36864             var e = {
36865                 grid: this,
36866                 record: r,
36867                 field: field,
36868                 value: r.data[field],
36869                 row: row,
36870                 column: col,
36871                 cancel:false 
36872             };
36873             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36874                 this.editing = true;
36875                 var ed = this.colModel.getCellEditor(col, row);
36876                 
36877                 if (!ed) {
36878                     return;
36879                 }
36880                 if(!ed.rendered){
36881                     ed.render(ed.parentEl || document.body);
36882                 }
36883                 ed.field.reset();
36884                
36885                 cell.hide();
36886                 
36887                 (function(){ // complex but required for focus issues in safari, ie and opera
36888                     ed.row = row;
36889                     ed.col = col;
36890                     ed.record = r;
36891                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36892                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36893                     this.activeEditor = ed;
36894                     var v = r.data[field];
36895                     ed.startEdit(this.view.getCell(row, col), v);
36896                     // combo's with 'displayField and name set
36897                     if (ed.field.displayField && ed.field.name) {
36898                         ed.field.el.dom.value = r.data[ed.field.name];
36899                     }
36900                     
36901                     
36902                 }).defer(50, this);
36903             }
36904         }
36905     },
36906         
36907     /**
36908      * Stops any active editing
36909      */
36910     stopEditing : function(){
36911         if(this.activeEditor){
36912             this.activeEditor.completeEdit();
36913         }
36914         this.activeEditor = null;
36915     },
36916         
36917          /**
36918      * Called to get grid's drag proxy text, by default returns this.ddText.
36919      * @return {String}
36920      */
36921     getDragDropText : function(){
36922         var count = this.selModel.getSelectedCell() ? 1 : 0;
36923         return String.format(this.ddText, count, count == 1 ? '' : 's');
36924     }
36925         
36926 });/*
36927  * Based on:
36928  * Ext JS Library 1.1.1
36929  * Copyright(c) 2006-2007, Ext JS, LLC.
36930  *
36931  * Originally Released Under LGPL - original licence link has changed is not relivant.
36932  *
36933  * Fork - LGPL
36934  * <script type="text/javascript">
36935  */
36936
36937 // private - not really -- you end up using it !
36938 // This is a support class used internally by the Grid components
36939
36940 /**
36941  * @class Roo.grid.GridEditor
36942  * @extends Roo.Editor
36943  * Class for creating and editable grid elements.
36944  * @param {Object} config any settings (must include field)
36945  */
36946 Roo.grid.GridEditor = function(field, config){
36947     if (!config && field.field) {
36948         config = field;
36949         field = Roo.factory(config.field, Roo.form);
36950     }
36951     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36952     field.monitorTab = false;
36953 };
36954
36955 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36956     
36957     /**
36958      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36959      */
36960     
36961     alignment: "tl-tl",
36962     autoSize: "width",
36963     hideEl : false,
36964     cls: "x-small-editor x-grid-editor",
36965     shim:false,
36966     shadow:"frame"
36967 });/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977   
36978
36979   
36980 Roo.grid.PropertyRecord = Roo.data.Record.create([
36981     {name:'name',type:'string'},  'value'
36982 ]);
36983
36984
36985 Roo.grid.PropertyStore = function(grid, source){
36986     this.grid = grid;
36987     this.store = new Roo.data.Store({
36988         recordType : Roo.grid.PropertyRecord
36989     });
36990     this.store.on('update', this.onUpdate,  this);
36991     if(source){
36992         this.setSource(source);
36993     }
36994     Roo.grid.PropertyStore.superclass.constructor.call(this);
36995 };
36996
36997
36998
36999 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37000     setSource : function(o){
37001         this.source = o;
37002         this.store.removeAll();
37003         var data = [];
37004         for(var k in o){
37005             if(this.isEditableValue(o[k])){
37006                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37007             }
37008         }
37009         this.store.loadRecords({records: data}, {}, true);
37010     },
37011
37012     onUpdate : function(ds, record, type){
37013         if(type == Roo.data.Record.EDIT){
37014             var v = record.data['value'];
37015             var oldValue = record.modified['value'];
37016             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37017                 this.source[record.id] = v;
37018                 record.commit();
37019                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37020             }else{
37021                 record.reject();
37022             }
37023         }
37024     },
37025
37026     getProperty : function(row){
37027        return this.store.getAt(row);
37028     },
37029
37030     isEditableValue: function(val){
37031         if(val && val instanceof Date){
37032             return true;
37033         }else if(typeof val == 'object' || typeof val == 'function'){
37034             return false;
37035         }
37036         return true;
37037     },
37038
37039     setValue : function(prop, value){
37040         this.source[prop] = value;
37041         this.store.getById(prop).set('value', value);
37042     },
37043
37044     getSource : function(){
37045         return this.source;
37046     }
37047 });
37048
37049 Roo.grid.PropertyColumnModel = function(grid, store){
37050     this.grid = grid;
37051     var g = Roo.grid;
37052     g.PropertyColumnModel.superclass.constructor.call(this, [
37053         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37054         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37055     ]);
37056     this.store = store;
37057     this.bselect = Roo.DomHelper.append(document.body, {
37058         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37059             {tag: 'option', value: 'true', html: 'true'},
37060             {tag: 'option', value: 'false', html: 'false'}
37061         ]
37062     });
37063     Roo.id(this.bselect);
37064     var f = Roo.form;
37065     this.editors = {
37066         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37067         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37068         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37069         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37070         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37071     };
37072     this.renderCellDelegate = this.renderCell.createDelegate(this);
37073     this.renderPropDelegate = this.renderProp.createDelegate(this);
37074 };
37075
37076 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37077     
37078     
37079     nameText : 'Name',
37080     valueText : 'Value',
37081     
37082     dateFormat : 'm/j/Y',
37083     
37084     
37085     renderDate : function(dateVal){
37086         return dateVal.dateFormat(this.dateFormat);
37087     },
37088
37089     renderBool : function(bVal){
37090         return bVal ? 'true' : 'false';
37091     },
37092
37093     isCellEditable : function(colIndex, rowIndex){
37094         return colIndex == 1;
37095     },
37096
37097     getRenderer : function(col){
37098         return col == 1 ?
37099             this.renderCellDelegate : this.renderPropDelegate;
37100     },
37101
37102     renderProp : function(v){
37103         return this.getPropertyName(v);
37104     },
37105
37106     renderCell : function(val){
37107         var rv = val;
37108         if(val instanceof Date){
37109             rv = this.renderDate(val);
37110         }else if(typeof val == 'boolean'){
37111             rv = this.renderBool(val);
37112         }
37113         return Roo.util.Format.htmlEncode(rv);
37114     },
37115
37116     getPropertyName : function(name){
37117         var pn = this.grid.propertyNames;
37118         return pn && pn[name] ? pn[name] : name;
37119     },
37120
37121     getCellEditor : function(colIndex, rowIndex){
37122         var p = this.store.getProperty(rowIndex);
37123         var n = p.data['name'], val = p.data['value'];
37124         
37125         if(typeof(this.grid.customEditors[n]) == 'string'){
37126             return this.editors[this.grid.customEditors[n]];
37127         }
37128         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37129             return this.grid.customEditors[n];
37130         }
37131         if(val instanceof Date){
37132             return this.editors['date'];
37133         }else if(typeof val == 'number'){
37134             return this.editors['number'];
37135         }else if(typeof val == 'boolean'){
37136             return this.editors['boolean'];
37137         }else{
37138             return this.editors['string'];
37139         }
37140     }
37141 });
37142
37143 /**
37144  * @class Roo.grid.PropertyGrid
37145  * @extends Roo.grid.EditorGrid
37146  * This class represents the  interface of a component based property grid control.
37147  * <br><br>Usage:<pre><code>
37148  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37149       
37150  });
37151  // set any options
37152  grid.render();
37153  * </code></pre>
37154   
37155  * @constructor
37156  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37157  * The container MUST have some type of size defined for the grid to fill. The container will be
37158  * automatically set to position relative if it isn't already.
37159  * @param {Object} config A config object that sets properties on this grid.
37160  */
37161 Roo.grid.PropertyGrid = function(container, config){
37162     config = config || {};
37163     var store = new Roo.grid.PropertyStore(this);
37164     this.store = store;
37165     var cm = new Roo.grid.PropertyColumnModel(this, store);
37166     store.store.sort('name', 'ASC');
37167     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37168         ds: store.store,
37169         cm: cm,
37170         enableColLock:false,
37171         enableColumnMove:false,
37172         stripeRows:false,
37173         trackMouseOver: false,
37174         clicksToEdit:1
37175     }, config));
37176     this.getGridEl().addClass('x-props-grid');
37177     this.lastEditRow = null;
37178     this.on('columnresize', this.onColumnResize, this);
37179     this.addEvents({
37180          /**
37181              * @event beforepropertychange
37182              * Fires before a property changes (return false to stop?)
37183              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37184              * @param {String} id Record Id
37185              * @param {String} newval New Value
37186          * @param {String} oldval Old Value
37187              */
37188         "beforepropertychange": true,
37189         /**
37190              * @event propertychange
37191              * Fires after a property changes
37192              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37193              * @param {String} id Record Id
37194              * @param {String} newval New Value
37195          * @param {String} oldval Old Value
37196              */
37197         "propertychange": true
37198     });
37199     this.customEditors = this.customEditors || {};
37200 };
37201 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37202     
37203      /**
37204      * @cfg {Object} customEditors map of colnames=> custom editors.
37205      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37206      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37207      * false disables editing of the field.
37208          */
37209     
37210       /**
37211      * @cfg {Object} propertyNames map of property Names to their displayed value
37212          */
37213     
37214     render : function(){
37215         Roo.grid.PropertyGrid.superclass.render.call(this);
37216         this.autoSize.defer(100, this);
37217     },
37218
37219     autoSize : function(){
37220         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37221         if(this.view){
37222             this.view.fitColumns();
37223         }
37224     },
37225
37226     onColumnResize : function(){
37227         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37228         this.autoSize();
37229     },
37230     /**
37231      * Sets the data for the Grid
37232      * accepts a Key => Value object of all the elements avaiable.
37233      * @param {Object} data  to appear in grid.
37234      */
37235     setSource : function(source){
37236         this.store.setSource(source);
37237         //this.autoSize();
37238     },
37239     /**
37240      * Gets all the data from the grid.
37241      * @return {Object} data  data stored in grid
37242      */
37243     getSource : function(){
37244         return this.store.getSource();
37245     }
37246 });/*
37247   
37248  * Licence LGPL
37249  
37250  */
37251  
37252 /**
37253  * @class Roo.grid.Calendar
37254  * @extends Roo.util.Grid
37255  * This class extends the Grid to provide a calendar widget
37256  * <br><br>Usage:<pre><code>
37257  var grid = new Roo.grid.Calendar("my-container-id", {
37258      ds: myDataStore,
37259      cm: myColModel,
37260      selModel: mySelectionModel,
37261      autoSizeColumns: true,
37262      monitorWindowResize: false,
37263      trackMouseOver: true
37264      eventstore : real data store..
37265  });
37266  // set any options
37267  grid.render();
37268   
37269   * @constructor
37270  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37271  * The container MUST have some type of size defined for the grid to fill. The container will be
37272  * automatically set to position relative if it isn't already.
37273  * @param {Object} config A config object that sets properties on this grid.
37274  */
37275 Roo.grid.Calendar = function(container, config){
37276         // initialize the container
37277         this.container = Roo.get(container);
37278         this.container.update("");
37279         this.container.setStyle("overflow", "hidden");
37280     this.container.addClass('x-grid-container');
37281
37282     this.id = this.container.id;
37283
37284     Roo.apply(this, config);
37285     // check and correct shorthanded configs
37286     
37287     var rows = [];
37288     var d =1;
37289     for (var r = 0;r < 6;r++) {
37290         
37291         rows[r]=[];
37292         for (var c =0;c < 7;c++) {
37293             rows[r][c]= '';
37294         }
37295     }
37296     if (this.eventStore) {
37297         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37298         this.eventStore.on('load',this.onLoad, this);
37299         this.eventStore.on('beforeload',this.clearEvents, this);
37300          
37301     }
37302     
37303     this.dataSource = new Roo.data.Store({
37304             proxy: new Roo.data.MemoryProxy(rows),
37305             reader: new Roo.data.ArrayReader({}, [
37306                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37307     });
37308
37309     this.dataSource.load();
37310     this.ds = this.dataSource;
37311     this.ds.xmodule = this.xmodule || false;
37312     
37313     
37314     var cellRender = function(v,x,r)
37315     {
37316         return String.format(
37317             '<div class="fc-day  fc-widget-content"><div>' +
37318                 '<div class="fc-event-container"></div>' +
37319                 '<div class="fc-day-number">{0}</div>'+
37320                 
37321                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37322             '</div></div>', v);
37323     
37324     }
37325     
37326     
37327     this.colModel = new Roo.grid.ColumnModel( [
37328         {
37329             xtype: 'ColumnModel',
37330             xns: Roo.grid,
37331             dataIndex : 'weekday0',
37332             header : 'Sunday',
37333             renderer : cellRender
37334         },
37335         {
37336             xtype: 'ColumnModel',
37337             xns: Roo.grid,
37338             dataIndex : 'weekday1',
37339             header : 'Monday',
37340             renderer : cellRender
37341         },
37342         {
37343             xtype: 'ColumnModel',
37344             xns: Roo.grid,
37345             dataIndex : 'weekday2',
37346             header : 'Tuesday',
37347             renderer : cellRender
37348         },
37349         {
37350             xtype: 'ColumnModel',
37351             xns: Roo.grid,
37352             dataIndex : 'weekday3',
37353             header : 'Wednesday',
37354             renderer : cellRender
37355         },
37356         {
37357             xtype: 'ColumnModel',
37358             xns: Roo.grid,
37359             dataIndex : 'weekday4',
37360             header : 'Thursday',
37361             renderer : cellRender
37362         },
37363         {
37364             xtype: 'ColumnModel',
37365             xns: Roo.grid,
37366             dataIndex : 'weekday5',
37367             header : 'Friday',
37368             renderer : cellRender
37369         },
37370         {
37371             xtype: 'ColumnModel',
37372             xns: Roo.grid,
37373             dataIndex : 'weekday6',
37374             header : 'Saturday',
37375             renderer : cellRender
37376         }
37377     ]);
37378     this.cm = this.colModel;
37379     this.cm.xmodule = this.xmodule || false;
37380  
37381         
37382           
37383     //this.selModel = new Roo.grid.CellSelectionModel();
37384     //this.sm = this.selModel;
37385     //this.selModel.init(this);
37386     
37387     
37388     if(this.width){
37389         this.container.setWidth(this.width);
37390     }
37391
37392     if(this.height){
37393         this.container.setHeight(this.height);
37394     }
37395     /** @private */
37396         this.addEvents({
37397         // raw events
37398         /**
37399          * @event click
37400          * The raw click event for the entire grid.
37401          * @param {Roo.EventObject} e
37402          */
37403         "click" : true,
37404         /**
37405          * @event dblclick
37406          * The raw dblclick event for the entire grid.
37407          * @param {Roo.EventObject} e
37408          */
37409         "dblclick" : true,
37410         /**
37411          * @event contextmenu
37412          * The raw contextmenu event for the entire grid.
37413          * @param {Roo.EventObject} e
37414          */
37415         "contextmenu" : true,
37416         /**
37417          * @event mousedown
37418          * The raw mousedown event for the entire grid.
37419          * @param {Roo.EventObject} e
37420          */
37421         "mousedown" : true,
37422         /**
37423          * @event mouseup
37424          * The raw mouseup event for the entire grid.
37425          * @param {Roo.EventObject} e
37426          */
37427         "mouseup" : true,
37428         /**
37429          * @event mouseover
37430          * The raw mouseover event for the entire grid.
37431          * @param {Roo.EventObject} e
37432          */
37433         "mouseover" : true,
37434         /**
37435          * @event mouseout
37436          * The raw mouseout event for the entire grid.
37437          * @param {Roo.EventObject} e
37438          */
37439         "mouseout" : true,
37440         /**
37441          * @event keypress
37442          * The raw keypress event for the entire grid.
37443          * @param {Roo.EventObject} e
37444          */
37445         "keypress" : true,
37446         /**
37447          * @event keydown
37448          * The raw keydown event for the entire grid.
37449          * @param {Roo.EventObject} e
37450          */
37451         "keydown" : true,
37452
37453         // custom events
37454
37455         /**
37456          * @event cellclick
37457          * Fires when a cell is clicked
37458          * @param {Grid} this
37459          * @param {Number} rowIndex
37460          * @param {Number} columnIndex
37461          * @param {Roo.EventObject} e
37462          */
37463         "cellclick" : true,
37464         /**
37465          * @event celldblclick
37466          * Fires when a cell is double clicked
37467          * @param {Grid} this
37468          * @param {Number} rowIndex
37469          * @param {Number} columnIndex
37470          * @param {Roo.EventObject} e
37471          */
37472         "celldblclick" : true,
37473         /**
37474          * @event rowclick
37475          * Fires when a row is clicked
37476          * @param {Grid} this
37477          * @param {Number} rowIndex
37478          * @param {Roo.EventObject} e
37479          */
37480         "rowclick" : true,
37481         /**
37482          * @event rowdblclick
37483          * Fires when a row is double clicked
37484          * @param {Grid} this
37485          * @param {Number} rowIndex
37486          * @param {Roo.EventObject} e
37487          */
37488         "rowdblclick" : true,
37489         /**
37490          * @event headerclick
37491          * Fires when a header is clicked
37492          * @param {Grid} this
37493          * @param {Number} columnIndex
37494          * @param {Roo.EventObject} e
37495          */
37496         "headerclick" : true,
37497         /**
37498          * @event headerdblclick
37499          * Fires when a header cell is double clicked
37500          * @param {Grid} this
37501          * @param {Number} columnIndex
37502          * @param {Roo.EventObject} e
37503          */
37504         "headerdblclick" : true,
37505         /**
37506          * @event rowcontextmenu
37507          * Fires when a row is right clicked
37508          * @param {Grid} this
37509          * @param {Number} rowIndex
37510          * @param {Roo.EventObject} e
37511          */
37512         "rowcontextmenu" : true,
37513         /**
37514          * @event cellcontextmenu
37515          * Fires when a cell is right clicked
37516          * @param {Grid} this
37517          * @param {Number} rowIndex
37518          * @param {Number} cellIndex
37519          * @param {Roo.EventObject} e
37520          */
37521          "cellcontextmenu" : true,
37522         /**
37523          * @event headercontextmenu
37524          * Fires when a header is right clicked
37525          * @param {Grid} this
37526          * @param {Number} columnIndex
37527          * @param {Roo.EventObject} e
37528          */
37529         "headercontextmenu" : true,
37530         /**
37531          * @event bodyscroll
37532          * Fires when the body element is scrolled
37533          * @param {Number} scrollLeft
37534          * @param {Number} scrollTop
37535          */
37536         "bodyscroll" : true,
37537         /**
37538          * @event columnresize
37539          * Fires when the user resizes a column
37540          * @param {Number} columnIndex
37541          * @param {Number} newSize
37542          */
37543         "columnresize" : true,
37544         /**
37545          * @event columnmove
37546          * Fires when the user moves a column
37547          * @param {Number} oldIndex
37548          * @param {Number} newIndex
37549          */
37550         "columnmove" : true,
37551         /**
37552          * @event startdrag
37553          * Fires when row(s) start being dragged
37554          * @param {Grid} this
37555          * @param {Roo.GridDD} dd The drag drop object
37556          * @param {event} e The raw browser event
37557          */
37558         "startdrag" : true,
37559         /**
37560          * @event enddrag
37561          * Fires when a drag operation is complete
37562          * @param {Grid} this
37563          * @param {Roo.GridDD} dd The drag drop object
37564          * @param {event} e The raw browser event
37565          */
37566         "enddrag" : true,
37567         /**
37568          * @event dragdrop
37569          * Fires when dragged row(s) are dropped on a valid DD target
37570          * @param {Grid} this
37571          * @param {Roo.GridDD} dd The drag drop object
37572          * @param {String} targetId The target drag drop object
37573          * @param {event} e The raw browser event
37574          */
37575         "dragdrop" : true,
37576         /**
37577          * @event dragover
37578          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37579          * @param {Grid} this
37580          * @param {Roo.GridDD} dd The drag drop object
37581          * @param {String} targetId The target drag drop object
37582          * @param {event} e The raw browser event
37583          */
37584         "dragover" : true,
37585         /**
37586          * @event dragenter
37587          *  Fires when the dragged row(s) first cross another DD target while being dragged
37588          * @param {Grid} this
37589          * @param {Roo.GridDD} dd The drag drop object
37590          * @param {String} targetId The target drag drop object
37591          * @param {event} e The raw browser event
37592          */
37593         "dragenter" : true,
37594         /**
37595          * @event dragout
37596          * Fires when the dragged row(s) leave another DD target while being dragged
37597          * @param {Grid} this
37598          * @param {Roo.GridDD} dd The drag drop object
37599          * @param {String} targetId The target drag drop object
37600          * @param {event} e The raw browser event
37601          */
37602         "dragout" : true,
37603         /**
37604          * @event rowclass
37605          * Fires when a row is rendered, so you can change add a style to it.
37606          * @param {GridView} gridview   The grid view
37607          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37608          */
37609         'rowclass' : true,
37610
37611         /**
37612          * @event render
37613          * Fires when the grid is rendered
37614          * @param {Grid} grid
37615          */
37616         'render' : true,
37617             /**
37618              * @event select
37619              * Fires when a date is selected
37620              * @param {DatePicker} this
37621              * @param {Date} date The selected date
37622              */
37623         'select': true,
37624         /**
37625              * @event monthchange
37626              * Fires when the displayed month changes 
37627              * @param {DatePicker} this
37628              * @param {Date} date The selected month
37629              */
37630         'monthchange': true,
37631         /**
37632              * @event evententer
37633              * Fires when mouse over an event
37634              * @param {Calendar} this
37635              * @param {event} Event
37636              */
37637         'evententer': true,
37638         /**
37639              * @event eventleave
37640              * Fires when the mouse leaves an
37641              * @param {Calendar} this
37642              * @param {event}
37643              */
37644         'eventleave': true,
37645         /**
37646              * @event eventclick
37647              * Fires when the mouse click an
37648              * @param {Calendar} this
37649              * @param {event}
37650              */
37651         'eventclick': true,
37652         /**
37653              * @event eventrender
37654              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37655              * @param {Calendar} this
37656              * @param {data} data to be modified
37657              */
37658         'eventrender': true
37659         
37660     });
37661
37662     Roo.grid.Grid.superclass.constructor.call(this);
37663     this.on('render', function() {
37664         this.view.el.addClass('x-grid-cal'); 
37665         
37666         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37667
37668     },this);
37669     
37670     if (!Roo.grid.Calendar.style) {
37671         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37672             
37673             
37674             '.x-grid-cal .x-grid-col' :  {
37675                 height: 'auto !important',
37676                 'vertical-align': 'top'
37677             },
37678             '.x-grid-cal  .fc-event-hori' : {
37679                 height: '14px'
37680             }
37681              
37682             
37683         }, Roo.id());
37684     }
37685
37686     
37687     
37688 };
37689 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37690     /**
37691      * @cfg {Store} eventStore The store that loads events.
37692      */
37693     eventStore : 25,
37694
37695      
37696     activeDate : false,
37697     startDay : 0,
37698     autoWidth : true,
37699     monitorWindowResize : false,
37700
37701     
37702     resizeColumns : function() {
37703         var col = (this.view.el.getWidth() / 7) - 3;
37704         // loop through cols, and setWidth
37705         for(var i =0 ; i < 7 ; i++){
37706             this.cm.setColumnWidth(i, col);
37707         }
37708     },
37709      setDate :function(date) {
37710         
37711         Roo.log('setDate?');
37712         
37713         this.resizeColumns();
37714         var vd = this.activeDate;
37715         this.activeDate = date;
37716 //        if(vd && this.el){
37717 //            var t = date.getTime();
37718 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37719 //                Roo.log('using add remove');
37720 //                
37721 //                this.fireEvent('monthchange', this, date);
37722 //                
37723 //                this.cells.removeClass("fc-state-highlight");
37724 //                this.cells.each(function(c){
37725 //                   if(c.dateValue == t){
37726 //                       c.addClass("fc-state-highlight");
37727 //                       setTimeout(function(){
37728 //                            try{c.dom.firstChild.focus();}catch(e){}
37729 //                       }, 50);
37730 //                       return false;
37731 //                   }
37732 //                   return true;
37733 //                });
37734 //                return;
37735 //            }
37736 //        }
37737         
37738         var days = date.getDaysInMonth();
37739         
37740         var firstOfMonth = date.getFirstDateOfMonth();
37741         var startingPos = firstOfMonth.getDay()-this.startDay;
37742         
37743         if(startingPos < this.startDay){
37744             startingPos += 7;
37745         }
37746         
37747         var pm = date.add(Date.MONTH, -1);
37748         var prevStart = pm.getDaysInMonth()-startingPos;
37749 //        
37750         
37751         
37752         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37753         
37754         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37755         //this.cells.addClassOnOver('fc-state-hover');
37756         
37757         var cells = this.cells.elements;
37758         var textEls = this.textNodes;
37759         
37760         //Roo.each(cells, function(cell){
37761         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37762         //});
37763         
37764         days += startingPos;
37765
37766         // convert everything to numbers so it's fast
37767         var day = 86400000;
37768         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37769         //Roo.log(d);
37770         //Roo.log(pm);
37771         //Roo.log(prevStart);
37772         
37773         var today = new Date().clearTime().getTime();
37774         var sel = date.clearTime().getTime();
37775         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37776         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37777         var ddMatch = this.disabledDatesRE;
37778         var ddText = this.disabledDatesText;
37779         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37780         var ddaysText = this.disabledDaysText;
37781         var format = this.format;
37782         
37783         var setCellClass = function(cal, cell){
37784             
37785             //Roo.log('set Cell Class');
37786             cell.title = "";
37787             var t = d.getTime();
37788             
37789             //Roo.log(d);
37790             
37791             
37792             cell.dateValue = t;
37793             if(t == today){
37794                 cell.className += " fc-today";
37795                 cell.className += " fc-state-highlight";
37796                 cell.title = cal.todayText;
37797             }
37798             if(t == sel){
37799                 // disable highlight in other month..
37800                 cell.className += " fc-state-highlight";
37801                 
37802             }
37803             // disabling
37804             if(t < min) {
37805                 //cell.className = " fc-state-disabled";
37806                 cell.title = cal.minText;
37807                 return;
37808             }
37809             if(t > max) {
37810                 //cell.className = " fc-state-disabled";
37811                 cell.title = cal.maxText;
37812                 return;
37813             }
37814             if(ddays){
37815                 if(ddays.indexOf(d.getDay()) != -1){
37816                     // cell.title = ddaysText;
37817                    // cell.className = " fc-state-disabled";
37818                 }
37819             }
37820             if(ddMatch && format){
37821                 var fvalue = d.dateFormat(format);
37822                 if(ddMatch.test(fvalue)){
37823                     cell.title = ddText.replace("%0", fvalue);
37824                    cell.className = " fc-state-disabled";
37825                 }
37826             }
37827             
37828             if (!cell.initialClassName) {
37829                 cell.initialClassName = cell.dom.className;
37830             }
37831             
37832             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37833         };
37834
37835         var i = 0;
37836         
37837         for(; i < startingPos; i++) {
37838             cells[i].dayName =  (++prevStart);
37839             Roo.log(textEls[i]);
37840             d.setDate(d.getDate()+1);
37841             
37842             //cells[i].className = "fc-past fc-other-month";
37843             setCellClass(this, cells[i]);
37844         }
37845         
37846         var intDay = 0;
37847         
37848         for(; i < days; i++){
37849             intDay = i - startingPos + 1;
37850             cells[i].dayName =  (intDay);
37851             d.setDate(d.getDate()+1);
37852             
37853             cells[i].className = ''; // "x-date-active";
37854             setCellClass(this, cells[i]);
37855         }
37856         var extraDays = 0;
37857         
37858         for(; i < 42; i++) {
37859             //textEls[i].innerHTML = (++extraDays);
37860             
37861             d.setDate(d.getDate()+1);
37862             cells[i].dayName = (++extraDays);
37863             cells[i].className = "fc-future fc-other-month";
37864             setCellClass(this, cells[i]);
37865         }
37866         
37867         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37868         
37869         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37870         
37871         // this will cause all the cells to mis
37872         var rows= [];
37873         var i =0;
37874         for (var r = 0;r < 6;r++) {
37875             for (var c =0;c < 7;c++) {
37876                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37877             }    
37878         }
37879         
37880         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37881         for(i=0;i<cells.length;i++) {
37882             
37883             this.cells.elements[i].dayName = cells[i].dayName ;
37884             this.cells.elements[i].className = cells[i].className;
37885             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37886             this.cells.elements[i].title = cells[i].title ;
37887             this.cells.elements[i].dateValue = cells[i].dateValue ;
37888         }
37889         
37890         
37891         
37892         
37893         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37894         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37895         
37896         ////if(totalRows != 6){
37897             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37898            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37899        // }
37900         
37901         this.fireEvent('monthchange', this, date);
37902         
37903         
37904     },
37905  /**
37906      * Returns the grid's SelectionModel.
37907      * @return {SelectionModel}
37908      */
37909     getSelectionModel : function(){
37910         if(!this.selModel){
37911             this.selModel = new Roo.grid.CellSelectionModel();
37912         }
37913         return this.selModel;
37914     },
37915
37916     load: function() {
37917         this.eventStore.load()
37918         
37919         
37920         
37921     },
37922     
37923     findCell : function(dt) {
37924         dt = dt.clearTime().getTime();
37925         var ret = false;
37926         this.cells.each(function(c){
37927             //Roo.log("check " +c.dateValue + '?=' + dt);
37928             if(c.dateValue == dt){
37929                 ret = c;
37930                 return false;
37931             }
37932             return true;
37933         });
37934         
37935         return ret;
37936     },
37937     
37938     findCells : function(rec) {
37939         var s = rec.data.start_dt.clone().clearTime().getTime();
37940        // Roo.log(s);
37941         var e= rec.data.end_dt.clone().clearTime().getTime();
37942        // Roo.log(e);
37943         var ret = [];
37944         this.cells.each(function(c){
37945              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37946             
37947             if(c.dateValue > e){
37948                 return ;
37949             }
37950             if(c.dateValue < s){
37951                 return ;
37952             }
37953             ret.push(c);
37954         });
37955         
37956         return ret;    
37957     },
37958     
37959     findBestRow: function(cells)
37960     {
37961         var ret = 0;
37962         
37963         for (var i =0 ; i < cells.length;i++) {
37964             ret  = Math.max(cells[i].rows || 0,ret);
37965         }
37966         return ret;
37967         
37968     },
37969     
37970     
37971     addItem : function(rec)
37972     {
37973         // look for vertical location slot in
37974         var cells = this.findCells(rec);
37975         
37976         rec.row = this.findBestRow(cells);
37977         
37978         // work out the location.
37979         
37980         var crow = false;
37981         var rows = [];
37982         for(var i =0; i < cells.length; i++) {
37983             if (!crow) {
37984                 crow = {
37985                     start : cells[i],
37986                     end :  cells[i]
37987                 };
37988                 continue;
37989             }
37990             if (crow.start.getY() == cells[i].getY()) {
37991                 // on same row.
37992                 crow.end = cells[i];
37993                 continue;
37994             }
37995             // different row.
37996             rows.push(crow);
37997             crow = {
37998                 start: cells[i],
37999                 end : cells[i]
38000             };
38001             
38002         }
38003         
38004         rows.push(crow);
38005         rec.els = [];
38006         rec.rows = rows;
38007         rec.cells = cells;
38008         for (var i = 0; i < cells.length;i++) {
38009             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38010             
38011         }
38012         
38013         
38014     },
38015     
38016     clearEvents: function() {
38017         
38018         if (!this.eventStore.getCount()) {
38019             return;
38020         }
38021         // reset number of rows in cells.
38022         Roo.each(this.cells.elements, function(c){
38023             c.rows = 0;
38024         });
38025         
38026         this.eventStore.each(function(e) {
38027             this.clearEvent(e);
38028         },this);
38029         
38030     },
38031     
38032     clearEvent : function(ev)
38033     {
38034         if (ev.els) {
38035             Roo.each(ev.els, function(el) {
38036                 el.un('mouseenter' ,this.onEventEnter, this);
38037                 el.un('mouseleave' ,this.onEventLeave, this);
38038                 el.remove();
38039             },this);
38040             ev.els = [];
38041         }
38042     },
38043     
38044     
38045     renderEvent : function(ev,ctr) {
38046         if (!ctr) {
38047              ctr = this.view.el.select('.fc-event-container',true).first();
38048         }
38049         
38050          
38051         this.clearEvent(ev);
38052             //code
38053        
38054         
38055         
38056         ev.els = [];
38057         var cells = ev.cells;
38058         var rows = ev.rows;
38059         this.fireEvent('eventrender', this, ev);
38060         
38061         for(var i =0; i < rows.length; i++) {
38062             
38063             cls = '';
38064             if (i == 0) {
38065                 cls += ' fc-event-start';
38066             }
38067             if ((i+1) == rows.length) {
38068                 cls += ' fc-event-end';
38069             }
38070             
38071             //Roo.log(ev.data);
38072             // how many rows should it span..
38073             var cg = this.eventTmpl.append(ctr,Roo.apply({
38074                 fccls : cls
38075                 
38076             }, ev.data) , true);
38077             
38078             
38079             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38080             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38081             cg.on('click', this.onEventClick, this, ev);
38082             
38083             ev.els.push(cg);
38084             
38085             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38086             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38087             //Roo.log(cg);
38088              
38089             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38090             cg.setWidth(ebox.right - sbox.x -2);
38091         }
38092     },
38093     
38094     renderEvents: function()
38095     {   
38096         // first make sure there is enough space..
38097         
38098         if (!this.eventTmpl) {
38099             this.eventTmpl = new Roo.Template(
38100                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38101                     '<div class="fc-event-inner">' +
38102                         '<span class="fc-event-time">{time}</span>' +
38103                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38104                     '</div>' +
38105                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38106                 '</div>'
38107             );
38108                 
38109         }
38110                
38111         
38112         
38113         this.cells.each(function(c) {
38114             //Roo.log(c.select('.fc-day-content div',true).first());
38115             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38116         });
38117         
38118         var ctr = this.view.el.select('.fc-event-container',true).first();
38119         
38120         var cls;
38121         this.eventStore.each(function(ev){
38122             
38123             this.renderEvent(ev);
38124              
38125              
38126         }, this);
38127         this.view.layout();
38128         
38129     },
38130     
38131     onEventEnter: function (e, el,event,d) {
38132         this.fireEvent('evententer', this, el, event);
38133     },
38134     
38135     onEventLeave: function (e, el,event,d) {
38136         this.fireEvent('eventleave', this, el, event);
38137     },
38138     
38139     onEventClick: function (e, el,event,d) {
38140         this.fireEvent('eventclick', this, el, event);
38141     },
38142     
38143     onMonthChange: function () {
38144         this.store.load();
38145     },
38146     
38147     onLoad: function () {
38148         
38149         //Roo.log('calendar onload');
38150 //         
38151         if(this.eventStore.getCount() > 0){
38152             
38153            
38154             
38155             this.eventStore.each(function(d){
38156                 
38157                 
38158                 // FIXME..
38159                 var add =   d.data;
38160                 if (typeof(add.end_dt) == 'undefined')  {
38161                     Roo.log("Missing End time in calendar data: ");
38162                     Roo.log(d);
38163                     return;
38164                 }
38165                 if (typeof(add.start_dt) == 'undefined')  {
38166                     Roo.log("Missing Start time in calendar data: ");
38167                     Roo.log(d);
38168                     return;
38169                 }
38170                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38171                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38172                 add.id = add.id || d.id;
38173                 add.title = add.title || '??';
38174                 
38175                 this.addItem(d);
38176                 
38177              
38178             },this);
38179         }
38180         
38181         this.renderEvents();
38182     }
38183     
38184
38185 });
38186 /*
38187  grid : {
38188                 xtype: 'Grid',
38189                 xns: Roo.grid,
38190                 listeners : {
38191                     render : function ()
38192                     {
38193                         _this.grid = this;
38194                         
38195                         if (!this.view.el.hasClass('course-timesheet')) {
38196                             this.view.el.addClass('course-timesheet');
38197                         }
38198                         if (this.tsStyle) {
38199                             this.ds.load({});
38200                             return; 
38201                         }
38202                         Roo.log('width');
38203                         Roo.log(_this.grid.view.el.getWidth());
38204                         
38205                         
38206                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38207                             '.course-timesheet .x-grid-row' : {
38208                                 height: '80px'
38209                             },
38210                             '.x-grid-row td' : {
38211                                 'vertical-align' : 0
38212                             },
38213                             '.course-edit-link' : {
38214                                 'color' : 'blue',
38215                                 'text-overflow' : 'ellipsis',
38216                                 'overflow' : 'hidden',
38217                                 'white-space' : 'nowrap',
38218                                 'cursor' : 'pointer'
38219                             },
38220                             '.sub-link' : {
38221                                 'color' : 'green'
38222                             },
38223                             '.de-act-sup-link' : {
38224                                 'color' : 'purple',
38225                                 'text-decoration' : 'line-through'
38226                             },
38227                             '.de-act-link' : {
38228                                 'color' : 'red',
38229                                 'text-decoration' : 'line-through'
38230                             },
38231                             '.course-timesheet .course-highlight' : {
38232                                 'border-top-style': 'dashed !important',
38233                                 'border-bottom-bottom': 'dashed !important'
38234                             },
38235                             '.course-timesheet .course-item' : {
38236                                 'font-family'   : 'tahoma, arial, helvetica',
38237                                 'font-size'     : '11px',
38238                                 'overflow'      : 'hidden',
38239                                 'padding-left'  : '10px',
38240                                 'padding-right' : '10px',
38241                                 'padding-top' : '10px' 
38242                             }
38243                             
38244                         }, Roo.id());
38245                                 this.ds.load({});
38246                     }
38247                 },
38248                 autoWidth : true,
38249                 monitorWindowResize : false,
38250                 cellrenderer : function(v,x,r)
38251                 {
38252                     return v;
38253                 },
38254                 sm : {
38255                     xtype: 'CellSelectionModel',
38256                     xns: Roo.grid
38257                 },
38258                 dataSource : {
38259                     xtype: 'Store',
38260                     xns: Roo.data,
38261                     listeners : {
38262                         beforeload : function (_self, options)
38263                         {
38264                             options.params = options.params || {};
38265                             options.params._month = _this.monthField.getValue();
38266                             options.params.limit = 9999;
38267                             options.params['sort'] = 'when_dt';    
38268                             options.params['dir'] = 'ASC';    
38269                             this.proxy.loadResponse = this.loadResponse;
38270                             Roo.log("load?");
38271                             //this.addColumns();
38272                         },
38273                         load : function (_self, records, options)
38274                         {
38275                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38276                                 // if you click on the translation.. you can edit it...
38277                                 var el = Roo.get(this);
38278                                 var id = el.dom.getAttribute('data-id');
38279                                 var d = el.dom.getAttribute('data-date');
38280                                 var t = el.dom.getAttribute('data-time');
38281                                 //var id = this.child('span').dom.textContent;
38282                                 
38283                                 //Roo.log(this);
38284                                 Pman.Dialog.CourseCalendar.show({
38285                                     id : id,
38286                                     when_d : d,
38287                                     when_t : t,
38288                                     productitem_active : id ? 1 : 0
38289                                 }, function() {
38290                                     _this.grid.ds.load({});
38291                                 });
38292                            
38293                            });
38294                            
38295                            _this.panel.fireEvent('resize', [ '', '' ]);
38296                         }
38297                     },
38298                     loadResponse : function(o, success, response){
38299                             // this is overridden on before load..
38300                             
38301                             Roo.log("our code?");       
38302                             //Roo.log(success);
38303                             //Roo.log(response)
38304                             delete this.activeRequest;
38305                             if(!success){
38306                                 this.fireEvent("loadexception", this, o, response);
38307                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38308                                 return;
38309                             }
38310                             var result;
38311                             try {
38312                                 result = o.reader.read(response);
38313                             }catch(e){
38314                                 Roo.log("load exception?");
38315                                 this.fireEvent("loadexception", this, o, response, e);
38316                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38317                                 return;
38318                             }
38319                             Roo.log("ready...");        
38320                             // loop through result.records;
38321                             // and set this.tdate[date] = [] << array of records..
38322                             _this.tdata  = {};
38323                             Roo.each(result.records, function(r){
38324                                 //Roo.log(r.data);
38325                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38326                                     _this.tdata[r.data.when_dt.format('j')] = [];
38327                                 }
38328                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38329                             });
38330                             
38331                             //Roo.log(_this.tdata);
38332                             
38333                             result.records = [];
38334                             result.totalRecords = 6;
38335                     
38336                             // let's generate some duumy records for the rows.
38337                             //var st = _this.dateField.getValue();
38338                             
38339                             // work out monday..
38340                             //st = st.add(Date.DAY, -1 * st.format('w'));
38341                             
38342                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38343                             
38344                             var firstOfMonth = date.getFirstDayOfMonth();
38345                             var days = date.getDaysInMonth();
38346                             var d = 1;
38347                             var firstAdded = false;
38348                             for (var i = 0; i < result.totalRecords ; i++) {
38349                                 //var d= st.add(Date.DAY, i);
38350                                 var row = {};
38351                                 var added = 0;
38352                                 for(var w = 0 ; w < 7 ; w++){
38353                                     if(!firstAdded && firstOfMonth != w){
38354                                         continue;
38355                                     }
38356                                     if(d > days){
38357                                         continue;
38358                                     }
38359                                     firstAdded = true;
38360                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38361                                     row['weekday'+w] = String.format(
38362                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38363                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38364                                                     d,
38365                                                     date.format('Y-m-')+dd
38366                                                 );
38367                                     added++;
38368                                     if(typeof(_this.tdata[d]) != 'undefined'){
38369                                         Roo.each(_this.tdata[d], function(r){
38370                                             var is_sub = '';
38371                                             var deactive = '';
38372                                             var id = r.id;
38373                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38374                                             if(r.parent_id*1>0){
38375                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38376                                                 id = r.parent_id;
38377                                             }
38378                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38379                                                 deactive = 'de-act-link';
38380                                             }
38381                                             
38382                                             row['weekday'+w] += String.format(
38383                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38384                                                     id, //0
38385                                                     r.product_id_name, //1
38386                                                     r.when_dt.format('h:ia'), //2
38387                                                     is_sub, //3
38388                                                     deactive, //4
38389                                                     desc // 5
38390                                             );
38391                                         });
38392                                     }
38393                                     d++;
38394                                 }
38395                                 
38396                                 // only do this if something added..
38397                                 if(added > 0){ 
38398                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38399                                 }
38400                                 
38401                                 
38402                                 // push it twice. (second one with an hour..
38403                                 
38404                             }
38405                             //Roo.log(result);
38406                             this.fireEvent("load", this, o, o.request.arg);
38407                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38408                         },
38409                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38410                     proxy : {
38411                         xtype: 'HttpProxy',
38412                         xns: Roo.data,
38413                         method : 'GET',
38414                         url : baseURL + '/Roo/Shop_course.php'
38415                     },
38416                     reader : {
38417                         xtype: 'JsonReader',
38418                         xns: Roo.data,
38419                         id : 'id',
38420                         fields : [
38421                             {
38422                                 'name': 'id',
38423                                 'type': 'int'
38424                             },
38425                             {
38426                                 'name': 'when_dt',
38427                                 'type': 'string'
38428                             },
38429                             {
38430                                 'name': 'end_dt',
38431                                 'type': 'string'
38432                             },
38433                             {
38434                                 'name': 'parent_id',
38435                                 'type': 'int'
38436                             },
38437                             {
38438                                 'name': 'product_id',
38439                                 'type': 'int'
38440                             },
38441                             {
38442                                 'name': 'productitem_id',
38443                                 'type': 'int'
38444                             },
38445                             {
38446                                 'name': 'guid',
38447                                 'type': 'int'
38448                             }
38449                         ]
38450                     }
38451                 },
38452                 toolbar : {
38453                     xtype: 'Toolbar',
38454                     xns: Roo,
38455                     items : [
38456                         {
38457                             xtype: 'Button',
38458                             xns: Roo.Toolbar,
38459                             listeners : {
38460                                 click : function (_self, e)
38461                                 {
38462                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38463                                     sd.setMonth(sd.getMonth()-1);
38464                                     _this.monthField.setValue(sd.format('Y-m-d'));
38465                                     _this.grid.ds.load({});
38466                                 }
38467                             },
38468                             text : "Back"
38469                         },
38470                         {
38471                             xtype: 'Separator',
38472                             xns: Roo.Toolbar
38473                         },
38474                         {
38475                             xtype: 'MonthField',
38476                             xns: Roo.form,
38477                             listeners : {
38478                                 render : function (_self)
38479                                 {
38480                                     _this.monthField = _self;
38481                                    // _this.monthField.set  today
38482                                 },
38483                                 select : function (combo, date)
38484                                 {
38485                                     _this.grid.ds.load({});
38486                                 }
38487                             },
38488                             value : (function() { return new Date(); })()
38489                         },
38490                         {
38491                             xtype: 'Separator',
38492                             xns: Roo.Toolbar
38493                         },
38494                         {
38495                             xtype: 'TextItem',
38496                             xns: Roo.Toolbar,
38497                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38498                         },
38499                         {
38500                             xtype: 'Fill',
38501                             xns: Roo.Toolbar
38502                         },
38503                         {
38504                             xtype: 'Button',
38505                             xns: Roo.Toolbar,
38506                             listeners : {
38507                                 click : function (_self, e)
38508                                 {
38509                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38510                                     sd.setMonth(sd.getMonth()+1);
38511                                     _this.monthField.setValue(sd.format('Y-m-d'));
38512                                     _this.grid.ds.load({});
38513                                 }
38514                             },
38515                             text : "Next"
38516                         }
38517                     ]
38518                 },
38519                  
38520             }
38521         };
38522         
38523         *//*
38524  * Based on:
38525  * Ext JS Library 1.1.1
38526  * Copyright(c) 2006-2007, Ext JS, LLC.
38527  *
38528  * Originally Released Under LGPL - original licence link has changed is not relivant.
38529  *
38530  * Fork - LGPL
38531  * <script type="text/javascript">
38532  */
38533  
38534 /**
38535  * @class Roo.LoadMask
38536  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38537  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38538  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38539  * element's UpdateManager load indicator and will be destroyed after the initial load.
38540  * @constructor
38541  * Create a new LoadMask
38542  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38543  * @param {Object} config The config object
38544  */
38545 Roo.LoadMask = function(el, config){
38546     this.el = Roo.get(el);
38547     Roo.apply(this, config);
38548     if(this.store){
38549         this.store.on('beforeload', this.onBeforeLoad, this);
38550         this.store.on('load', this.onLoad, this);
38551         this.store.on('loadexception', this.onLoadException, this);
38552         this.removeMask = false;
38553     }else{
38554         var um = this.el.getUpdateManager();
38555         um.showLoadIndicator = false; // disable the default indicator
38556         um.on('beforeupdate', this.onBeforeLoad, this);
38557         um.on('update', this.onLoad, this);
38558         um.on('failure', this.onLoad, this);
38559         this.removeMask = true;
38560     }
38561 };
38562
38563 Roo.LoadMask.prototype = {
38564     /**
38565      * @cfg {Boolean} removeMask
38566      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38567      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38568      */
38569     /**
38570      * @cfg {String} msg
38571      * The text to display in a centered loading message box (defaults to 'Loading...')
38572      */
38573     msg : 'Loading...',
38574     /**
38575      * @cfg {String} msgCls
38576      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38577      */
38578     msgCls : 'x-mask-loading',
38579
38580     /**
38581      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38582      * @type Boolean
38583      */
38584     disabled: false,
38585
38586     /**
38587      * Disables the mask to prevent it from being displayed
38588      */
38589     disable : function(){
38590        this.disabled = true;
38591     },
38592
38593     /**
38594      * Enables the mask so that it can be displayed
38595      */
38596     enable : function(){
38597         this.disabled = false;
38598     },
38599     
38600     onLoadException : function()
38601     {
38602         Roo.log(arguments);
38603         
38604         if (typeof(arguments[3]) != 'undefined') {
38605             Roo.MessageBox.alert("Error loading",arguments[3]);
38606         } 
38607         /*
38608         try {
38609             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38610                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38611             }   
38612         } catch(e) {
38613             
38614         }
38615         */
38616     
38617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38618     },
38619     // private
38620     onLoad : function()
38621     {
38622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38623     },
38624
38625     // private
38626     onBeforeLoad : function(){
38627         if(!this.disabled){
38628             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38629         }
38630     },
38631
38632     // private
38633     destroy : function(){
38634         if(this.store){
38635             this.store.un('beforeload', this.onBeforeLoad, this);
38636             this.store.un('load', this.onLoad, this);
38637             this.store.un('loadexception', this.onLoadException, this);
38638         }else{
38639             var um = this.el.getUpdateManager();
38640             um.un('beforeupdate', this.onBeforeLoad, this);
38641             um.un('update', this.onLoad, this);
38642             um.un('failure', this.onLoad, this);
38643         }
38644     }
38645 };/*
38646  * Based on:
38647  * Ext JS Library 1.1.1
38648  * Copyright(c) 2006-2007, Ext JS, LLC.
38649  *
38650  * Originally Released Under LGPL - original licence link has changed is not relivant.
38651  *
38652  * Fork - LGPL
38653  * <script type="text/javascript">
38654  */
38655
38656
38657 /**
38658  * @class Roo.XTemplate
38659  * @extends Roo.Template
38660  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38661 <pre><code>
38662 var t = new Roo.XTemplate(
38663         '&lt;select name="{name}"&gt;',
38664                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38665         '&lt;/select&gt;'
38666 );
38667  
38668 // then append, applying the master template values
38669  </code></pre>
38670  *
38671  * Supported features:
38672  *
38673  *  Tags:
38674
38675 <pre><code>
38676       {a_variable} - output encoded.
38677       {a_variable.format:("Y-m-d")} - call a method on the variable
38678       {a_variable:raw} - unencoded output
38679       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38680       {a_variable:this.method_on_template(...)} - call a method on the template object.
38681  
38682 </code></pre>
38683  *  The tpl tag:
38684 <pre><code>
38685         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38686         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38687         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38688         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38689   
38690         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38691         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38692 </code></pre>
38693  *      
38694  */
38695 Roo.XTemplate = function()
38696 {
38697     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38698     if (this.html) {
38699         this.compile();
38700     }
38701 };
38702
38703
38704 Roo.extend(Roo.XTemplate, Roo.Template, {
38705
38706     /**
38707      * The various sub templates
38708      */
38709     tpls : false,
38710     /**
38711      *
38712      * basic tag replacing syntax
38713      * WORD:WORD()
38714      *
38715      * // you can fake an object call by doing this
38716      *  x.t:(test,tesT) 
38717      * 
38718      */
38719     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38720
38721     /**
38722      * compile the template
38723      *
38724      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38725      *
38726      */
38727     compile: function()
38728     {
38729         var s = this.html;
38730      
38731         s = ['<tpl>', s, '</tpl>'].join('');
38732     
38733         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38734             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38735             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38736             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38737             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38738             m,
38739             id     = 0,
38740             tpls   = [];
38741     
38742         while(true == !!(m = s.match(re))){
38743             var forMatch   = m[0].match(nameRe),
38744                 ifMatch   = m[0].match(ifRe),
38745                 execMatch   = m[0].match(execRe),
38746                 namedMatch   = m[0].match(namedRe),
38747                 
38748                 exp  = null, 
38749                 fn   = null,
38750                 exec = null,
38751                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38752                 
38753             if (ifMatch) {
38754                 // if - puts fn into test..
38755                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38756                 if(exp){
38757                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38758                 }
38759             }
38760             
38761             if (execMatch) {
38762                 // exec - calls a function... returns empty if true is  returned.
38763                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38764                 if(exp){
38765                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38766                 }
38767             }
38768             
38769             
38770             if (name) {
38771                 // for = 
38772                 switch(name){
38773                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38774                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38775                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38776                 }
38777             }
38778             var uid = namedMatch ? namedMatch[1] : id;
38779             
38780             
38781             tpls.push({
38782                 id:     namedMatch ? namedMatch[1] : id,
38783                 target: name,
38784                 exec:   exec,
38785                 test:   fn,
38786                 body:   m[1] || ''
38787             });
38788             if (namedMatch) {
38789                 s = s.replace(m[0], '');
38790             } else { 
38791                 s = s.replace(m[0], '{xtpl'+ id + '}');
38792             }
38793             ++id;
38794         }
38795         this.tpls = [];
38796         for(var i = tpls.length-1; i >= 0; --i){
38797             this.compileTpl(tpls[i]);
38798             this.tpls[tpls[i].id] = tpls[i];
38799         }
38800         this.master = tpls[tpls.length-1];
38801         return this;
38802     },
38803     /**
38804      * same as applyTemplate, except it's done to one of the subTemplates
38805      * when using named templates, you can do:
38806      *
38807      * var str = pl.applySubTemplate('your-name', values);
38808      *
38809      * 
38810      * @param {Number} id of the template
38811      * @param {Object} values to apply to template
38812      * @param {Object} parent (normaly the instance of this object)
38813      */
38814     applySubTemplate : function(id, values, parent)
38815     {
38816         
38817         
38818         var t = this.tpls[id];
38819         
38820         
38821         try { 
38822             if(t.test && !t.test.call(this, values, parent)){
38823                 return '';
38824             }
38825         } catch(e) {
38826             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38827             Roo.log(e.toString());
38828             Roo.log(t.test);
38829             return ''
38830         }
38831         try { 
38832             
38833             if(t.exec && t.exec.call(this, values, parent)){
38834                 return '';
38835             }
38836         } catch(e) {
38837             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38838             Roo.log(e.toString());
38839             Roo.log(t.exec);
38840             return ''
38841         }
38842         try {
38843             var vs = t.target ? t.target.call(this, values, parent) : values;
38844             parent = t.target ? values : parent;
38845             if(t.target && vs instanceof Array){
38846                 var buf = [];
38847                 for(var i = 0, len = vs.length; i < len; i++){
38848                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38849                 }
38850                 return buf.join('');
38851             }
38852             return t.compiled.call(this, vs, parent);
38853         } catch (e) {
38854             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38855             Roo.log(e.toString());
38856             Roo.log(t.compiled);
38857             return '';
38858         }
38859     },
38860
38861     compileTpl : function(tpl)
38862     {
38863         var fm = Roo.util.Format;
38864         var useF = this.disableFormats !== true;
38865         var sep = Roo.isGecko ? "+" : ",";
38866         var undef = function(str) {
38867             Roo.log("Property not found :"  + str);
38868             return '';
38869         };
38870         
38871         var fn = function(m, name, format, args)
38872         {
38873             //Roo.log(arguments);
38874             args = args ? args.replace(/\\'/g,"'") : args;
38875             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38876             if (typeof(format) == 'undefined') {
38877                 format= 'htmlEncode';
38878             }
38879             if (format == 'raw' ) {
38880                 format = false;
38881             }
38882             
38883             if(name.substr(0, 4) == 'xtpl'){
38884                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38885             }
38886             
38887             // build an array of options to determine if value is undefined..
38888             
38889             // basically get 'xxxx.yyyy' then do
38890             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38891             //    (function () { Roo.log("Property not found"); return ''; })() :
38892             //    ......
38893             
38894             var udef_ar = [];
38895             var lookfor = '';
38896             Roo.each(name.split('.'), function(st) {
38897                 lookfor += (lookfor.length ? '.': '') + st;
38898                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38899             });
38900             
38901             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38902             
38903             
38904             if(format && useF){
38905                 
38906                 args = args ? ',' + args : "";
38907                  
38908                 if(format.substr(0, 5) != "this."){
38909                     format = "fm." + format + '(';
38910                 }else{
38911                     format = 'this.call("'+ format.substr(5) + '", ';
38912                     args = ", values";
38913                 }
38914                 
38915                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38916             }
38917              
38918             if (args.length) {
38919                 // called with xxyx.yuu:(test,test)
38920                 // change to ()
38921                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38922             }
38923             // raw.. - :raw modifier..
38924             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38925             
38926         };
38927         var body;
38928         // branched to use + in gecko and [].join() in others
38929         if(Roo.isGecko){
38930             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38931                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38932                     "';};};";
38933         }else{
38934             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38935             body.push(tpl.body.replace(/(\r\n|\n)/g,
38936                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38937             body.push("'].join('');};};");
38938             body = body.join('');
38939         }
38940         
38941         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38942        
38943         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38944         eval(body);
38945         
38946         return this;
38947     },
38948
38949     applyTemplate : function(values){
38950         return this.master.compiled.call(this, values, {});
38951         //var s = this.subs;
38952     },
38953
38954     apply : function(){
38955         return this.applyTemplate.apply(this, arguments);
38956     }
38957
38958  });
38959
38960 Roo.XTemplate.from = function(el){
38961     el = Roo.getDom(el);
38962     return new Roo.XTemplate(el.value || el.innerHTML);
38963 };