roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204                 var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     /**
2224      * using 'cn' the nested child reader read the child array into it's child stores.
2225      * @param {Object} rec The record with a 'children array
2226      */
2227     toLoadData: function(rec)
2228     {
2229         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2230         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2231         
2232     }
2233     
2234     
2235 });/*
2236  * Based on:
2237  * Ext JS Library 1.1.1
2238  * Copyright(c) 2006-2007, Ext JS, LLC.
2239  *
2240  * Originally Released Under LGPL - original licence link has changed is not relivant.
2241  *
2242  * Fork - LGPL
2243  * <script type="text/javascript">
2244  */
2245
2246
2247 /**
2248  * @class Roo.data.Tree
2249  * @extends Roo.util.Observable
2250  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2251  * in the tree have most standard DOM functionality.
2252  * @constructor
2253  * @param {Node} root (optional) The root node
2254  */
2255 Roo.data.Tree = function(root){
2256    this.nodeHash = {};
2257    /**
2258     * The root node for this tree
2259     * @type Node
2260     */
2261    this.root = null;
2262    if(root){
2263        this.setRootNode(root);
2264    }
2265    this.addEvents({
2266        /**
2267         * @event append
2268         * Fires when a new child node is appended to a node in this tree.
2269         * @param {Tree} tree The owner tree
2270         * @param {Node} parent The parent node
2271         * @param {Node} node The newly appended node
2272         * @param {Number} index The index of the newly appended node
2273         */
2274        "append" : true,
2275        /**
2276         * @event remove
2277         * Fires when a child node is removed from a node in this tree.
2278         * @param {Tree} tree The owner tree
2279         * @param {Node} parent The parent node
2280         * @param {Node} node The child node removed
2281         */
2282        "remove" : true,
2283        /**
2284         * @event move
2285         * Fires when a node is moved to a new location in the tree
2286         * @param {Tree} tree The owner tree
2287         * @param {Node} node The node moved
2288         * @param {Node} oldParent The old parent of this node
2289         * @param {Node} newParent The new parent of this node
2290         * @param {Number} index The index it was moved to
2291         */
2292        "move" : true,
2293        /**
2294         * @event insert
2295         * Fires when a new child node is inserted in a node in this tree.
2296         * @param {Tree} tree The owner tree
2297         * @param {Node} parent The parent node
2298         * @param {Node} node The child node inserted
2299         * @param {Node} refNode The child node the node was inserted before
2300         */
2301        "insert" : true,
2302        /**
2303         * @event beforeappend
2304         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2305         * @param {Tree} tree The owner tree
2306         * @param {Node} parent The parent node
2307         * @param {Node} node The child node to be appended
2308         */
2309        "beforeappend" : true,
2310        /**
2311         * @event beforeremove
2312         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} parent The parent node
2315         * @param {Node} node The child node to be removed
2316         */
2317        "beforeremove" : true,
2318        /**
2319         * @event beforemove
2320         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2321         * @param {Tree} tree The owner tree
2322         * @param {Node} node The node being moved
2323         * @param {Node} oldParent The parent of the node
2324         * @param {Node} newParent The new parent the node is moving to
2325         * @param {Number} index The index it is being moved to
2326         */
2327        "beforemove" : true,
2328        /**
2329         * @event beforeinsert
2330         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2331         * @param {Tree} tree The owner tree
2332         * @param {Node} parent The parent node
2333         * @param {Node} node The child node to be inserted
2334         * @param {Node} refNode The child node the node is being inserted before
2335         */
2336        "beforeinsert" : true
2337    });
2338
2339     Roo.data.Tree.superclass.constructor.call(this);
2340 };
2341
2342 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2343     pathSeparator: "/",
2344
2345     proxyNodeEvent : function(){
2346         return this.fireEvent.apply(this, arguments);
2347     },
2348
2349     /**
2350      * Returns the root node for this tree.
2351      * @return {Node}
2352      */
2353     getRootNode : function(){
2354         return this.root;
2355     },
2356
2357     /**
2358      * Sets the root node for this tree.
2359      * @param {Node} node
2360      * @return {Node}
2361      */
2362     setRootNode : function(node){
2363         this.root = node;
2364         node.ownerTree = this;
2365         node.isRoot = true;
2366         this.registerNode(node);
2367         return node;
2368     },
2369
2370     /**
2371      * Gets a node in this tree by its id.
2372      * @param {String} id
2373      * @return {Node}
2374      */
2375     getNodeById : function(id){
2376         return this.nodeHash[id];
2377     },
2378
2379     registerNode : function(node){
2380         this.nodeHash[node.id] = node;
2381     },
2382
2383     unregisterNode : function(node){
2384         delete this.nodeHash[node.id];
2385     },
2386
2387     toString : function(){
2388         return "[Tree"+(this.id?" "+this.id:"")+"]";
2389     }
2390 });
2391
2392 /**
2393  * @class Roo.data.Node
2394  * @extends Roo.util.Observable
2395  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2396  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2397  * @constructor
2398  * @param {Object} attributes The attributes/config for the node
2399  */
2400 Roo.data.Node = function(attributes){
2401     /**
2402      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2403      * @type {Object}
2404      */
2405     this.attributes = attributes || {};
2406     this.leaf = this.attributes.leaf;
2407     /**
2408      * The node id. @type String
2409      */
2410     this.id = this.attributes.id;
2411     if(!this.id){
2412         this.id = Roo.id(null, "ynode-");
2413         this.attributes.id = this.id;
2414     }
2415      
2416     
2417     /**
2418      * All child nodes of this node. @type Array
2419      */
2420     this.childNodes = [];
2421     if(!this.childNodes.indexOf){ // indexOf is a must
2422         this.childNodes.indexOf = function(o){
2423             for(var i = 0, len = this.length; i < len; i++){
2424                 if(this[i] == o) {
2425                     return i;
2426                 }
2427             }
2428             return -1;
2429         };
2430     }
2431     /**
2432      * The parent node for this node. @type Node
2433      */
2434     this.parentNode = null;
2435     /**
2436      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2437      */
2438     this.firstChild = null;
2439     /**
2440      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2441      */
2442     this.lastChild = null;
2443     /**
2444      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2445      */
2446     this.previousSibling = null;
2447     /**
2448      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2449      */
2450     this.nextSibling = null;
2451
2452     this.addEvents({
2453        /**
2454         * @event append
2455         * Fires when a new child node is appended
2456         * @param {Tree} tree The owner tree
2457         * @param {Node} this This node
2458         * @param {Node} node The newly appended node
2459         * @param {Number} index The index of the newly appended node
2460         */
2461        "append" : true,
2462        /**
2463         * @event remove
2464         * Fires when a child node is removed
2465         * @param {Tree} tree The owner tree
2466         * @param {Node} this This node
2467         * @param {Node} node The removed node
2468         */
2469        "remove" : true,
2470        /**
2471         * @event move
2472         * Fires when this node is moved to a new location in the tree
2473         * @param {Tree} tree The owner tree
2474         * @param {Node} this This node
2475         * @param {Node} oldParent The old parent of this node
2476         * @param {Node} newParent The new parent of this node
2477         * @param {Number} index The index it was moved to
2478         */
2479        "move" : true,
2480        /**
2481         * @event insert
2482         * Fires when a new child node is inserted.
2483         * @param {Tree} tree The owner tree
2484         * @param {Node} this This node
2485         * @param {Node} node The child node inserted
2486         * @param {Node} refNode The child node the node was inserted before
2487         */
2488        "insert" : true,
2489        /**
2490         * @event beforeappend
2491         * Fires before a new child is appended, return false to cancel the append.
2492         * @param {Tree} tree The owner tree
2493         * @param {Node} this This node
2494         * @param {Node} node The child node to be appended
2495         */
2496        "beforeappend" : true,
2497        /**
2498         * @event beforeremove
2499         * Fires before a child is removed, return false to cancel the remove.
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} node The child node to be removed
2503         */
2504        "beforeremove" : true,
2505        /**
2506         * @event beforemove
2507         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2508         * @param {Tree} tree The owner tree
2509         * @param {Node} this This node
2510         * @param {Node} oldParent The parent of this node
2511         * @param {Node} newParent The new parent this node is moving to
2512         * @param {Number} index The index it is being moved to
2513         */
2514        "beforemove" : true,
2515        /**
2516         * @event beforeinsert
2517         * Fires before a new child is inserted, return false to cancel the insert.
2518         * @param {Tree} tree The owner tree
2519         * @param {Node} this This node
2520         * @param {Node} node The child node to be inserted
2521         * @param {Node} refNode The child node the node is being inserted before
2522         */
2523        "beforeinsert" : true
2524    });
2525     this.listeners = this.attributes.listeners;
2526     Roo.data.Node.superclass.constructor.call(this);
2527 };
2528
2529 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2530     fireEvent : function(evtName){
2531         // first do standard event for this node
2532         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2533             return false;
2534         }
2535         // then bubble it up to the tree if the event wasn't cancelled
2536         var ot = this.getOwnerTree();
2537         if(ot){
2538             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2539                 return false;
2540             }
2541         }
2542         return true;
2543     },
2544
2545     /**
2546      * Returns true if this node is a leaf
2547      * @return {Boolean}
2548      */
2549     isLeaf : function(){
2550         return this.leaf === true;
2551     },
2552
2553     // private
2554     setFirstChild : function(node){
2555         this.firstChild = node;
2556     },
2557
2558     //private
2559     setLastChild : function(node){
2560         this.lastChild = node;
2561     },
2562
2563
2564     /**
2565      * Returns true if this node is the last child of its parent
2566      * @return {Boolean}
2567      */
2568     isLast : function(){
2569        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2570     },
2571
2572     /**
2573      * Returns true if this node is the first child of its parent
2574      * @return {Boolean}
2575      */
2576     isFirst : function(){
2577        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2578     },
2579
2580     hasChildNodes : function(){
2581         return !this.isLeaf() && this.childNodes.length > 0;
2582     },
2583
2584     /**
2585      * Insert node(s) as the last child node of this node.
2586      * @param {Node/Array} node The node or Array of nodes to append
2587      * @return {Node} The appended node if single append, or null if an array was passed
2588      */
2589     appendChild : function(node){
2590         var multi = false;
2591         if(node instanceof Array){
2592             multi = node;
2593         }else if(arguments.length > 1){
2594             multi = arguments;
2595         }
2596         
2597         // if passed an array or multiple args do them one by one
2598         if(multi){
2599             for(var i = 0, len = multi.length; i < len; i++) {
2600                 this.appendChild(multi[i]);
2601             }
2602         }else{
2603             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2604                 return false;
2605             }
2606             var index = this.childNodes.length;
2607             var oldParent = node.parentNode;
2608             // it's a move, make sure we move it cleanly
2609             if(oldParent){
2610                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2611                     return false;
2612                 }
2613                 oldParent.removeChild(node);
2614             }
2615             
2616             index = this.childNodes.length;
2617             if(index == 0){
2618                 this.setFirstChild(node);
2619             }
2620             this.childNodes.push(node);
2621             node.parentNode = this;
2622             var ps = this.childNodes[index-1];
2623             if(ps){
2624                 node.previousSibling = ps;
2625                 ps.nextSibling = node;
2626             }else{
2627                 node.previousSibling = null;
2628             }
2629             node.nextSibling = null;
2630             this.setLastChild(node);
2631             node.setOwnerTree(this.getOwnerTree());
2632             this.fireEvent("append", this.ownerTree, this, node, index);
2633             if(this.ownerTree) {
2634                 this.ownerTree.fireEvent("appendnode", this, node, index);
2635             }
2636             if(oldParent){
2637                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2638             }
2639             return node;
2640         }
2641     },
2642
2643     /**
2644      * Removes a child node from this node.
2645      * @param {Node} node The node to remove
2646      * @return {Node} The removed node
2647      */
2648     removeChild : function(node){
2649         var index = this.childNodes.indexOf(node);
2650         if(index == -1){
2651             return false;
2652         }
2653         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2654             return false;
2655         }
2656
2657         // remove it from childNodes collection
2658         this.childNodes.splice(index, 1);
2659
2660         // update siblings
2661         if(node.previousSibling){
2662             node.previousSibling.nextSibling = node.nextSibling;
2663         }
2664         if(node.nextSibling){
2665             node.nextSibling.previousSibling = node.previousSibling;
2666         }
2667
2668         // update child refs
2669         if(this.firstChild == node){
2670             this.setFirstChild(node.nextSibling);
2671         }
2672         if(this.lastChild == node){
2673             this.setLastChild(node.previousSibling);
2674         }
2675
2676         node.setOwnerTree(null);
2677         // clear any references from the node
2678         node.parentNode = null;
2679         node.previousSibling = null;
2680         node.nextSibling = null;
2681         this.fireEvent("remove", this.ownerTree, this, node);
2682         return node;
2683     },
2684
2685     /**
2686      * Inserts the first node before the second node in this nodes childNodes collection.
2687      * @param {Node} node The node to insert
2688      * @param {Node} refNode The node to insert before (if null the node is appended)
2689      * @return {Node} The inserted node
2690      */
2691     insertBefore : function(node, refNode){
2692         if(!refNode){ // like standard Dom, refNode can be null for append
2693             return this.appendChild(node);
2694         }
2695         // nothing to do
2696         if(node == refNode){
2697             return false;
2698         }
2699
2700         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2701             return false;
2702         }
2703         var index = this.childNodes.indexOf(refNode);
2704         var oldParent = node.parentNode;
2705         var refIndex = index;
2706
2707         // when moving internally, indexes will change after remove
2708         if(oldParent == this && this.childNodes.indexOf(node) < index){
2709             refIndex--;
2710         }
2711
2712         // it's a move, make sure we move it cleanly
2713         if(oldParent){
2714             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2715                 return false;
2716             }
2717             oldParent.removeChild(node);
2718         }
2719         if(refIndex == 0){
2720             this.setFirstChild(node);
2721         }
2722         this.childNodes.splice(refIndex, 0, node);
2723         node.parentNode = this;
2724         var ps = this.childNodes[refIndex-1];
2725         if(ps){
2726             node.previousSibling = ps;
2727             ps.nextSibling = node;
2728         }else{
2729             node.previousSibling = null;
2730         }
2731         node.nextSibling = refNode;
2732         refNode.previousSibling = node;
2733         node.setOwnerTree(this.getOwnerTree());
2734         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2735         if(oldParent){
2736             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2737         }
2738         return node;
2739     },
2740
2741     /**
2742      * Returns the child node at the specified index.
2743      * @param {Number} index
2744      * @return {Node}
2745      */
2746     item : function(index){
2747         return this.childNodes[index];
2748     },
2749
2750     /**
2751      * Replaces one child node in this node with another.
2752      * @param {Node} newChild The replacement node
2753      * @param {Node} oldChild The node to replace
2754      * @return {Node} The replaced node
2755      */
2756     replaceChild : function(newChild, oldChild){
2757         this.insertBefore(newChild, oldChild);
2758         this.removeChild(oldChild);
2759         return oldChild;
2760     },
2761
2762     /**
2763      * Returns the index of a child node
2764      * @param {Node} node
2765      * @return {Number} The index of the node or -1 if it was not found
2766      */
2767     indexOf : function(child){
2768         return this.childNodes.indexOf(child);
2769     },
2770
2771     /**
2772      * Returns the tree this node is in.
2773      * @return {Tree}
2774      */
2775     getOwnerTree : function(){
2776         // if it doesn't have one, look for one
2777         if(!this.ownerTree){
2778             var p = this;
2779             while(p){
2780                 if(p.ownerTree){
2781                     this.ownerTree = p.ownerTree;
2782                     break;
2783                 }
2784                 p = p.parentNode;
2785             }
2786         }
2787         return this.ownerTree;
2788     },
2789
2790     /**
2791      * Returns depth of this node (the root node has a depth of 0)
2792      * @return {Number}
2793      */
2794     getDepth : function(){
2795         var depth = 0;
2796         var p = this;
2797         while(p.parentNode){
2798             ++depth;
2799             p = p.parentNode;
2800         }
2801         return depth;
2802     },
2803
2804     // private
2805     setOwnerTree : function(tree){
2806         // if it's move, we need to update everyone
2807         if(tree != this.ownerTree){
2808             if(this.ownerTree){
2809                 this.ownerTree.unregisterNode(this);
2810             }
2811             this.ownerTree = tree;
2812             var cs = this.childNodes;
2813             for(var i = 0, len = cs.length; i < len; i++) {
2814                 cs[i].setOwnerTree(tree);
2815             }
2816             if(tree){
2817                 tree.registerNode(this);
2818             }
2819         }
2820     },
2821
2822     /**
2823      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2824      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2825      * @return {String} The path
2826      */
2827     getPath : function(attr){
2828         attr = attr || "id";
2829         var p = this.parentNode;
2830         var b = [this.attributes[attr]];
2831         while(p){
2832             b.unshift(p.attributes[attr]);
2833             p = p.parentNode;
2834         }
2835         var sep = this.getOwnerTree().pathSeparator;
2836         return sep + b.join(sep);
2837     },
2838
2839     /**
2840      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2841      * function call will be the scope provided or the current node. The arguments to the function
2842      * will be the args provided or the current node. If the function returns false at any point,
2843      * the bubble is stopped.
2844      * @param {Function} fn The function to call
2845      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2846      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2847      */
2848     bubble : function(fn, scope, args){
2849         var p = this;
2850         while(p){
2851             if(fn.call(scope || p, args || p) === false){
2852                 break;
2853             }
2854             p = p.parentNode;
2855         }
2856     },
2857
2858     /**
2859      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2860      * function call will be the scope provided or the current node. The arguments to the function
2861      * will be the args provided or the current node. If the function returns false at any point,
2862      * the cascade is stopped on that branch.
2863      * @param {Function} fn The function to call
2864      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2865      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2866      */
2867     cascade : function(fn, scope, args){
2868         if(fn.call(scope || this, args || this) !== false){
2869             var cs = this.childNodes;
2870             for(var i = 0, len = cs.length; i < len; i++) {
2871                 cs[i].cascade(fn, scope, args);
2872             }
2873         }
2874     },
2875
2876     /**
2877      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2878      * function call will be the scope provided or the current node. The arguments to the function
2879      * will be the args provided or the current node. If the function returns false at any point,
2880      * the iteration stops.
2881      * @param {Function} fn The function to call
2882      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2883      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2884      */
2885     eachChild : function(fn, scope, args){
2886         var cs = this.childNodes;
2887         for(var i = 0, len = cs.length; i < len; i++) {
2888                 if(fn.call(scope || this, args || cs[i]) === false){
2889                     break;
2890                 }
2891         }
2892     },
2893
2894     /**
2895      * Finds the first child that has the attribute with the specified value.
2896      * @param {String} attribute The attribute name
2897      * @param {Mixed} value The value to search for
2898      * @return {Node} The found child or null if none was found
2899      */
2900     findChild : function(attribute, value){
2901         var cs = this.childNodes;
2902         for(var i = 0, len = cs.length; i < len; i++) {
2903                 if(cs[i].attributes[attribute] == value){
2904                     return cs[i];
2905                 }
2906         }
2907         return null;
2908     },
2909
2910     /**
2911      * Finds the first child by a custom function. The child matches if the function passed
2912      * returns true.
2913      * @param {Function} fn
2914      * @param {Object} scope (optional)
2915      * @return {Node} The found child or null if none was found
2916      */
2917     findChildBy : function(fn, scope){
2918         var cs = this.childNodes;
2919         for(var i = 0, len = cs.length; i < len; i++) {
2920                 if(fn.call(scope||cs[i], cs[i]) === true){
2921                     return cs[i];
2922                 }
2923         }
2924         return null;
2925     },
2926
2927     /**
2928      * Sorts this nodes children using the supplied sort function
2929      * @param {Function} fn
2930      * @param {Object} scope (optional)
2931      */
2932     sort : function(fn, scope){
2933         var cs = this.childNodes;
2934         var len = cs.length;
2935         if(len > 0){
2936             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2937             cs.sort(sortFn);
2938             for(var i = 0; i < len; i++){
2939                 var n = cs[i];
2940                 n.previousSibling = cs[i-1];
2941                 n.nextSibling = cs[i+1];
2942                 if(i == 0){
2943                     this.setFirstChild(n);
2944                 }
2945                 if(i == len-1){
2946                     this.setLastChild(n);
2947                 }
2948             }
2949         }
2950     },
2951
2952     /**
2953      * Returns true if this node is an ancestor (at any point) of the passed node.
2954      * @param {Node} node
2955      * @return {Boolean}
2956      */
2957     contains : function(node){
2958         return node.isAncestor(this);
2959     },
2960
2961     /**
2962      * Returns true if the passed node is an ancestor (at any point) of this node.
2963      * @param {Node} node
2964      * @return {Boolean}
2965      */
2966     isAncestor : function(node){
2967         var p = this.parentNode;
2968         while(p){
2969             if(p == node){
2970                 return true;
2971             }
2972             p = p.parentNode;
2973         }
2974         return false;
2975     },
2976
2977     toString : function(){
2978         return "[Node"+(this.id?" "+this.id:"")+"]";
2979     }
2980 });/*
2981  * Based on:
2982  * Ext JS Library 1.1.1
2983  * Copyright(c) 2006-2007, Ext JS, LLC.
2984  *
2985  * Originally Released Under LGPL - original licence link has changed is not relivant.
2986  *
2987  * Fork - LGPL
2988  * <script type="text/javascript">
2989  */
2990  (function(){ 
2991 /**
2992  * @class Roo.Layer
2993  * @extends Roo.Element
2994  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2995  * automatic maintaining of shadow/shim positions.
2996  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2997  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2998  * you can pass a string with a CSS class name. False turns off the shadow.
2999  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
3000  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
3001  * @cfg {String} cls CSS class to add to the element
3002  * @cfg {Number} zindex Starting z-index (defaults to 11000)
3003  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
3004  * @constructor
3005  * @param {Object} config An object with config options.
3006  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
3007  */
3008
3009 Roo.Layer = function(config, existingEl){
3010     config = config || {};
3011     var dh = Roo.DomHelper;
3012     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
3013     if(existingEl){
3014         this.dom = Roo.getDom(existingEl);
3015     }
3016     if(!this.dom){
3017         var o = config.dh || {tag: "div", cls: "x-layer"};
3018         this.dom = dh.append(pel, o);
3019     }
3020     if(config.cls){
3021         this.addClass(config.cls);
3022     }
3023     this.constrain = config.constrain !== false;
3024     this.visibilityMode = Roo.Element.VISIBILITY;
3025     if(config.id){
3026         this.id = this.dom.id = config.id;
3027     }else{
3028         this.id = Roo.id(this.dom);
3029     }
3030     this.zindex = config.zindex || this.getZIndex();
3031     this.position("absolute", this.zindex);
3032     if(config.shadow){
3033         this.shadowOffset = config.shadowOffset || 4;
3034         this.shadow = new Roo.Shadow({
3035             offset : this.shadowOffset,
3036             mode : config.shadow
3037         });
3038     }else{
3039         this.shadowOffset = 0;
3040     }
3041     this.useShim = config.shim !== false && Roo.useShims;
3042     this.useDisplay = config.useDisplay;
3043     this.hide();
3044 };
3045
3046 var supr = Roo.Element.prototype;
3047
3048 // shims are shared among layer to keep from having 100 iframes
3049 var shims = [];
3050
3051 Roo.extend(Roo.Layer, Roo.Element, {
3052
3053     getZIndex : function(){
3054         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3055     },
3056
3057     getShim : function(){
3058         if(!this.useShim){
3059             return null;
3060         }
3061         if(this.shim){
3062             return this.shim;
3063         }
3064         var shim = shims.shift();
3065         if(!shim){
3066             shim = this.createShim();
3067             shim.enableDisplayMode('block');
3068             shim.dom.style.display = 'none';
3069             shim.dom.style.visibility = 'visible';
3070         }
3071         var pn = this.dom.parentNode;
3072         if(shim.dom.parentNode != pn){
3073             pn.insertBefore(shim.dom, this.dom);
3074         }
3075         shim.setStyle('z-index', this.getZIndex()-2);
3076         this.shim = shim;
3077         return shim;
3078     },
3079
3080     hideShim : function(){
3081         if(this.shim){
3082             this.shim.setDisplayed(false);
3083             shims.push(this.shim);
3084             delete this.shim;
3085         }
3086     },
3087
3088     disableShadow : function(){
3089         if(this.shadow){
3090             this.shadowDisabled = true;
3091             this.shadow.hide();
3092             this.lastShadowOffset = this.shadowOffset;
3093             this.shadowOffset = 0;
3094         }
3095     },
3096
3097     enableShadow : function(show){
3098         if(this.shadow){
3099             this.shadowDisabled = false;
3100             this.shadowOffset = this.lastShadowOffset;
3101             delete this.lastShadowOffset;
3102             if(show){
3103                 this.sync(true);
3104             }
3105         }
3106     },
3107
3108     // private
3109     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3110     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3111     sync : function(doShow){
3112         var sw = this.shadow;
3113         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3114             var sh = this.getShim();
3115
3116             var w = this.getWidth(),
3117                 h = this.getHeight();
3118
3119             var l = this.getLeft(true),
3120                 t = this.getTop(true);
3121
3122             if(sw && !this.shadowDisabled){
3123                 if(doShow && !sw.isVisible()){
3124                     sw.show(this);
3125                 }else{
3126                     sw.realign(l, t, w, h);
3127                 }
3128                 if(sh){
3129                     if(doShow){
3130                        sh.show();
3131                     }
3132                     // fit the shim behind the shadow, so it is shimmed too
3133                     var a = sw.adjusts, s = sh.dom.style;
3134                     s.left = (Math.min(l, l+a.l))+"px";
3135                     s.top = (Math.min(t, t+a.t))+"px";
3136                     s.width = (w+a.w)+"px";
3137                     s.height = (h+a.h)+"px";
3138                 }
3139             }else if(sh){
3140                 if(doShow){
3141                    sh.show();
3142                 }
3143                 sh.setSize(w, h);
3144                 sh.setLeftTop(l, t);
3145             }
3146             
3147         }
3148     },
3149
3150     // private
3151     destroy : function(){
3152         this.hideShim();
3153         if(this.shadow){
3154             this.shadow.hide();
3155         }
3156         this.removeAllListeners();
3157         var pn = this.dom.parentNode;
3158         if(pn){
3159             pn.removeChild(this.dom);
3160         }
3161         Roo.Element.uncache(this.id);
3162     },
3163
3164     remove : function(){
3165         this.destroy();
3166     },
3167
3168     // private
3169     beginUpdate : function(){
3170         this.updating = true;
3171     },
3172
3173     // private
3174     endUpdate : function(){
3175         this.updating = false;
3176         this.sync(true);
3177     },
3178
3179     // private
3180     hideUnders : function(negOffset){
3181         if(this.shadow){
3182             this.shadow.hide();
3183         }
3184         this.hideShim();
3185     },
3186
3187     // private
3188     constrainXY : function(){
3189         if(this.constrain){
3190             var vw = Roo.lib.Dom.getViewWidth(),
3191                 vh = Roo.lib.Dom.getViewHeight();
3192             var s = Roo.get(document).getScroll();
3193
3194             var xy = this.getXY();
3195             var x = xy[0], y = xy[1];   
3196             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3197             // only move it if it needs it
3198             var moved = false;
3199             // first validate right/bottom
3200             if((x + w) > vw+s.left){
3201                 x = vw - w - this.shadowOffset;
3202                 moved = true;
3203             }
3204             if((y + h) > vh+s.top){
3205                 y = vh - h - this.shadowOffset;
3206                 moved = true;
3207             }
3208             // then make sure top/left isn't negative
3209             if(x < s.left){
3210                 x = s.left;
3211                 moved = true;
3212             }
3213             if(y < s.top){
3214                 y = s.top;
3215                 moved = true;
3216             }
3217             if(moved){
3218                 if(this.avoidY){
3219                     var ay = this.avoidY;
3220                     if(y <= ay && (y+h) >= ay){
3221                         y = ay-h-5;   
3222                     }
3223                 }
3224                 xy = [x, y];
3225                 this.storeXY(xy);
3226                 supr.setXY.call(this, xy);
3227                 this.sync();
3228             }
3229         }
3230     },
3231
3232     isVisible : function(){
3233         return this.visible;    
3234     },
3235
3236     // private
3237     showAction : function(){
3238         this.visible = true; // track visibility to prevent getStyle calls
3239         if(this.useDisplay === true){
3240             this.setDisplayed("");
3241         }else if(this.lastXY){
3242             supr.setXY.call(this, this.lastXY);
3243         }else if(this.lastLT){
3244             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3245         }
3246     },
3247
3248     // private
3249     hideAction : function(){
3250         this.visible = false;
3251         if(this.useDisplay === true){
3252             this.setDisplayed(false);
3253         }else{
3254             this.setLeftTop(-10000,-10000);
3255         }
3256     },
3257
3258     // overridden Element method
3259     setVisible : function(v, a, d, c, e){
3260         if(v){
3261             this.showAction();
3262         }
3263         if(a && v){
3264             var cb = function(){
3265                 this.sync(true);
3266                 if(c){
3267                     c();
3268                 }
3269             }.createDelegate(this);
3270             supr.setVisible.call(this, true, true, d, cb, e);
3271         }else{
3272             if(!v){
3273                 this.hideUnders(true);
3274             }
3275             var cb = c;
3276             if(a){
3277                 cb = function(){
3278                     this.hideAction();
3279                     if(c){
3280                         c();
3281                     }
3282                 }.createDelegate(this);
3283             }
3284             supr.setVisible.call(this, v, a, d, cb, e);
3285             if(v){
3286                 this.sync(true);
3287             }else if(!a){
3288                 this.hideAction();
3289             }
3290         }
3291     },
3292
3293     storeXY : function(xy){
3294         delete this.lastLT;
3295         this.lastXY = xy;
3296     },
3297
3298     storeLeftTop : function(left, top){
3299         delete this.lastXY;
3300         this.lastLT = [left, top];
3301     },
3302
3303     // private
3304     beforeFx : function(){
3305         this.beforeAction();
3306         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3307     },
3308
3309     // private
3310     afterFx : function(){
3311         Roo.Layer.superclass.afterFx.apply(this, arguments);
3312         this.sync(this.isVisible());
3313     },
3314
3315     // private
3316     beforeAction : function(){
3317         if(!this.updating && this.shadow){
3318             this.shadow.hide();
3319         }
3320     },
3321
3322     // overridden Element method
3323     setLeft : function(left){
3324         this.storeLeftTop(left, this.getTop(true));
3325         supr.setLeft.apply(this, arguments);
3326         this.sync();
3327     },
3328
3329     setTop : function(top){
3330         this.storeLeftTop(this.getLeft(true), top);
3331         supr.setTop.apply(this, arguments);
3332         this.sync();
3333     },
3334
3335     setLeftTop : function(left, top){
3336         this.storeLeftTop(left, top);
3337         supr.setLeftTop.apply(this, arguments);
3338         this.sync();
3339     },
3340
3341     setXY : function(xy, a, d, c, e){
3342         this.fixDisplay();
3343         this.beforeAction();
3344         this.storeXY(xy);
3345         var cb = this.createCB(c);
3346         supr.setXY.call(this, xy, a, d, cb, e);
3347         if(!a){
3348             cb();
3349         }
3350     },
3351
3352     // private
3353     createCB : function(c){
3354         var el = this;
3355         return function(){
3356             el.constrainXY();
3357             el.sync(true);
3358             if(c){
3359                 c();
3360             }
3361         };
3362     },
3363
3364     // overridden Element method
3365     setX : function(x, a, d, c, e){
3366         this.setXY([x, this.getY()], a, d, c, e);
3367     },
3368
3369     // overridden Element method
3370     setY : function(y, a, d, c, e){
3371         this.setXY([this.getX(), y], a, d, c, e);
3372     },
3373
3374     // overridden Element method
3375     setSize : function(w, h, a, d, c, e){
3376         this.beforeAction();
3377         var cb = this.createCB(c);
3378         supr.setSize.call(this, w, h, a, d, cb, e);
3379         if(!a){
3380             cb();
3381         }
3382     },
3383
3384     // overridden Element method
3385     setWidth : function(w, a, d, c, e){
3386         this.beforeAction();
3387         var cb = this.createCB(c);
3388         supr.setWidth.call(this, w, a, d, cb, e);
3389         if(!a){
3390             cb();
3391         }
3392     },
3393
3394     // overridden Element method
3395     setHeight : function(h, a, d, c, e){
3396         this.beforeAction();
3397         var cb = this.createCB(c);
3398         supr.setHeight.call(this, h, a, d, cb, e);
3399         if(!a){
3400             cb();
3401         }
3402     },
3403
3404     // overridden Element method
3405     setBounds : function(x, y, w, h, a, d, c, e){
3406         this.beforeAction();
3407         var cb = this.createCB(c);
3408         if(!a){
3409             this.storeXY([x, y]);
3410             supr.setXY.call(this, [x, y]);
3411             supr.setSize.call(this, w, h, a, d, cb, e);
3412             cb();
3413         }else{
3414             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3415         }
3416         return this;
3417     },
3418     
3419     /**
3420      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3421      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3422      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3423      * @param {Number} zindex The new z-index to set
3424      * @return {this} The Layer
3425      */
3426     setZIndex : function(zindex){
3427         this.zindex = zindex;
3428         this.setStyle("z-index", zindex + 2);
3429         if(this.shadow){
3430             this.shadow.setZIndex(zindex + 1);
3431         }
3432         if(this.shim){
3433             this.shim.setStyle("z-index", zindex);
3434         }
3435     }
3436 });
3437 })();/*
3438  * Based on:
3439  * Ext JS Library 1.1.1
3440  * Copyright(c) 2006-2007, Ext JS, LLC.
3441  *
3442  * Originally Released Under LGPL - original licence link has changed is not relivant.
3443  *
3444  * Fork - LGPL
3445  * <script type="text/javascript">
3446  */
3447
3448
3449 /**
3450  * @class Roo.Shadow
3451  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3452  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3453  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3454  * @constructor
3455  * Create a new Shadow
3456  * @param {Object} config The config object
3457  */
3458 Roo.Shadow = function(config){
3459     Roo.apply(this, config);
3460     if(typeof this.mode != "string"){
3461         this.mode = this.defaultMode;
3462     }
3463     var o = this.offset, a = {h: 0};
3464     var rad = Math.floor(this.offset/2);
3465     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3466         case "drop":
3467             a.w = 0;
3468             a.l = a.t = o;
3469             a.t -= 1;
3470             if(Roo.isIE){
3471                 a.l -= this.offset + rad;
3472                 a.t -= this.offset + rad;
3473                 a.w -= rad;
3474                 a.h -= rad;
3475                 a.t += 1;
3476             }
3477         break;
3478         case "sides":
3479             a.w = (o*2);
3480             a.l = -o;
3481             a.t = o-1;
3482             if(Roo.isIE){
3483                 a.l -= (this.offset - rad);
3484                 a.t -= this.offset + rad;
3485                 a.l += 1;
3486                 a.w -= (this.offset - rad)*2;
3487                 a.w -= rad + 1;
3488                 a.h -= 1;
3489             }
3490         break;
3491         case "frame":
3492             a.w = a.h = (o*2);
3493             a.l = a.t = -o;
3494             a.t += 1;
3495             a.h -= 2;
3496             if(Roo.isIE){
3497                 a.l -= (this.offset - rad);
3498                 a.t -= (this.offset - rad);
3499                 a.l += 1;
3500                 a.w -= (this.offset + rad + 1);
3501                 a.h -= (this.offset + rad);
3502                 a.h += 1;
3503             }
3504         break;
3505     };
3506
3507     this.adjusts = a;
3508 };
3509
3510 Roo.Shadow.prototype = {
3511     /**
3512      * @cfg {String} mode
3513      * The shadow display mode.  Supports the following options:<br />
3514      * sides: Shadow displays on both sides and bottom only<br />
3515      * frame: Shadow displays equally on all four sides<br />
3516      * drop: Traditional bottom-right drop shadow (default)
3517      */
3518     /**
3519      * @cfg {String} offset
3520      * The number of pixels to offset the shadow from the element (defaults to 4)
3521      */
3522     offset: 4,
3523
3524     // private
3525     defaultMode: "drop",
3526
3527     /**
3528      * Displays the shadow under the target element
3529      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3530      */
3531     show : function(target){
3532         target = Roo.get(target);
3533         if(!this.el){
3534             this.el = Roo.Shadow.Pool.pull();
3535             if(this.el.dom.nextSibling != target.dom){
3536                 this.el.insertBefore(target);
3537             }
3538         }
3539         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3540         if(Roo.isIE){
3541             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3542         }
3543         this.realign(
3544             target.getLeft(true),
3545             target.getTop(true),
3546             target.getWidth(),
3547             target.getHeight()
3548         );
3549         this.el.dom.style.display = "block";
3550     },
3551
3552     /**
3553      * Returns true if the shadow is visible, else false
3554      */
3555     isVisible : function(){
3556         return this.el ? true : false;  
3557     },
3558
3559     /**
3560      * Direct alignment when values are already available. Show must be called at least once before
3561      * calling this method to ensure it is initialized.
3562      * @param {Number} left The target element left position
3563      * @param {Number} top The target element top position
3564      * @param {Number} width The target element width
3565      * @param {Number} height The target element height
3566      */
3567     realign : function(l, t, w, h){
3568         if(!this.el){
3569             return;
3570         }
3571         var a = this.adjusts, d = this.el.dom, s = d.style;
3572         var iea = 0;
3573         s.left = (l+a.l)+"px";
3574         s.top = (t+a.t)+"px";
3575         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3576  
3577         if(s.width != sws || s.height != shs){
3578             s.width = sws;
3579             s.height = shs;
3580             if(!Roo.isIE){
3581                 var cn = d.childNodes;
3582                 var sww = Math.max(0, (sw-12))+"px";
3583                 cn[0].childNodes[1].style.width = sww;
3584                 cn[1].childNodes[1].style.width = sww;
3585                 cn[2].childNodes[1].style.width = sww;
3586                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3587             }
3588         }
3589     },
3590
3591     /**
3592      * Hides this shadow
3593      */
3594     hide : function(){
3595         if(this.el){
3596             this.el.dom.style.display = "none";
3597             Roo.Shadow.Pool.push(this.el);
3598             delete this.el;
3599         }
3600     },
3601
3602     /**
3603      * Adjust the z-index of this shadow
3604      * @param {Number} zindex The new z-index
3605      */
3606     setZIndex : function(z){
3607         this.zIndex = z;
3608         if(this.el){
3609             this.el.setStyle("z-index", z);
3610         }
3611     }
3612 };
3613
3614 // Private utility class that manages the internal Shadow cache
3615 Roo.Shadow.Pool = function(){
3616     var p = [];
3617     var markup = Roo.isIE ?
3618                  '<div class="x-ie-shadow"></div>' :
3619                  '<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>';
3620     return {
3621         pull : function(){
3622             var sh = p.shift();
3623             if(!sh){
3624                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3625                 sh.autoBoxAdjust = false;
3626             }
3627             return sh;
3628         },
3629
3630         push : function(sh){
3631             p.push(sh);
3632         }
3633     };
3634 }();/*
3635  * Based on:
3636  * Ext JS Library 1.1.1
3637  * Copyright(c) 2006-2007, Ext JS, LLC.
3638  *
3639  * Originally Released Under LGPL - original licence link has changed is not relivant.
3640  *
3641  * Fork - LGPL
3642  * <script type="text/javascript">
3643  */
3644
3645
3646 /**
3647  * @class Roo.SplitBar
3648  * @extends Roo.util.Observable
3649  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3650  * <br><br>
3651  * Usage:
3652  * <pre><code>
3653 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3654                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3655 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3656 split.minSize = 100;
3657 split.maxSize = 600;
3658 split.animate = true;
3659 split.on('moved', splitterMoved);
3660 </code></pre>
3661  * @constructor
3662  * Create a new SplitBar
3663  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3664  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3665  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3666  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3667                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3668                         position of the SplitBar).
3669  */
3670 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3671     
3672     /** @private */
3673     this.el = Roo.get(dragElement, true);
3674     this.el.dom.unselectable = "on";
3675     /** @private */
3676     this.resizingEl = Roo.get(resizingElement, true);
3677
3678     /**
3679      * @private
3680      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3681      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3682      * @type Number
3683      */
3684     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3685     
3686     /**
3687      * The minimum size of the resizing element. (Defaults to 0)
3688      * @type Number
3689      */
3690     this.minSize = 0;
3691     
3692     /**
3693      * The maximum size of the resizing element. (Defaults to 2000)
3694      * @type Number
3695      */
3696     this.maxSize = 2000;
3697     
3698     /**
3699      * Whether to animate the transition to the new size
3700      * @type Boolean
3701      */
3702     this.animate = false;
3703     
3704     /**
3705      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3706      * @type Boolean
3707      */
3708     this.useShim = false;
3709     
3710     /** @private */
3711     this.shim = null;
3712     
3713     if(!existingProxy){
3714         /** @private */
3715         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3716     }else{
3717         this.proxy = Roo.get(existingProxy).dom;
3718     }
3719     /** @private */
3720     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3721     
3722     /** @private */
3723     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3724     
3725     /** @private */
3726     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3727     
3728     /** @private */
3729     this.dragSpecs = {};
3730     
3731     /**
3732      * @private The adapter to use to positon and resize elements
3733      */
3734     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3735     this.adapter.init(this);
3736     
3737     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3738         /** @private */
3739         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3740         this.el.addClass("x-splitbar-h");
3741     }else{
3742         /** @private */
3743         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3744         this.el.addClass("x-splitbar-v");
3745     }
3746     
3747     this.addEvents({
3748         /**
3749          * @event resize
3750          * Fires when the splitter is moved (alias for {@link #event-moved})
3751          * @param {Roo.SplitBar} this
3752          * @param {Number} newSize the new width or height
3753          */
3754         "resize" : true,
3755         /**
3756          * @event moved
3757          * Fires when the splitter is moved
3758          * @param {Roo.SplitBar} this
3759          * @param {Number} newSize the new width or height
3760          */
3761         "moved" : true,
3762         /**
3763          * @event beforeresize
3764          * Fires before the splitter is dragged
3765          * @param {Roo.SplitBar} this
3766          */
3767         "beforeresize" : true,
3768
3769         "beforeapply" : true
3770     });
3771
3772     Roo.util.Observable.call(this);
3773 };
3774
3775 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3776     onStartProxyDrag : function(x, y){
3777         this.fireEvent("beforeresize", this);
3778         if(!this.overlay){
3779             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3780             o.unselectable();
3781             o.enableDisplayMode("block");
3782             // all splitbars share the same overlay
3783             Roo.SplitBar.prototype.overlay = o;
3784         }
3785         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3786         this.overlay.show();
3787         Roo.get(this.proxy).setDisplayed("block");
3788         var size = this.adapter.getElementSize(this);
3789         this.activeMinSize = this.getMinimumSize();;
3790         this.activeMaxSize = this.getMaximumSize();;
3791         var c1 = size - this.activeMinSize;
3792         var c2 = Math.max(this.activeMaxSize - size, 0);
3793         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3794             this.dd.resetConstraints();
3795             this.dd.setXConstraint(
3796                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3797                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3798             );
3799             this.dd.setYConstraint(0, 0);
3800         }else{
3801             this.dd.resetConstraints();
3802             this.dd.setXConstraint(0, 0);
3803             this.dd.setYConstraint(
3804                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3805                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3806             );
3807          }
3808         this.dragSpecs.startSize = size;
3809         this.dragSpecs.startPoint = [x, y];
3810         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3811     },
3812     
3813     /** 
3814      * @private Called after the drag operation by the DDProxy
3815      */
3816     onEndProxyDrag : function(e){
3817         Roo.get(this.proxy).setDisplayed(false);
3818         var endPoint = Roo.lib.Event.getXY(e);
3819         if(this.overlay){
3820             this.overlay.hide();
3821         }
3822         var newSize;
3823         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3824             newSize = this.dragSpecs.startSize + 
3825                 (this.placement == Roo.SplitBar.LEFT ?
3826                     endPoint[0] - this.dragSpecs.startPoint[0] :
3827                     this.dragSpecs.startPoint[0] - endPoint[0]
3828                 );
3829         }else{
3830             newSize = this.dragSpecs.startSize + 
3831                 (this.placement == Roo.SplitBar.TOP ?
3832                     endPoint[1] - this.dragSpecs.startPoint[1] :
3833                     this.dragSpecs.startPoint[1] - endPoint[1]
3834                 );
3835         }
3836         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3837         if(newSize != this.dragSpecs.startSize){
3838             if(this.fireEvent('beforeapply', this, newSize) !== false){
3839                 this.adapter.setElementSize(this, newSize);
3840                 this.fireEvent("moved", this, newSize);
3841                 this.fireEvent("resize", this, newSize);
3842             }
3843         }
3844     },
3845     
3846     /**
3847      * Get the adapter this SplitBar uses
3848      * @return The adapter object
3849      */
3850     getAdapter : function(){
3851         return this.adapter;
3852     },
3853     
3854     /**
3855      * Set the adapter this SplitBar uses
3856      * @param {Object} adapter A SplitBar adapter object
3857      */
3858     setAdapter : function(adapter){
3859         this.adapter = adapter;
3860         this.adapter.init(this);
3861     },
3862     
3863     /**
3864      * Gets the minimum size for the resizing element
3865      * @return {Number} The minimum size
3866      */
3867     getMinimumSize : function(){
3868         return this.minSize;
3869     },
3870     
3871     /**
3872      * Sets the minimum size for the resizing element
3873      * @param {Number} minSize The minimum size
3874      */
3875     setMinimumSize : function(minSize){
3876         this.minSize = minSize;
3877     },
3878     
3879     /**
3880      * Gets the maximum size for the resizing element
3881      * @return {Number} The maximum size
3882      */
3883     getMaximumSize : function(){
3884         return this.maxSize;
3885     },
3886     
3887     /**
3888      * Sets the maximum size for the resizing element
3889      * @param {Number} maxSize The maximum size
3890      */
3891     setMaximumSize : function(maxSize){
3892         this.maxSize = maxSize;
3893     },
3894     
3895     /**
3896      * Sets the initialize size for the resizing element
3897      * @param {Number} size The initial size
3898      */
3899     setCurrentSize : function(size){
3900         var oldAnimate = this.animate;
3901         this.animate = false;
3902         this.adapter.setElementSize(this, size);
3903         this.animate = oldAnimate;
3904     },
3905     
3906     /**
3907      * Destroy this splitbar. 
3908      * @param {Boolean} removeEl True to remove the element
3909      */
3910     destroy : function(removeEl){
3911         if(this.shim){
3912             this.shim.remove();
3913         }
3914         this.dd.unreg();
3915         this.proxy.parentNode.removeChild(this.proxy);
3916         if(removeEl){
3917             this.el.remove();
3918         }
3919     }
3920 });
3921
3922 /**
3923  * @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.
3924  */
3925 Roo.SplitBar.createProxy = function(dir){
3926     var proxy = new Roo.Element(document.createElement("div"));
3927     proxy.unselectable();
3928     var cls = 'x-splitbar-proxy';
3929     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3930     document.body.appendChild(proxy.dom);
3931     return proxy.dom;
3932 };
3933
3934 /** 
3935  * @class Roo.SplitBar.BasicLayoutAdapter
3936  * Default Adapter. It assumes the splitter and resizing element are not positioned
3937  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3938  */
3939 Roo.SplitBar.BasicLayoutAdapter = function(){
3940 };
3941
3942 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3943     // do nothing for now
3944     init : function(s){
3945     
3946     },
3947     /**
3948      * Called before drag operations to get the current size of the resizing element. 
3949      * @param {Roo.SplitBar} s The SplitBar using this adapter
3950      */
3951      getElementSize : function(s){
3952         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3953             return s.resizingEl.getWidth();
3954         }else{
3955             return s.resizingEl.getHeight();
3956         }
3957     },
3958     
3959     /**
3960      * Called after drag operations to set the size of the resizing element.
3961      * @param {Roo.SplitBar} s The SplitBar using this adapter
3962      * @param {Number} newSize The new size to set
3963      * @param {Function} onComplete A function to be invoked when resizing is complete
3964      */
3965     setElementSize : function(s, newSize, onComplete){
3966         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3967             if(!s.animate){
3968                 s.resizingEl.setWidth(newSize);
3969                 if(onComplete){
3970                     onComplete(s, newSize);
3971                 }
3972             }else{
3973                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3974             }
3975         }else{
3976             
3977             if(!s.animate){
3978                 s.resizingEl.setHeight(newSize);
3979                 if(onComplete){
3980                     onComplete(s, newSize);
3981                 }
3982             }else{
3983                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3984             }
3985         }
3986     }
3987 };
3988
3989 /** 
3990  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3991  * @extends Roo.SplitBar.BasicLayoutAdapter
3992  * Adapter that  moves the splitter element to align with the resized sizing element. 
3993  * Used with an absolute positioned SplitBar.
3994  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3995  * document.body, make sure you assign an id to the body element.
3996  */
3997 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3998     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3999     this.container = Roo.get(container);
4000 };
4001
4002 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
4003     init : function(s){
4004         this.basic.init(s);
4005     },
4006     
4007     getElementSize : function(s){
4008         return this.basic.getElementSize(s);
4009     },
4010     
4011     setElementSize : function(s, newSize, onComplete){
4012         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
4013     },
4014     
4015     moveSplitter : function(s){
4016         var yes = Roo.SplitBar;
4017         switch(s.placement){
4018             case yes.LEFT:
4019                 s.el.setX(s.resizingEl.getRight());
4020                 break;
4021             case yes.RIGHT:
4022                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4023                 break;
4024             case yes.TOP:
4025                 s.el.setY(s.resizingEl.getBottom());
4026                 break;
4027             case yes.BOTTOM:
4028                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4029                 break;
4030         }
4031     }
4032 };
4033
4034 /**
4035  * Orientation constant - Create a vertical SplitBar
4036  * @static
4037  * @type Number
4038  */
4039 Roo.SplitBar.VERTICAL = 1;
4040
4041 /**
4042  * Orientation constant - Create a horizontal SplitBar
4043  * @static
4044  * @type Number
4045  */
4046 Roo.SplitBar.HORIZONTAL = 2;
4047
4048 /**
4049  * Placement constant - The resizing element is to the left of the splitter element
4050  * @static
4051  * @type Number
4052  */
4053 Roo.SplitBar.LEFT = 1;
4054
4055 /**
4056  * Placement constant - The resizing element is to the right of the splitter element
4057  * @static
4058  * @type Number
4059  */
4060 Roo.SplitBar.RIGHT = 2;
4061
4062 /**
4063  * Placement constant - The resizing element is positioned above the splitter element
4064  * @static
4065  * @type Number
4066  */
4067 Roo.SplitBar.TOP = 3;
4068
4069 /**
4070  * Placement constant - The resizing element is positioned under splitter element
4071  * @static
4072  * @type Number
4073  */
4074 Roo.SplitBar.BOTTOM = 4;
4075 /*
4076  * Based on:
4077  * Ext JS Library 1.1.1
4078  * Copyright(c) 2006-2007, Ext JS, LLC.
4079  *
4080  * Originally Released Under LGPL - original licence link has changed is not relivant.
4081  *
4082  * Fork - LGPL
4083  * <script type="text/javascript">
4084  */
4085
4086 /**
4087  * @class Roo.View
4088  * @extends Roo.util.Observable
4089  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4090  * This class also supports single and multi selection modes. <br>
4091  * Create a data model bound view:
4092  <pre><code>
4093  var store = new Roo.data.Store(...);
4094
4095  var view = new Roo.View({
4096     el : "my-element",
4097     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4098  
4099     singleSelect: true,
4100     selectedClass: "ydataview-selected",
4101     store: store
4102  });
4103
4104  // listen for node click?
4105  view.on("click", function(vw, index, node, e){
4106  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4107  });
4108
4109  // load XML data
4110  dataModel.load("foobar.xml");
4111  </code></pre>
4112  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4113  * <br><br>
4114  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4115  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4116  * 
4117  * Note: old style constructor is still suported (container, template, config)
4118  * 
4119  * @constructor
4120  * Create a new View
4121  * @param {Object} config The config object
4122  * 
4123  */
4124 Roo.View = function(config, depreciated_tpl, depreciated_config){
4125     
4126     this.parent = false;
4127     
4128     if (typeof(depreciated_tpl) == 'undefined') {
4129         // new way.. - universal constructor.
4130         Roo.apply(this, config);
4131         this.el  = Roo.get(this.el);
4132     } else {
4133         // old format..
4134         this.el  = Roo.get(config);
4135         this.tpl = depreciated_tpl;
4136         Roo.apply(this, depreciated_config);
4137     }
4138     this.wrapEl  = this.el.wrap().wrap();
4139     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4140     
4141     
4142     if(typeof(this.tpl) == "string"){
4143         this.tpl = new Roo.Template(this.tpl);
4144     } else {
4145         // support xtype ctors..
4146         this.tpl = new Roo.factory(this.tpl, Roo);
4147     }
4148     
4149     
4150     this.tpl.compile();
4151     
4152     /** @private */
4153     this.addEvents({
4154         /**
4155          * @event beforeclick
4156          * Fires before a click is processed. Returns false to cancel the default action.
4157          * @param {Roo.View} this
4158          * @param {Number} index The index of the target node
4159          * @param {HTMLElement} node The target node
4160          * @param {Roo.EventObject} e The raw event object
4161          */
4162             "beforeclick" : true,
4163         /**
4164          * @event click
4165          * Fires when a template node is clicked.
4166          * @param {Roo.View} this
4167          * @param {Number} index The index of the target node
4168          * @param {HTMLElement} node The target node
4169          * @param {Roo.EventObject} e The raw event object
4170          */
4171             "click" : true,
4172         /**
4173          * @event dblclick
4174          * Fires when a template node is double clicked.
4175          * @param {Roo.View} this
4176          * @param {Number} index The index of the target node
4177          * @param {HTMLElement} node The target node
4178          * @param {Roo.EventObject} e The raw event object
4179          */
4180             "dblclick" : true,
4181         /**
4182          * @event contextmenu
4183          * Fires when a template node is right clicked.
4184          * @param {Roo.View} this
4185          * @param {Number} index The index of the target node
4186          * @param {HTMLElement} node The target node
4187          * @param {Roo.EventObject} e The raw event object
4188          */
4189             "contextmenu" : true,
4190         /**
4191          * @event selectionchange
4192          * Fires when the selected nodes change.
4193          * @param {Roo.View} this
4194          * @param {Array} selections Array of the selected nodes
4195          */
4196             "selectionchange" : true,
4197     
4198         /**
4199          * @event beforeselect
4200          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4201          * @param {Roo.View} this
4202          * @param {HTMLElement} node The node to be selected
4203          * @param {Array} selections Array of currently selected nodes
4204          */
4205             "beforeselect" : true,
4206         /**
4207          * @event preparedata
4208          * Fires on every row to render, to allow you to change the data.
4209          * @param {Roo.View} this
4210          * @param {Object} data to be rendered (change this)
4211          */
4212           "preparedata" : true
4213           
4214           
4215         });
4216
4217
4218
4219     this.el.on({
4220         "click": this.onClick,
4221         "dblclick": this.onDblClick,
4222         "contextmenu": this.onContextMenu,
4223         scope:this
4224     });
4225
4226     this.selections = [];
4227     this.nodes = [];
4228     this.cmp = new Roo.CompositeElementLite([]);
4229     if(this.store){
4230         this.store = Roo.factory(this.store, Roo.data);
4231         this.setStore(this.store, true);
4232     }
4233     
4234     if ( this.footer && this.footer.xtype) {
4235            
4236          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4237         
4238         this.footer.dataSource = this.store;
4239         this.footer.container = fctr;
4240         this.footer = Roo.factory(this.footer, Roo);
4241         fctr.insertFirst(this.el);
4242         
4243         // this is a bit insane - as the paging toolbar seems to detach the el..
4244 //        dom.parentNode.parentNode.parentNode
4245          // they get detached?
4246     }
4247     
4248     
4249     Roo.View.superclass.constructor.call(this);
4250     
4251     
4252 };
4253
4254 Roo.extend(Roo.View, Roo.util.Observable, {
4255     
4256      /**
4257      * @cfg {Roo.data.Store} store Data store to load data from.
4258      */
4259     store : false,
4260     
4261     /**
4262      * @cfg {String|Roo.Element} el The container element.
4263      */
4264     el : '',
4265     
4266     /**
4267      * @cfg {String|Roo.Template} tpl The template used by this View 
4268      */
4269     tpl : false,
4270     /**
4271      * @cfg {String} dataName the named area of the template to use as the data area
4272      *                          Works with domtemplates roo-name="name"
4273      */
4274     dataName: false,
4275     /**
4276      * @cfg {String} selectedClass The css class to add to selected nodes
4277      */
4278     selectedClass : "x-view-selected",
4279      /**
4280      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4281      */
4282     emptyText : "",
4283     
4284     /**
4285      * @cfg {String} text to display on mask (default Loading)
4286      */
4287     mask : false,
4288     /**
4289      * @cfg {Boolean} multiSelect Allow multiple selection
4290      */
4291     multiSelect : false,
4292     /**
4293      * @cfg {Boolean} singleSelect Allow single selection
4294      */
4295     singleSelect:  false,
4296     
4297     /**
4298      * @cfg {Boolean} toggleSelect - selecting 
4299      */
4300     toggleSelect : false,
4301     
4302     /**
4303      * @cfg {Boolean} tickable - selecting 
4304      */
4305     tickable : false,
4306     
4307     /**
4308      * Returns the element this view is bound to.
4309      * @return {Roo.Element}
4310      */
4311     getEl : function(){
4312         return this.wrapEl;
4313     },
4314     
4315     
4316
4317     /**
4318      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4319      */
4320     refresh : function(){
4321         //Roo.log('refresh');
4322         var t = this.tpl;
4323         
4324         // if we are using something like 'domtemplate', then
4325         // the what gets used is:
4326         // t.applySubtemplate(NAME, data, wrapping data..)
4327         // the outer template then get' applied with
4328         //     the store 'extra data'
4329         // and the body get's added to the
4330         //      roo-name="data" node?
4331         //      <span class='roo-tpl-{name}'></span> ?????
4332         
4333         
4334         
4335         this.clearSelections();
4336         this.el.update("");
4337         var html = [];
4338         var records = this.store.getRange();
4339         if(records.length < 1) {
4340             
4341             // is this valid??  = should it render a template??
4342             
4343             this.el.update(this.emptyText);
4344             return;
4345         }
4346         var el = this.el;
4347         if (this.dataName) {
4348             this.el.update(t.apply(this.store.meta)); //????
4349             el = this.el.child('.roo-tpl-' + this.dataName);
4350         }
4351         
4352         for(var i = 0, len = records.length; i < len; i++){
4353             var data = this.prepareData(records[i].data, i, records[i]);
4354             this.fireEvent("preparedata", this, data, i, records[i]);
4355             
4356             var d = Roo.apply({}, data);
4357             
4358             if(this.tickable){
4359                 Roo.apply(d, {'roo-id' : Roo.id()});
4360                 
4361                 var _this = this;
4362             
4363                 Roo.each(this.parent.item, function(item){
4364                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4365                         return;
4366                     }
4367                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4368                 });
4369             }
4370             
4371             html[html.length] = Roo.util.Format.trim(
4372                 this.dataName ?
4373                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4374                     t.apply(d)
4375             );
4376         }
4377         
4378         
4379         
4380         el.update(html.join(""));
4381         this.nodes = el.dom.childNodes;
4382         this.updateIndexes(0);
4383     },
4384     
4385
4386     /**
4387      * Function to override to reformat the data that is sent to
4388      * the template for each node.
4389      * DEPRICATED - use the preparedata event handler.
4390      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4391      * a JSON object for an UpdateManager bound view).
4392      */
4393     prepareData : function(data, index, record)
4394     {
4395         this.fireEvent("preparedata", this, data, index, record);
4396         return data;
4397     },
4398
4399     onUpdate : function(ds, record){
4400         // Roo.log('on update');   
4401         this.clearSelections();
4402         var index = this.store.indexOf(record);
4403         var n = this.nodes[index];
4404         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4405         n.parentNode.removeChild(n);
4406         this.updateIndexes(index, index);
4407     },
4408
4409     
4410     
4411 // --------- FIXME     
4412     onAdd : function(ds, records, index)
4413     {
4414         //Roo.log(['on Add', ds, records, index] );        
4415         this.clearSelections();
4416         if(this.nodes.length == 0){
4417             this.refresh();
4418             return;
4419         }
4420         var n = this.nodes[index];
4421         for(var i = 0, len = records.length; i < len; i++){
4422             var d = this.prepareData(records[i].data, i, records[i]);
4423             if(n){
4424                 this.tpl.insertBefore(n, d);
4425             }else{
4426                 
4427                 this.tpl.append(this.el, d);
4428             }
4429         }
4430         this.updateIndexes(index);
4431     },
4432
4433     onRemove : function(ds, record, index){
4434        // Roo.log('onRemove');
4435         this.clearSelections();
4436         var el = this.dataName  ?
4437             this.el.child('.roo-tpl-' + this.dataName) :
4438             this.el; 
4439         
4440         el.dom.removeChild(this.nodes[index]);
4441         this.updateIndexes(index);
4442     },
4443
4444     /**
4445      * Refresh an individual node.
4446      * @param {Number} index
4447      */
4448     refreshNode : function(index){
4449         this.onUpdate(this.store, this.store.getAt(index));
4450     },
4451
4452     updateIndexes : function(startIndex, endIndex){
4453         var ns = this.nodes;
4454         startIndex = startIndex || 0;
4455         endIndex = endIndex || ns.length - 1;
4456         for(var i = startIndex; i <= endIndex; i++){
4457             ns[i].nodeIndex = i;
4458         }
4459     },
4460
4461     /**
4462      * Changes the data store this view uses and refresh the view.
4463      * @param {Store} store
4464      */
4465     setStore : function(store, initial){
4466         if(!initial && this.store){
4467             this.store.un("datachanged", this.refresh);
4468             this.store.un("add", this.onAdd);
4469             this.store.un("remove", this.onRemove);
4470             this.store.un("update", this.onUpdate);
4471             this.store.un("clear", this.refresh);
4472             this.store.un("beforeload", this.onBeforeLoad);
4473             this.store.un("load", this.onLoad);
4474             this.store.un("loadexception", this.onLoad);
4475         }
4476         if(store){
4477           
4478             store.on("datachanged", this.refresh, this);
4479             store.on("add", this.onAdd, this);
4480             store.on("remove", this.onRemove, this);
4481             store.on("update", this.onUpdate, this);
4482             store.on("clear", this.refresh, this);
4483             store.on("beforeload", this.onBeforeLoad, this);
4484             store.on("load", this.onLoad, this);
4485             store.on("loadexception", this.onLoad, this);
4486         }
4487         
4488         if(store){
4489             this.refresh();
4490         }
4491     },
4492     /**
4493      * onbeforeLoad - masks the loading area.
4494      *
4495      */
4496     onBeforeLoad : function(store,opts)
4497     {
4498          //Roo.log('onBeforeLoad');   
4499         if (!opts.add) {
4500             this.el.update("");
4501         }
4502         this.el.mask(this.mask ? this.mask : "Loading" ); 
4503     },
4504     onLoad : function ()
4505     {
4506         this.el.unmask();
4507     },
4508     
4509
4510     /**
4511      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4512      * @param {HTMLElement} node
4513      * @return {HTMLElement} The template node
4514      */
4515     findItemFromChild : function(node){
4516         var el = this.dataName  ?
4517             this.el.child('.roo-tpl-' + this.dataName,true) :
4518             this.el.dom; 
4519         
4520         if(!node || node.parentNode == el){
4521                     return node;
4522             }
4523             var p = node.parentNode;
4524             while(p && p != el){
4525             if(p.parentNode == el){
4526                 return p;
4527             }
4528             p = p.parentNode;
4529         }
4530             return null;
4531     },
4532
4533     /** @ignore */
4534     onClick : function(e){
4535         var item = this.findItemFromChild(e.getTarget());
4536         if(item){
4537             var index = this.indexOf(item);
4538             if(this.onItemClick(item, index, e) !== false){
4539                 this.fireEvent("click", this, index, item, e);
4540             }
4541         }else{
4542             this.clearSelections();
4543         }
4544     },
4545
4546     /** @ignore */
4547     onContextMenu : function(e){
4548         var item = this.findItemFromChild(e.getTarget());
4549         if(item){
4550             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4551         }
4552     },
4553
4554     /** @ignore */
4555     onDblClick : function(e){
4556         var item = this.findItemFromChild(e.getTarget());
4557         if(item){
4558             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4559         }
4560     },
4561
4562     onItemClick : function(item, index, e)
4563     {
4564         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4565             return false;
4566         }
4567         if (this.toggleSelect) {
4568             var m = this.isSelected(item) ? 'unselect' : 'select';
4569             //Roo.log(m);
4570             var _t = this;
4571             _t[m](item, true, false);
4572             return true;
4573         }
4574         if(this.multiSelect || this.singleSelect){
4575             if(this.multiSelect && e.shiftKey && this.lastSelection){
4576                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4577             }else{
4578                 this.select(item, this.multiSelect && e.ctrlKey);
4579                 this.lastSelection = item;
4580             }
4581             
4582             if(!this.tickable){
4583                 e.preventDefault();
4584             }
4585             
4586         }
4587         return true;
4588     },
4589
4590     /**
4591      * Get the number of selected nodes.
4592      * @return {Number}
4593      */
4594     getSelectionCount : function(){
4595         return this.selections.length;
4596     },
4597
4598     /**
4599      * Get the currently selected nodes.
4600      * @return {Array} An array of HTMLElements
4601      */
4602     getSelectedNodes : function(){
4603         return this.selections;
4604     },
4605
4606     /**
4607      * Get the indexes of the selected nodes.
4608      * @return {Array}
4609      */
4610     getSelectedIndexes : function(){
4611         var indexes = [], s = this.selections;
4612         for(var i = 0, len = s.length; i < len; i++){
4613             indexes.push(s[i].nodeIndex);
4614         }
4615         return indexes;
4616     },
4617
4618     /**
4619      * Clear all selections
4620      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4621      */
4622     clearSelections : function(suppressEvent){
4623         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4624             this.cmp.elements = this.selections;
4625             this.cmp.removeClass(this.selectedClass);
4626             this.selections = [];
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631     },
4632
4633     /**
4634      * Returns true if the passed node is selected
4635      * @param {HTMLElement/Number} node The node or node index
4636      * @return {Boolean}
4637      */
4638     isSelected : function(node){
4639         var s = this.selections;
4640         if(s.length < 1){
4641             return false;
4642         }
4643         node = this.getNode(node);
4644         return s.indexOf(node) !== -1;
4645     },
4646
4647     /**
4648      * Selects 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 to keep existing selections
4651      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4652      */
4653     select : function(nodeInfo, keepExisting, suppressEvent){
4654         if(nodeInfo instanceof Array){
4655             if(!keepExisting){
4656                 this.clearSelections(true);
4657             }
4658             for(var i = 0, len = nodeInfo.length; i < len; i++){
4659                 this.select(nodeInfo[i], true, true);
4660             }
4661             return;
4662         } 
4663         var node = this.getNode(nodeInfo);
4664         if(!node || this.isSelected(node)){
4665             return; // already selected.
4666         }
4667         if(!keepExisting){
4668             this.clearSelections(true);
4669         }
4670         
4671         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4672             Roo.fly(node).addClass(this.selectedClass);
4673             this.selections.push(node);
4674             if(!suppressEvent){
4675                 this.fireEvent("selectionchange", this, this.selections);
4676             }
4677         }
4678         
4679         
4680     },
4681       /**
4682      * Unselects nodes.
4683      * @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
4684      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4685      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4686      */
4687     unselect : function(nodeInfo, keepExisting, suppressEvent)
4688     {
4689         if(nodeInfo instanceof Array){
4690             Roo.each(this.selections, function(s) {
4691                 this.unselect(s, nodeInfo);
4692             }, this);
4693             return;
4694         }
4695         var node = this.getNode(nodeInfo);
4696         if(!node || !this.isSelected(node)){
4697             //Roo.log("not selected");
4698             return; // not selected.
4699         }
4700         // fireevent???
4701         var ns = [];
4702         Roo.each(this.selections, function(s) {
4703             if (s == node ) {
4704                 Roo.fly(node).removeClass(this.selectedClass);
4705
4706                 return;
4707             }
4708             ns.push(s);
4709         },this);
4710         
4711         this.selections= ns;
4712         this.fireEvent("selectionchange", this, this.selections);
4713     },
4714
4715     /**
4716      * Gets a template node.
4717      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4718      * @return {HTMLElement} The node or null if it wasn't found
4719      */
4720     getNode : function(nodeInfo){
4721         if(typeof nodeInfo == "string"){
4722             return document.getElementById(nodeInfo);
4723         }else if(typeof nodeInfo == "number"){
4724             return this.nodes[nodeInfo];
4725         }
4726         return nodeInfo;
4727     },
4728
4729     /**
4730      * Gets a range template nodes.
4731      * @param {Number} startIndex
4732      * @param {Number} endIndex
4733      * @return {Array} An array of nodes
4734      */
4735     getNodes : function(start, end){
4736         var ns = this.nodes;
4737         start = start || 0;
4738         end = typeof end == "undefined" ? ns.length - 1 : end;
4739         var nodes = [];
4740         if(start <= end){
4741             for(var i = start; i <= end; i++){
4742                 nodes.push(ns[i]);
4743             }
4744         } else{
4745             for(var i = start; i >= end; i--){
4746                 nodes.push(ns[i]);
4747             }
4748         }
4749         return nodes;
4750     },
4751
4752     /**
4753      * Finds the index of the passed node
4754      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4755      * @return {Number} The index of the node or -1
4756      */
4757     indexOf : function(node){
4758         node = this.getNode(node);
4759         if(typeof node.nodeIndex == "number"){
4760             return node.nodeIndex;
4761         }
4762         var ns = this.nodes;
4763         for(var i = 0, len = ns.length; i < len; i++){
4764             if(ns[i] == node){
4765                 return i;
4766             }
4767         }
4768         return -1;
4769     }
4770 });
4771 /*
4772  * Based on:
4773  * Ext JS Library 1.1.1
4774  * Copyright(c) 2006-2007, Ext JS, LLC.
4775  *
4776  * Originally Released Under LGPL - original licence link has changed is not relivant.
4777  *
4778  * Fork - LGPL
4779  * <script type="text/javascript">
4780  */
4781
4782 /**
4783  * @class Roo.JsonView
4784  * @extends Roo.View
4785  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4786 <pre><code>
4787 var view = new Roo.JsonView({
4788     container: "my-element",
4789     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4790     multiSelect: true, 
4791     jsonRoot: "data" 
4792 });
4793
4794 // listen for node click?
4795 view.on("click", function(vw, index, node, e){
4796     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4797 });
4798
4799 // direct load of JSON data
4800 view.load("foobar.php");
4801
4802 // Example from my blog list
4803 var tpl = new Roo.Template(
4804     '&lt;div class="entry"&gt;' +
4805     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4806     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4807     "&lt;/div&gt;&lt;hr /&gt;"
4808 );
4809
4810 var moreView = new Roo.JsonView({
4811     container :  "entry-list", 
4812     template : tpl,
4813     jsonRoot: "posts"
4814 });
4815 moreView.on("beforerender", this.sortEntries, this);
4816 moreView.load({
4817     url: "/blog/get-posts.php",
4818     params: "allposts=true",
4819     text: "Loading Blog Entries..."
4820 });
4821 </code></pre>
4822
4823 * Note: old code is supported with arguments : (container, template, config)
4824
4825
4826  * @constructor
4827  * Create a new JsonView
4828  * 
4829  * @param {Object} config The config object
4830  * 
4831  */
4832 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4833     
4834     
4835     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4836
4837     var um = this.el.getUpdateManager();
4838     um.setRenderer(this);
4839     um.on("update", this.onLoad, this);
4840     um.on("failure", this.onLoadException, this);
4841
4842     /**
4843      * @event beforerender
4844      * Fires before rendering of the downloaded JSON data.
4845      * @param {Roo.JsonView} this
4846      * @param {Object} data The JSON data loaded
4847      */
4848     /**
4849      * @event load
4850      * Fires when data is loaded.
4851      * @param {Roo.JsonView} this
4852      * @param {Object} data The JSON data loaded
4853      * @param {Object} response The raw Connect response object
4854      */
4855     /**
4856      * @event loadexception
4857      * Fires when loading fails.
4858      * @param {Roo.JsonView} this
4859      * @param {Object} response The raw Connect response object
4860      */
4861     this.addEvents({
4862         'beforerender' : true,
4863         'load' : true,
4864         'loadexception' : true
4865     });
4866 };
4867 Roo.extend(Roo.JsonView, Roo.View, {
4868     /**
4869      * @type {String} The root property in the loaded JSON object that contains the data
4870      */
4871     jsonRoot : "",
4872
4873     /**
4874      * Refreshes the view.
4875      */
4876     refresh : function(){
4877         this.clearSelections();
4878         this.el.update("");
4879         var html = [];
4880         var o = this.jsonData;
4881         if(o && o.length > 0){
4882             for(var i = 0, len = o.length; i < len; i++){
4883                 var data = this.prepareData(o[i], i, o);
4884                 html[html.length] = this.tpl.apply(data);
4885             }
4886         }else{
4887             html.push(this.emptyText);
4888         }
4889         this.el.update(html.join(""));
4890         this.nodes = this.el.dom.childNodes;
4891         this.updateIndexes(0);
4892     },
4893
4894     /**
4895      * 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.
4896      * @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:
4897      <pre><code>
4898      view.load({
4899          url: "your-url.php",
4900          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4901          callback: yourFunction,
4902          scope: yourObject, //(optional scope)
4903          discardUrl: false,
4904          nocache: false,
4905          text: "Loading...",
4906          timeout: 30,
4907          scripts: false
4908      });
4909      </code></pre>
4910      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4911      * 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.
4912      * @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}
4913      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4914      * @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.
4915      */
4916     load : function(){
4917         var um = this.el.getUpdateManager();
4918         um.update.apply(um, arguments);
4919     },
4920
4921     // note - render is a standard framework call...
4922     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4923     render : function(el, response){
4924         
4925         this.clearSelections();
4926         this.el.update("");
4927         var o;
4928         try{
4929             if (response != '') {
4930                 o = Roo.util.JSON.decode(response.responseText);
4931                 if(this.jsonRoot){
4932                     
4933                     o = o[this.jsonRoot];
4934                 }
4935             }
4936         } catch(e){
4937         }
4938         /**
4939          * The current JSON data or null
4940          */
4941         this.jsonData = o;
4942         this.beforeRender();
4943         this.refresh();
4944     },
4945
4946 /**
4947  * Get the number of records in the current JSON dataset
4948  * @return {Number}
4949  */
4950     getCount : function(){
4951         return this.jsonData ? this.jsonData.length : 0;
4952     },
4953
4954 /**
4955  * Returns the JSON object for the specified node(s)
4956  * @param {HTMLElement/Array} node The node or an array of nodes
4957  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4958  * you get the JSON object for the node
4959  */
4960     getNodeData : function(node){
4961         if(node instanceof Array){
4962             var data = [];
4963             for(var i = 0, len = node.length; i < len; i++){
4964                 data.push(this.getNodeData(node[i]));
4965             }
4966             return data;
4967         }
4968         return this.jsonData[this.indexOf(node)] || null;
4969     },
4970
4971     beforeRender : function(){
4972         this.snapshot = this.jsonData;
4973         if(this.sortInfo){
4974             this.sort.apply(this, this.sortInfo);
4975         }
4976         this.fireEvent("beforerender", this, this.jsonData);
4977     },
4978
4979     onLoad : function(el, o){
4980         this.fireEvent("load", this, this.jsonData, o);
4981     },
4982
4983     onLoadException : function(el, o){
4984         this.fireEvent("loadexception", this, o);
4985     },
4986
4987 /**
4988  * Filter the data by a specific property.
4989  * @param {String} property A property on your JSON objects
4990  * @param {String/RegExp} value Either string that the property values
4991  * should start with, or a RegExp to test against the property
4992  */
4993     filter : function(property, value){
4994         if(this.jsonData){
4995             var data = [];
4996             var ss = this.snapshot;
4997             if(typeof value == "string"){
4998                 var vlen = value.length;
4999                 if(vlen == 0){
5000                     this.clearFilter();
5001                     return;
5002                 }
5003                 value = value.toLowerCase();
5004                 for(var i = 0, len = ss.length; i < len; i++){
5005                     var o = ss[i];
5006                     if(o[property].substr(0, vlen).toLowerCase() == value){
5007                         data.push(o);
5008                     }
5009                 }
5010             } else if(value.exec){ // regex?
5011                 for(var i = 0, len = ss.length; i < len; i++){
5012                     var o = ss[i];
5013                     if(value.test(o[property])){
5014                         data.push(o);
5015                     }
5016                 }
5017             } else{
5018                 return;
5019             }
5020             this.jsonData = data;
5021             this.refresh();
5022         }
5023     },
5024
5025 /**
5026  * Filter by a function. The passed function will be called with each
5027  * object in the current dataset. If the function returns true the value is kept,
5028  * otherwise it is filtered.
5029  * @param {Function} fn
5030  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5031  */
5032     filterBy : function(fn, scope){
5033         if(this.jsonData){
5034             var data = [];
5035             var ss = this.snapshot;
5036             for(var i = 0, len = ss.length; i < len; i++){
5037                 var o = ss[i];
5038                 if(fn.call(scope || this, o)){
5039                     data.push(o);
5040                 }
5041             }
5042             this.jsonData = data;
5043             this.refresh();
5044         }
5045     },
5046
5047 /**
5048  * Clears the current filter.
5049  */
5050     clearFilter : function(){
5051         if(this.snapshot && this.jsonData != this.snapshot){
5052             this.jsonData = this.snapshot;
5053             this.refresh();
5054         }
5055     },
5056
5057
5058 /**
5059  * Sorts the data for this view and refreshes it.
5060  * @param {String} property A property on your JSON objects to sort on
5061  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5062  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5063  */
5064     sort : function(property, dir, sortType){
5065         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5066         if(this.jsonData){
5067             var p = property;
5068             var dsc = dir && dir.toLowerCase() == "desc";
5069             var f = function(o1, o2){
5070                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5071                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5072                 ;
5073                 if(v1 < v2){
5074                     return dsc ? +1 : -1;
5075                 } else if(v1 > v2){
5076                     return dsc ? -1 : +1;
5077                 } else{
5078                     return 0;
5079                 }
5080             };
5081             this.jsonData.sort(f);
5082             this.refresh();
5083             if(this.jsonData != this.snapshot){
5084                 this.snapshot.sort(f);
5085             }
5086         }
5087     }
5088 });/*
5089  * Based on:
5090  * Ext JS Library 1.1.1
5091  * Copyright(c) 2006-2007, Ext JS, LLC.
5092  *
5093  * Originally Released Under LGPL - original licence link has changed is not relivant.
5094  *
5095  * Fork - LGPL
5096  * <script type="text/javascript">
5097  */
5098  
5099
5100 /**
5101  * @class Roo.ColorPalette
5102  * @extends Roo.Component
5103  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5104  * Here's an example of typical usage:
5105  * <pre><code>
5106 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5107 cp.render('my-div');
5108
5109 cp.on('select', function(palette, selColor){
5110     // do something with selColor
5111 });
5112 </code></pre>
5113  * @constructor
5114  * Create a new ColorPalette
5115  * @param {Object} config The config object
5116  */
5117 Roo.ColorPalette = function(config){
5118     Roo.ColorPalette.superclass.constructor.call(this, config);
5119     this.addEvents({
5120         /**
5121              * @event select
5122              * Fires when a color is selected
5123              * @param {ColorPalette} this
5124              * @param {String} color The 6-digit color hex code (without the # symbol)
5125              */
5126         select: true
5127     });
5128
5129     if(this.handler){
5130         this.on("select", this.handler, this.scope, true);
5131     }
5132 };
5133 Roo.extend(Roo.ColorPalette, Roo.Component, {
5134     /**
5135      * @cfg {String} itemCls
5136      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5137      */
5138     itemCls : "x-color-palette",
5139     /**
5140      * @cfg {String} value
5141      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5142      * the hex codes are case-sensitive.
5143      */
5144     value : null,
5145     clickEvent:'click',
5146     // private
5147     ctype: "Roo.ColorPalette",
5148
5149     /**
5150      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5151      */
5152     allowReselect : false,
5153
5154     /**
5155      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5156      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5157      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5158      * of colors with the width setting until the box is symmetrical.</p>
5159      * <p>You can override individual colors if needed:</p>
5160      * <pre><code>
5161 var cp = new Roo.ColorPalette();
5162 cp.colors[0] = "FF0000";  // change the first box to red
5163 </code></pre>
5164
5165 Or you can provide a custom array of your own for complete control:
5166 <pre><code>
5167 var cp = new Roo.ColorPalette();
5168 cp.colors = ["000000", "993300", "333300"];
5169 </code></pre>
5170      * @type Array
5171      */
5172     colors : [
5173         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5174         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5175         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5176         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5177         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5178     ],
5179
5180     // private
5181     onRender : function(container, position){
5182         var t = new Roo.MasterTemplate(
5183             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5184         );
5185         var c = this.colors;
5186         for(var i = 0, len = c.length; i < len; i++){
5187             t.add([c[i]]);
5188         }
5189         var el = document.createElement("div");
5190         el.className = this.itemCls;
5191         t.overwrite(el);
5192         container.dom.insertBefore(el, position);
5193         this.el = Roo.get(el);
5194         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5195         if(this.clickEvent != 'click'){
5196             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5197         }
5198     },
5199
5200     // private
5201     afterRender : function(){
5202         Roo.ColorPalette.superclass.afterRender.call(this);
5203         if(this.value){
5204             var s = this.value;
5205             this.value = null;
5206             this.select(s);
5207         }
5208     },
5209
5210     // private
5211     handleClick : function(e, t){
5212         e.preventDefault();
5213         if(!this.disabled){
5214             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5215             this.select(c.toUpperCase());
5216         }
5217     },
5218
5219     /**
5220      * Selects the specified color in the palette (fires the select event)
5221      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5222      */
5223     select : function(color){
5224         color = color.replace("#", "");
5225         if(color != this.value || this.allowReselect){
5226             var el = this.el;
5227             if(this.value){
5228                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5229             }
5230             el.child("a.color-"+color).addClass("x-color-palette-sel");
5231             this.value = color;
5232             this.fireEvent("select", this, color);
5233         }
5234     }
5235 });/*
5236  * Based on:
5237  * Ext JS Library 1.1.1
5238  * Copyright(c) 2006-2007, Ext JS, LLC.
5239  *
5240  * Originally Released Under LGPL - original licence link has changed is not relivant.
5241  *
5242  * Fork - LGPL
5243  * <script type="text/javascript">
5244  */
5245  
5246 /**
5247  * @class Roo.DatePicker
5248  * @extends Roo.Component
5249  * Simple date picker class.
5250  * @constructor
5251  * Create a new DatePicker
5252  * @param {Object} config The config object
5253  */
5254 Roo.DatePicker = function(config){
5255     Roo.DatePicker.superclass.constructor.call(this, config);
5256
5257     this.value = config && config.value ?
5258                  config.value.clearTime() : new Date().clearTime();
5259
5260     this.addEvents({
5261         /**
5262              * @event select
5263              * Fires when a date is selected
5264              * @param {DatePicker} this
5265              * @param {Date} date The selected date
5266              */
5267         'select': true,
5268         /**
5269              * @event monthchange
5270              * Fires when the displayed month changes 
5271              * @param {DatePicker} this
5272              * @param {Date} date The selected month
5273              */
5274         'monthchange': true
5275     });
5276
5277     if(this.handler){
5278         this.on("select", this.handler,  this.scope || this);
5279     }
5280     // build the disabledDatesRE
5281     if(!this.disabledDatesRE && this.disabledDates){
5282         var dd = this.disabledDates;
5283         var re = "(?:";
5284         for(var i = 0; i < dd.length; i++){
5285             re += dd[i];
5286             if(i != dd.length-1) {
5287                 re += "|";
5288             }
5289         }
5290         this.disabledDatesRE = new RegExp(re + ")");
5291     }
5292 };
5293
5294 Roo.extend(Roo.DatePicker, Roo.Component, {
5295     /**
5296      * @cfg {String} todayText
5297      * The text to display on the button that selects the current date (defaults to "Today")
5298      */
5299     todayText : "Today",
5300     /**
5301      * @cfg {String} okText
5302      * The text to display on the ok button
5303      */
5304     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5305     /**
5306      * @cfg {String} cancelText
5307      * The text to display on the cancel button
5308      */
5309     cancelText : "Cancel",
5310     /**
5311      * @cfg {String} todayTip
5312      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5313      */
5314     todayTip : "{0} (Spacebar)",
5315     /**
5316      * @cfg {Date} minDate
5317      * Minimum allowable date (JavaScript date object, defaults to null)
5318      */
5319     minDate : null,
5320     /**
5321      * @cfg {Date} maxDate
5322      * Maximum allowable date (JavaScript date object, defaults to null)
5323      */
5324     maxDate : null,
5325     /**
5326      * @cfg {String} minText
5327      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5328      */
5329     minText : "This date is before the minimum date",
5330     /**
5331      * @cfg {String} maxText
5332      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5333      */
5334     maxText : "This date is after the maximum date",
5335     /**
5336      * @cfg {String} format
5337      * The default date format string which can be overriden for localization support.  The format must be
5338      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5339      */
5340     format : "m/d/y",
5341     /**
5342      * @cfg {Array} disabledDays
5343      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5344      */
5345     disabledDays : null,
5346     /**
5347      * @cfg {String} disabledDaysText
5348      * The tooltip to display when the date falls on a disabled day (defaults to "")
5349      */
5350     disabledDaysText : "",
5351     /**
5352      * @cfg {RegExp} disabledDatesRE
5353      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5354      */
5355     disabledDatesRE : null,
5356     /**
5357      * @cfg {String} disabledDatesText
5358      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5359      */
5360     disabledDatesText : "",
5361     /**
5362      * @cfg {Boolean} constrainToViewport
5363      * True to constrain the date picker to the viewport (defaults to true)
5364      */
5365     constrainToViewport : true,
5366     /**
5367      * @cfg {Array} monthNames
5368      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5369      */
5370     monthNames : Date.monthNames,
5371     /**
5372      * @cfg {Array} dayNames
5373      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5374      */
5375     dayNames : Date.dayNames,
5376     /**
5377      * @cfg {String} nextText
5378      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5379      */
5380     nextText: 'Next Month (Control+Right)',
5381     /**
5382      * @cfg {String} prevText
5383      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5384      */
5385     prevText: 'Previous Month (Control+Left)',
5386     /**
5387      * @cfg {String} monthYearText
5388      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5389      */
5390     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5391     /**
5392      * @cfg {Number} startDay
5393      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5394      */
5395     startDay : 0,
5396     /**
5397      * @cfg {Bool} showClear
5398      * Show a clear button (usefull for date form elements that can be blank.)
5399      */
5400     
5401     showClear: false,
5402     
5403     /**
5404      * Sets the value of the date field
5405      * @param {Date} value The date to set
5406      */
5407     setValue : function(value){
5408         var old = this.value;
5409         
5410         if (typeof(value) == 'string') {
5411          
5412             value = Date.parseDate(value, this.format);
5413         }
5414         if (!value) {
5415             value = new Date();
5416         }
5417         
5418         this.value = value.clearTime(true);
5419         if(this.el){
5420             this.update(this.value);
5421         }
5422     },
5423
5424     /**
5425      * Gets the current selected value of the date field
5426      * @return {Date} The selected date
5427      */
5428     getValue : function(){
5429         return this.value;
5430     },
5431
5432     // private
5433     focus : function(){
5434         if(this.el){
5435             this.update(this.activeDate);
5436         }
5437     },
5438
5439     // privateval
5440     onRender : function(container, position){
5441         
5442         var m = [
5443              '<table cellspacing="0">',
5444                 '<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>',
5445                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5446         var dn = this.dayNames;
5447         for(var i = 0; i < 7; i++){
5448             var d = this.startDay+i;
5449             if(d > 6){
5450                 d = d-7;
5451             }
5452             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5453         }
5454         m[m.length] = "</tr></thead><tbody><tr>";
5455         for(var i = 0; i < 42; i++) {
5456             if(i % 7 == 0 && i != 0){
5457                 m[m.length] = "</tr><tr>";
5458             }
5459             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5460         }
5461         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5462             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5463
5464         var el = document.createElement("div");
5465         el.className = "x-date-picker";
5466         el.innerHTML = m.join("");
5467
5468         container.dom.insertBefore(el, position);
5469
5470         this.el = Roo.get(el);
5471         this.eventEl = Roo.get(el.firstChild);
5472
5473         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5474             handler: this.showPrevMonth,
5475             scope: this,
5476             preventDefault:true,
5477             stopDefault:true
5478         });
5479
5480         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5481             handler: this.showNextMonth,
5482             scope: this,
5483             preventDefault:true,
5484             stopDefault:true
5485         });
5486
5487         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5488
5489         this.monthPicker = this.el.down('div.x-date-mp');
5490         this.monthPicker.enableDisplayMode('block');
5491         
5492         var kn = new Roo.KeyNav(this.eventEl, {
5493             "left" : function(e){
5494                 e.ctrlKey ?
5495                     this.showPrevMonth() :
5496                     this.update(this.activeDate.add("d", -1));
5497             },
5498
5499             "right" : function(e){
5500                 e.ctrlKey ?
5501                     this.showNextMonth() :
5502                     this.update(this.activeDate.add("d", 1));
5503             },
5504
5505             "up" : function(e){
5506                 e.ctrlKey ?
5507                     this.showNextYear() :
5508                     this.update(this.activeDate.add("d", -7));
5509             },
5510
5511             "down" : function(e){
5512                 e.ctrlKey ?
5513                     this.showPrevYear() :
5514                     this.update(this.activeDate.add("d", 7));
5515             },
5516
5517             "pageUp" : function(e){
5518                 this.showNextMonth();
5519             },
5520
5521             "pageDown" : function(e){
5522                 this.showPrevMonth();
5523             },
5524
5525             "enter" : function(e){
5526                 e.stopPropagation();
5527                 return true;
5528             },
5529
5530             scope : this
5531         });
5532
5533         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5534
5535         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5536
5537         this.el.unselectable();
5538         
5539         this.cells = this.el.select("table.x-date-inner tbody td");
5540         this.textNodes = this.el.query("table.x-date-inner tbody span");
5541
5542         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5543             text: "&#160;",
5544             tooltip: this.monthYearText
5545         });
5546
5547         this.mbtn.on('click', this.showMonthPicker, this);
5548         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5549
5550
5551         var today = (new Date()).dateFormat(this.format);
5552         
5553         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5554         if (this.showClear) {
5555             baseTb.add( new Roo.Toolbar.Fill());
5556         }
5557         baseTb.add({
5558             text: String.format(this.todayText, today),
5559             tooltip: String.format(this.todayTip, today),
5560             handler: this.selectToday,
5561             scope: this
5562         });
5563         
5564         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5565             
5566         //});
5567         if (this.showClear) {
5568             
5569             baseTb.add( new Roo.Toolbar.Fill());
5570             baseTb.add({
5571                 text: '&#160;',
5572                 cls: 'x-btn-icon x-btn-clear',
5573                 handler: function() {
5574                     //this.value = '';
5575                     this.fireEvent("select", this, '');
5576                 },
5577                 scope: this
5578             });
5579         }
5580         
5581         
5582         if(Roo.isIE){
5583             this.el.repaint();
5584         }
5585         this.update(this.value);
5586     },
5587
5588     createMonthPicker : function(){
5589         if(!this.monthPicker.dom.firstChild){
5590             var buf = ['<table border="0" cellspacing="0">'];
5591             for(var i = 0; i < 6; i++){
5592                 buf.push(
5593                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5594                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5595                     i == 0 ?
5596                     '<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>' :
5597                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5598                 );
5599             }
5600             buf.push(
5601                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5602                     this.okText,
5603                     '</button><button type="button" class="x-date-mp-cancel">',
5604                     this.cancelText,
5605                     '</button></td></tr>',
5606                 '</table>'
5607             );
5608             this.monthPicker.update(buf.join(''));
5609             this.monthPicker.on('click', this.onMonthClick, this);
5610             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5611
5612             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5613             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5614
5615             this.mpMonths.each(function(m, a, i){
5616                 i += 1;
5617                 if((i%2) == 0){
5618                     m.dom.xmonth = 5 + Math.round(i * .5);
5619                 }else{
5620                     m.dom.xmonth = Math.round((i-1) * .5);
5621                 }
5622             });
5623         }
5624     },
5625
5626     showMonthPicker : function(){
5627         this.createMonthPicker();
5628         var size = this.el.getSize();
5629         this.monthPicker.setSize(size);
5630         this.monthPicker.child('table').setSize(size);
5631
5632         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5633         this.updateMPMonth(this.mpSelMonth);
5634         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5635         this.updateMPYear(this.mpSelYear);
5636
5637         this.monthPicker.slideIn('t', {duration:.2});
5638     },
5639
5640     updateMPYear : function(y){
5641         this.mpyear = y;
5642         var ys = this.mpYears.elements;
5643         for(var i = 1; i <= 10; i++){
5644             var td = ys[i-1], y2;
5645             if((i%2) == 0){
5646                 y2 = y + Math.round(i * .5);
5647                 td.firstChild.innerHTML = y2;
5648                 td.xyear = y2;
5649             }else{
5650                 y2 = y - (5-Math.round(i * .5));
5651                 td.firstChild.innerHTML = y2;
5652                 td.xyear = y2;
5653             }
5654             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5655         }
5656     },
5657
5658     updateMPMonth : function(sm){
5659         this.mpMonths.each(function(m, a, i){
5660             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5661         });
5662     },
5663
5664     selectMPMonth: function(m){
5665         
5666     },
5667
5668     onMonthClick : function(e, t){
5669         e.stopEvent();
5670         var el = new Roo.Element(t), pn;
5671         if(el.is('button.x-date-mp-cancel')){
5672             this.hideMonthPicker();
5673         }
5674         else if(el.is('button.x-date-mp-ok')){
5675             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5676             this.hideMonthPicker();
5677         }
5678         else if(pn = el.up('td.x-date-mp-month', 2)){
5679             this.mpMonths.removeClass('x-date-mp-sel');
5680             pn.addClass('x-date-mp-sel');
5681             this.mpSelMonth = pn.dom.xmonth;
5682         }
5683         else if(pn = el.up('td.x-date-mp-year', 2)){
5684             this.mpYears.removeClass('x-date-mp-sel');
5685             pn.addClass('x-date-mp-sel');
5686             this.mpSelYear = pn.dom.xyear;
5687         }
5688         else if(el.is('a.x-date-mp-prev')){
5689             this.updateMPYear(this.mpyear-10);
5690         }
5691         else if(el.is('a.x-date-mp-next')){
5692             this.updateMPYear(this.mpyear+10);
5693         }
5694     },
5695
5696     onMonthDblClick : function(e, t){
5697         e.stopEvent();
5698         var el = new Roo.Element(t), pn;
5699         if(pn = el.up('td.x-date-mp-month', 2)){
5700             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5701             this.hideMonthPicker();
5702         }
5703         else if(pn = el.up('td.x-date-mp-year', 2)){
5704             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5705             this.hideMonthPicker();
5706         }
5707     },
5708
5709     hideMonthPicker : function(disableAnim){
5710         if(this.monthPicker){
5711             if(disableAnim === true){
5712                 this.monthPicker.hide();
5713             }else{
5714                 this.monthPicker.slideOut('t', {duration:.2});
5715             }
5716         }
5717     },
5718
5719     // private
5720     showPrevMonth : function(e){
5721         this.update(this.activeDate.add("mo", -1));
5722     },
5723
5724     // private
5725     showNextMonth : function(e){
5726         this.update(this.activeDate.add("mo", 1));
5727     },
5728
5729     // private
5730     showPrevYear : function(){
5731         this.update(this.activeDate.add("y", -1));
5732     },
5733
5734     // private
5735     showNextYear : function(){
5736         this.update(this.activeDate.add("y", 1));
5737     },
5738
5739     // private
5740     handleMouseWheel : function(e){
5741         var delta = e.getWheelDelta();
5742         if(delta > 0){
5743             this.showPrevMonth();
5744             e.stopEvent();
5745         } else if(delta < 0){
5746             this.showNextMonth();
5747             e.stopEvent();
5748         }
5749     },
5750
5751     // private
5752     handleDateClick : function(e, t){
5753         e.stopEvent();
5754         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5755             this.setValue(new Date(t.dateValue));
5756             this.fireEvent("select", this, this.value);
5757         }
5758     },
5759
5760     // private
5761     selectToday : function(){
5762         this.setValue(new Date().clearTime());
5763         this.fireEvent("select", this, this.value);
5764     },
5765
5766     // private
5767     update : function(date)
5768     {
5769         var vd = this.activeDate;
5770         this.activeDate = date;
5771         if(vd && this.el){
5772             var t = date.getTime();
5773             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5774                 this.cells.removeClass("x-date-selected");
5775                 this.cells.each(function(c){
5776                    if(c.dom.firstChild.dateValue == t){
5777                        c.addClass("x-date-selected");
5778                        setTimeout(function(){
5779                             try{c.dom.firstChild.focus();}catch(e){}
5780                        }, 50);
5781                        return false;
5782                    }
5783                 });
5784                 return;
5785             }
5786         }
5787         
5788         var days = date.getDaysInMonth();
5789         var firstOfMonth = date.getFirstDateOfMonth();
5790         var startingPos = firstOfMonth.getDay()-this.startDay;
5791
5792         if(startingPos <= this.startDay){
5793             startingPos += 7;
5794         }
5795
5796         var pm = date.add("mo", -1);
5797         var prevStart = pm.getDaysInMonth()-startingPos;
5798
5799         var cells = this.cells.elements;
5800         var textEls = this.textNodes;
5801         days += startingPos;
5802
5803         // convert everything to numbers so it's fast
5804         var day = 86400000;
5805         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5806         var today = new Date().clearTime().getTime();
5807         var sel = date.clearTime().getTime();
5808         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5809         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5810         var ddMatch = this.disabledDatesRE;
5811         var ddText = this.disabledDatesText;
5812         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5813         var ddaysText = this.disabledDaysText;
5814         var format = this.format;
5815
5816         var setCellClass = function(cal, cell){
5817             cell.title = "";
5818             var t = d.getTime();
5819             cell.firstChild.dateValue = t;
5820             if(t == today){
5821                 cell.className += " x-date-today";
5822                 cell.title = cal.todayText;
5823             }
5824             if(t == sel){
5825                 cell.className += " x-date-selected";
5826                 setTimeout(function(){
5827                     try{cell.firstChild.focus();}catch(e){}
5828                 }, 50);
5829             }
5830             // disabling
5831             if(t < min) {
5832                 cell.className = " x-date-disabled";
5833                 cell.title = cal.minText;
5834                 return;
5835             }
5836             if(t > max) {
5837                 cell.className = " x-date-disabled";
5838                 cell.title = cal.maxText;
5839                 return;
5840             }
5841             if(ddays){
5842                 if(ddays.indexOf(d.getDay()) != -1){
5843                     cell.title = ddaysText;
5844                     cell.className = " x-date-disabled";
5845                 }
5846             }
5847             if(ddMatch && format){
5848                 var fvalue = d.dateFormat(format);
5849                 if(ddMatch.test(fvalue)){
5850                     cell.title = ddText.replace("%0", fvalue);
5851                     cell.className = " x-date-disabled";
5852                 }
5853             }
5854         };
5855
5856         var i = 0;
5857         for(; i < startingPos; i++) {
5858             textEls[i].innerHTML = (++prevStart);
5859             d.setDate(d.getDate()+1);
5860             cells[i].className = "x-date-prevday";
5861             setCellClass(this, cells[i]);
5862         }
5863         for(; i < days; i++){
5864             intDay = i - startingPos + 1;
5865             textEls[i].innerHTML = (intDay);
5866             d.setDate(d.getDate()+1);
5867             cells[i].className = "x-date-active";
5868             setCellClass(this, cells[i]);
5869         }
5870         var extraDays = 0;
5871         for(; i < 42; i++) {
5872              textEls[i].innerHTML = (++extraDays);
5873              d.setDate(d.getDate()+1);
5874              cells[i].className = "x-date-nextday";
5875              setCellClass(this, cells[i]);
5876         }
5877
5878         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5879         this.fireEvent('monthchange', this, date);
5880         
5881         if(!this.internalRender){
5882             var main = this.el.dom.firstChild;
5883             var w = main.offsetWidth;
5884             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5885             Roo.fly(main).setWidth(w);
5886             this.internalRender = true;
5887             // opera does not respect the auto grow header center column
5888             // then, after it gets a width opera refuses to recalculate
5889             // without a second pass
5890             if(Roo.isOpera && !this.secondPass){
5891                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5892                 this.secondPass = true;
5893                 this.update.defer(10, this, [date]);
5894             }
5895         }
5896         
5897         
5898     }
5899 });        /*
5900  * Based on:
5901  * Ext JS Library 1.1.1
5902  * Copyright(c) 2006-2007, Ext JS, LLC.
5903  *
5904  * Originally Released Under LGPL - original licence link has changed is not relivant.
5905  *
5906  * Fork - LGPL
5907  * <script type="text/javascript">
5908  */
5909 /**
5910  * @class Roo.TabPanel
5911  * @extends Roo.util.Observable
5912  * A lightweight tab container.
5913  * <br><br>
5914  * Usage:
5915  * <pre><code>
5916 // basic tabs 1, built from existing content
5917 var tabs = new Roo.TabPanel("tabs1");
5918 tabs.addTab("script", "View Script");
5919 tabs.addTab("markup", "View Markup");
5920 tabs.activate("script");
5921
5922 // more advanced tabs, built from javascript
5923 var jtabs = new Roo.TabPanel("jtabs");
5924 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5925
5926 // set up the UpdateManager
5927 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5928 var updater = tab2.getUpdateManager();
5929 updater.setDefaultUrl("ajax1.htm");
5930 tab2.on('activate', updater.refresh, updater, true);
5931
5932 // Use setUrl for Ajax loading
5933 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5934 tab3.setUrl("ajax2.htm", null, true);
5935
5936 // Disabled tab
5937 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5938 tab4.disable();
5939
5940 jtabs.activate("jtabs-1");
5941  * </code></pre>
5942  * @constructor
5943  * Create a new TabPanel.
5944  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5945  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5946  */
5947 Roo.TabPanel = function(container, config){
5948     /**
5949     * The container element for this TabPanel.
5950     * @type Roo.Element
5951     */
5952     this.el = Roo.get(container, true);
5953     if(config){
5954         if(typeof config == "boolean"){
5955             this.tabPosition = config ? "bottom" : "top";
5956         }else{
5957             Roo.apply(this, config);
5958         }
5959     }
5960     if(this.tabPosition == "bottom"){
5961         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5962         this.el.addClass("x-tabs-bottom");
5963     }
5964     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5965     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5966     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5967     if(Roo.isIE){
5968         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5969     }
5970     if(this.tabPosition != "bottom"){
5971         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5972          * @type Roo.Element
5973          */
5974         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5975         this.el.addClass("x-tabs-top");
5976     }
5977     this.items = [];
5978
5979     this.bodyEl.setStyle("position", "relative");
5980
5981     this.active = null;
5982     this.activateDelegate = this.activate.createDelegate(this);
5983
5984     this.addEvents({
5985         /**
5986          * @event tabchange
5987          * Fires when the active tab changes
5988          * @param {Roo.TabPanel} this
5989          * @param {Roo.TabPanelItem} activePanel The new active tab
5990          */
5991         "tabchange": true,
5992         /**
5993          * @event beforetabchange
5994          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5995          * @param {Roo.TabPanel} this
5996          * @param {Object} e Set cancel to true on this object to cancel the tab change
5997          * @param {Roo.TabPanelItem} tab The tab being changed to
5998          */
5999         "beforetabchange" : true
6000     });
6001
6002     Roo.EventManager.onWindowResize(this.onResize, this);
6003     this.cpad = this.el.getPadding("lr");
6004     this.hiddenCount = 0;
6005
6006
6007     // toolbar on the tabbar support...
6008     if (this.toolbar) {
6009         var tcfg = this.toolbar;
6010         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
6011         this.toolbar = new Roo.Toolbar(tcfg);
6012         if (Roo.isSafari) {
6013             var tbl = tcfg.container.child('table', true);
6014             tbl.setAttribute('width', '100%');
6015         }
6016         
6017     }
6018    
6019
6020
6021     Roo.TabPanel.superclass.constructor.call(this);
6022 };
6023
6024 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6025     /*
6026      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6027      */
6028     tabPosition : "top",
6029     /*
6030      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6031      */
6032     currentTabWidth : 0,
6033     /*
6034      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6035      */
6036     minTabWidth : 40,
6037     /*
6038      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6039      */
6040     maxTabWidth : 250,
6041     /*
6042      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6043      */
6044     preferredTabWidth : 175,
6045     /*
6046      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6047      */
6048     resizeTabs : false,
6049     /*
6050      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6051      */
6052     monitorResize : true,
6053     /*
6054      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6055      */
6056     toolbar : false,
6057
6058     /**
6059      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6060      * @param {String} id The id of the div to use <b>or create</b>
6061      * @param {String} text The text for the tab
6062      * @param {String} content (optional) Content to put in the TabPanelItem body
6063      * @param {Boolean} closable (optional) True to create a close icon on the tab
6064      * @return {Roo.TabPanelItem} The created TabPanelItem
6065      */
6066     addTab : function(id, text, content, closable){
6067         var item = new Roo.TabPanelItem(this, id, text, closable);
6068         this.addTabItem(item);
6069         if(content){
6070             item.setContent(content);
6071         }
6072         return item;
6073     },
6074
6075     /**
6076      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6077      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6078      * @return {Roo.TabPanelItem}
6079      */
6080     getTab : function(id){
6081         return this.items[id];
6082     },
6083
6084     /**
6085      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6086      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6087      */
6088     hideTab : function(id){
6089         var t = this.items[id];
6090         if(!t.isHidden()){
6091            t.setHidden(true);
6092            this.hiddenCount++;
6093            this.autoSizeTabs();
6094         }
6095     },
6096
6097     /**
6098      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6099      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6100      */
6101     unhideTab : function(id){
6102         var t = this.items[id];
6103         if(t.isHidden()){
6104            t.setHidden(false);
6105            this.hiddenCount--;
6106            this.autoSizeTabs();
6107         }
6108     },
6109
6110     /**
6111      * Adds an existing {@link Roo.TabPanelItem}.
6112      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6113      */
6114     addTabItem : function(item){
6115         this.items[item.id] = item;
6116         this.items.push(item);
6117         if(this.resizeTabs){
6118            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6119            this.autoSizeTabs();
6120         }else{
6121             item.autoSize();
6122         }
6123     },
6124
6125     /**
6126      * Removes a {@link Roo.TabPanelItem}.
6127      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6128      */
6129     removeTab : function(id){
6130         var items = this.items;
6131         var tab = items[id];
6132         if(!tab) { return; }
6133         var index = items.indexOf(tab);
6134         if(this.active == tab && items.length > 1){
6135             var newTab = this.getNextAvailable(index);
6136             if(newTab) {
6137                 newTab.activate();
6138             }
6139         }
6140         this.stripEl.dom.removeChild(tab.pnode.dom);
6141         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6142             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6143         }
6144         items.splice(index, 1);
6145         delete this.items[tab.id];
6146         tab.fireEvent("close", tab);
6147         tab.purgeListeners();
6148         this.autoSizeTabs();
6149     },
6150
6151     getNextAvailable : function(start){
6152         var items = this.items;
6153         var index = start;
6154         // look for a next tab that will slide over to
6155         // replace the one being removed
6156         while(index < items.length){
6157             var item = items[++index];
6158             if(item && !item.isHidden()){
6159                 return item;
6160             }
6161         }
6162         // if one isn't found select the previous tab (on the left)
6163         index = start;
6164         while(index >= 0){
6165             var item = items[--index];
6166             if(item && !item.isHidden()){
6167                 return item;
6168             }
6169         }
6170         return null;
6171     },
6172
6173     /**
6174      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6175      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6176      */
6177     disableTab : function(id){
6178         var tab = this.items[id];
6179         if(tab && this.active != tab){
6180             tab.disable();
6181         }
6182     },
6183
6184     /**
6185      * Enables a {@link Roo.TabPanelItem} that is disabled.
6186      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6187      */
6188     enableTab : function(id){
6189         var tab = this.items[id];
6190         tab.enable();
6191     },
6192
6193     /**
6194      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6195      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6196      * @return {Roo.TabPanelItem} The TabPanelItem.
6197      */
6198     activate : function(id){
6199         var tab = this.items[id];
6200         if(!tab){
6201             return null;
6202         }
6203         if(tab == this.active || tab.disabled){
6204             return tab;
6205         }
6206         var e = {};
6207         this.fireEvent("beforetabchange", this, e, tab);
6208         if(e.cancel !== true && !tab.disabled){
6209             if(this.active){
6210                 this.active.hide();
6211             }
6212             this.active = this.items[id];
6213             this.active.show();
6214             this.fireEvent("tabchange", this, this.active);
6215         }
6216         return tab;
6217     },
6218
6219     /**
6220      * Gets the active {@link Roo.TabPanelItem}.
6221      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6222      */
6223     getActiveTab : function(){
6224         return this.active;
6225     },
6226
6227     /**
6228      * Updates the tab body element to fit the height of the container element
6229      * for overflow scrolling
6230      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6231      */
6232     syncHeight : function(targetHeight){
6233         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6234         var bm = this.bodyEl.getMargins();
6235         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6236         this.bodyEl.setHeight(newHeight);
6237         return newHeight;
6238     },
6239
6240     onResize : function(){
6241         if(this.monitorResize){
6242             this.autoSizeTabs();
6243         }
6244     },
6245
6246     /**
6247      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6248      */
6249     beginUpdate : function(){
6250         this.updating = true;
6251     },
6252
6253     /**
6254      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6255      */
6256     endUpdate : function(){
6257         this.updating = false;
6258         this.autoSizeTabs();
6259     },
6260
6261     /**
6262      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6263      */
6264     autoSizeTabs : function(){
6265         var count = this.items.length;
6266         var vcount = count - this.hiddenCount;
6267         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6268             return;
6269         }
6270         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6271         var availWidth = Math.floor(w / vcount);
6272         var b = this.stripBody;
6273         if(b.getWidth() > w){
6274             var tabs = this.items;
6275             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6276             if(availWidth < this.minTabWidth){
6277                 /*if(!this.sleft){    // incomplete scrolling code
6278                     this.createScrollButtons();
6279                 }
6280                 this.showScroll();
6281                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6282             }
6283         }else{
6284             if(this.currentTabWidth < this.preferredTabWidth){
6285                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6286             }
6287         }
6288     },
6289
6290     /**
6291      * Returns the number of tabs in this TabPanel.
6292      * @return {Number}
6293      */
6294      getCount : function(){
6295          return this.items.length;
6296      },
6297
6298     /**
6299      * Resizes all the tabs to the passed width
6300      * @param {Number} The new width
6301      */
6302     setTabWidth : function(width){
6303         this.currentTabWidth = width;
6304         for(var i = 0, len = this.items.length; i < len; i++) {
6305                 if(!this.items[i].isHidden()) {
6306                 this.items[i].setWidth(width);
6307             }
6308         }
6309     },
6310
6311     /**
6312      * Destroys this TabPanel
6313      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6314      */
6315     destroy : function(removeEl){
6316         Roo.EventManager.removeResizeListener(this.onResize, this);
6317         for(var i = 0, len = this.items.length; i < len; i++){
6318             this.items[i].purgeListeners();
6319         }
6320         if(removeEl === true){
6321             this.el.update("");
6322             this.el.remove();
6323         }
6324     }
6325 });
6326
6327 /**
6328  * @class Roo.TabPanelItem
6329  * @extends Roo.util.Observable
6330  * Represents an individual item (tab plus body) in a TabPanel.
6331  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6332  * @param {String} id The id of this TabPanelItem
6333  * @param {String} text The text for the tab of this TabPanelItem
6334  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6335  */
6336 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6337     /**
6338      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6339      * @type Roo.TabPanel
6340      */
6341     this.tabPanel = tabPanel;
6342     /**
6343      * The id for this TabPanelItem
6344      * @type String
6345      */
6346     this.id = id;
6347     /** @private */
6348     this.disabled = false;
6349     /** @private */
6350     this.text = text;
6351     /** @private */
6352     this.loaded = false;
6353     this.closable = closable;
6354
6355     /**
6356      * The body element for this TabPanelItem.
6357      * @type Roo.Element
6358      */
6359     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6360     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6361     this.bodyEl.setStyle("display", "block");
6362     this.bodyEl.setStyle("zoom", "1");
6363     this.hideAction();
6364
6365     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6366     /** @private */
6367     this.el = Roo.get(els.el, true);
6368     this.inner = Roo.get(els.inner, true);
6369     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6370     this.pnode = Roo.get(els.el.parentNode, true);
6371     this.el.on("mousedown", this.onTabMouseDown, this);
6372     this.el.on("click", this.onTabClick, this);
6373     /** @private */
6374     if(closable){
6375         var c = Roo.get(els.close, true);
6376         c.dom.title = this.closeText;
6377         c.addClassOnOver("close-over");
6378         c.on("click", this.closeClick, this);
6379      }
6380
6381     this.addEvents({
6382          /**
6383          * @event activate
6384          * Fires when this tab becomes the active tab.
6385          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6386          * @param {Roo.TabPanelItem} this
6387          */
6388         "activate": true,
6389         /**
6390          * @event beforeclose
6391          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6392          * @param {Roo.TabPanelItem} this
6393          * @param {Object} e Set cancel to true on this object to cancel the close.
6394          */
6395         "beforeclose": true,
6396         /**
6397          * @event close
6398          * Fires when this tab is closed.
6399          * @param {Roo.TabPanelItem} this
6400          */
6401          "close": true,
6402         /**
6403          * @event deactivate
6404          * Fires when this tab is no longer the active tab.
6405          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6406          * @param {Roo.TabPanelItem} this
6407          */
6408          "deactivate" : true
6409     });
6410     this.hidden = false;
6411
6412     Roo.TabPanelItem.superclass.constructor.call(this);
6413 };
6414
6415 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6416     purgeListeners : function(){
6417        Roo.util.Observable.prototype.purgeListeners.call(this);
6418        this.el.removeAllListeners();
6419     },
6420     /**
6421      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6422      */
6423     show : function(){
6424         this.pnode.addClass("on");
6425         this.showAction();
6426         if(Roo.isOpera){
6427             this.tabPanel.stripWrap.repaint();
6428         }
6429         this.fireEvent("activate", this.tabPanel, this);
6430     },
6431
6432     /**
6433      * Returns true if this tab is the active tab.
6434      * @return {Boolean}
6435      */
6436     isActive : function(){
6437         return this.tabPanel.getActiveTab() == this;
6438     },
6439
6440     /**
6441      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6442      */
6443     hide : function(){
6444         this.pnode.removeClass("on");
6445         this.hideAction();
6446         this.fireEvent("deactivate", this.tabPanel, this);
6447     },
6448
6449     hideAction : function(){
6450         this.bodyEl.hide();
6451         this.bodyEl.setStyle("position", "absolute");
6452         this.bodyEl.setLeft("-20000px");
6453         this.bodyEl.setTop("-20000px");
6454     },
6455
6456     showAction : function(){
6457         this.bodyEl.setStyle("position", "relative");
6458         this.bodyEl.setTop("");
6459         this.bodyEl.setLeft("");
6460         this.bodyEl.show();
6461     },
6462
6463     /**
6464      * Set the tooltip for the tab.
6465      * @param {String} tooltip The tab's tooltip
6466      */
6467     setTooltip : function(text){
6468         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6469             this.textEl.dom.qtip = text;
6470             this.textEl.dom.removeAttribute('title');
6471         }else{
6472             this.textEl.dom.title = text;
6473         }
6474     },
6475
6476     onTabClick : function(e){
6477         e.preventDefault();
6478         this.tabPanel.activate(this.id);
6479     },
6480
6481     onTabMouseDown : function(e){
6482         e.preventDefault();
6483         this.tabPanel.activate(this.id);
6484     },
6485
6486     getWidth : function(){
6487         return this.inner.getWidth();
6488     },
6489
6490     setWidth : function(width){
6491         var iwidth = width - this.pnode.getPadding("lr");
6492         this.inner.setWidth(iwidth);
6493         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6494         this.pnode.setWidth(width);
6495     },
6496
6497     /**
6498      * Show or hide the tab
6499      * @param {Boolean} hidden True to hide or false to show.
6500      */
6501     setHidden : function(hidden){
6502         this.hidden = hidden;
6503         this.pnode.setStyle("display", hidden ? "none" : "");
6504     },
6505
6506     /**
6507      * Returns true if this tab is "hidden"
6508      * @return {Boolean}
6509      */
6510     isHidden : function(){
6511         return this.hidden;
6512     },
6513
6514     /**
6515      * Returns the text for this tab
6516      * @return {String}
6517      */
6518     getText : function(){
6519         return this.text;
6520     },
6521
6522     autoSize : function(){
6523         //this.el.beginMeasure();
6524         this.textEl.setWidth(1);
6525         /*
6526          *  #2804 [new] Tabs in Roojs
6527          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6528          */
6529         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6530         //this.el.endMeasure();
6531     },
6532
6533     /**
6534      * Sets the text for the tab (Note: this also sets the tooltip text)
6535      * @param {String} text The tab's text and tooltip
6536      */
6537     setText : function(text){
6538         this.text = text;
6539         this.textEl.update(text);
6540         this.setTooltip(text);
6541         if(!this.tabPanel.resizeTabs){
6542             this.autoSize();
6543         }
6544     },
6545     /**
6546      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6547      */
6548     activate : function(){
6549         this.tabPanel.activate(this.id);
6550     },
6551
6552     /**
6553      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6554      */
6555     disable : function(){
6556         if(this.tabPanel.active != this){
6557             this.disabled = true;
6558             this.pnode.addClass("disabled");
6559         }
6560     },
6561
6562     /**
6563      * Enables this TabPanelItem if it was previously disabled.
6564      */
6565     enable : function(){
6566         this.disabled = false;
6567         this.pnode.removeClass("disabled");
6568     },
6569
6570     /**
6571      * Sets the content for this TabPanelItem.
6572      * @param {String} content The content
6573      * @param {Boolean} loadScripts true to look for and load scripts
6574      */
6575     setContent : function(content, loadScripts){
6576         this.bodyEl.update(content, loadScripts);
6577     },
6578
6579     /**
6580      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6581      * @return {Roo.UpdateManager} The UpdateManager
6582      */
6583     getUpdateManager : function(){
6584         return this.bodyEl.getUpdateManager();
6585     },
6586
6587     /**
6588      * Set a URL to be used to load the content for this TabPanelItem.
6589      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6590      * @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)
6591      * @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)
6592      * @return {Roo.UpdateManager} The UpdateManager
6593      */
6594     setUrl : function(url, params, loadOnce){
6595         if(this.refreshDelegate){
6596             this.un('activate', this.refreshDelegate);
6597         }
6598         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6599         this.on("activate", this.refreshDelegate);
6600         return this.bodyEl.getUpdateManager();
6601     },
6602
6603     /** @private */
6604     _handleRefresh : function(url, params, loadOnce){
6605         if(!loadOnce || !this.loaded){
6606             var updater = this.bodyEl.getUpdateManager();
6607             updater.update(url, params, this._setLoaded.createDelegate(this));
6608         }
6609     },
6610
6611     /**
6612      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6613      *   Will fail silently if the setUrl method has not been called.
6614      *   This does not activate the panel, just updates its content.
6615      */
6616     refresh : function(){
6617         if(this.refreshDelegate){
6618            this.loaded = false;
6619            this.refreshDelegate();
6620         }
6621     },
6622
6623     /** @private */
6624     _setLoaded : function(){
6625         this.loaded = true;
6626     },
6627
6628     /** @private */
6629     closeClick : function(e){
6630         var o = {};
6631         e.stopEvent();
6632         this.fireEvent("beforeclose", this, o);
6633         if(o.cancel !== true){
6634             this.tabPanel.removeTab(this.id);
6635         }
6636     },
6637     /**
6638      * The text displayed in the tooltip for the close icon.
6639      * @type String
6640      */
6641     closeText : "Close this tab"
6642 });
6643
6644 /** @private */
6645 Roo.TabPanel.prototype.createStrip = function(container){
6646     var strip = document.createElement("div");
6647     strip.className = "x-tabs-wrap";
6648     container.appendChild(strip);
6649     return strip;
6650 };
6651 /** @private */
6652 Roo.TabPanel.prototype.createStripList = function(strip){
6653     // div wrapper for retard IE
6654     // returns the "tr" element.
6655     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6656         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6657         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6658     return strip.firstChild.firstChild.firstChild.firstChild;
6659 };
6660 /** @private */
6661 Roo.TabPanel.prototype.createBody = function(container){
6662     var body = document.createElement("div");
6663     Roo.id(body, "tab-body");
6664     Roo.fly(body).addClass("x-tabs-body");
6665     container.appendChild(body);
6666     return body;
6667 };
6668 /** @private */
6669 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6670     var body = Roo.getDom(id);
6671     if(!body){
6672         body = document.createElement("div");
6673         body.id = id;
6674     }
6675     Roo.fly(body).addClass("x-tabs-item-body");
6676     bodyEl.insertBefore(body, bodyEl.firstChild);
6677     return body;
6678 };
6679 /** @private */
6680 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6681     var td = document.createElement("td");
6682     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6683     //stripEl.appendChild(td);
6684     if(closable){
6685         td.className = "x-tabs-closable";
6686         if(!this.closeTpl){
6687             this.closeTpl = new Roo.Template(
6688                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6689                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6690                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6691             );
6692         }
6693         var el = this.closeTpl.overwrite(td, {"text": text});
6694         var close = el.getElementsByTagName("div")[0];
6695         var inner = el.getElementsByTagName("em")[0];
6696         return {"el": el, "close": close, "inner": inner};
6697     } else {
6698         if(!this.tabTpl){
6699             this.tabTpl = new Roo.Template(
6700                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6701                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6702             );
6703         }
6704         var el = this.tabTpl.overwrite(td, {"text": text});
6705         var inner = el.getElementsByTagName("em")[0];
6706         return {"el": el, "inner": inner};
6707     }
6708 };/*
6709  * Based on:
6710  * Ext JS Library 1.1.1
6711  * Copyright(c) 2006-2007, Ext JS, LLC.
6712  *
6713  * Originally Released Under LGPL - original licence link has changed is not relivant.
6714  *
6715  * Fork - LGPL
6716  * <script type="text/javascript">
6717  */
6718
6719 /**
6720  * @class Roo.Button
6721  * @extends Roo.util.Observable
6722  * Simple Button class
6723  * @cfg {String} text The button text
6724  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6725  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6726  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6727  * @cfg {Object} scope The scope of the handler
6728  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6729  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6730  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6731  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6732  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6733  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6734    applies if enableToggle = true)
6735  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6736  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6737   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6738  * @constructor
6739  * Create a new button
6740  * @param {Object} config The config object
6741  */
6742 Roo.Button = function(renderTo, config)
6743 {
6744     if (!config) {
6745         config = renderTo;
6746         renderTo = config.renderTo || false;
6747     }
6748     
6749     Roo.apply(this, config);
6750     this.addEvents({
6751         /**
6752              * @event click
6753              * Fires when this button is clicked
6754              * @param {Button} this
6755              * @param {EventObject} e The click event
6756              */
6757             "click" : true,
6758         /**
6759              * @event toggle
6760              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6761              * @param {Button} this
6762              * @param {Boolean} pressed
6763              */
6764             "toggle" : true,
6765         /**
6766              * @event mouseover
6767              * Fires when the mouse hovers over the button
6768              * @param {Button} this
6769              * @param {Event} e The event object
6770              */
6771         'mouseover' : true,
6772         /**
6773              * @event mouseout
6774              * Fires when the mouse exits the button
6775              * @param {Button} this
6776              * @param {Event} e The event object
6777              */
6778         'mouseout': true,
6779          /**
6780              * @event render
6781              * Fires when the button is rendered
6782              * @param {Button} this
6783              */
6784         'render': true
6785     });
6786     if(this.menu){
6787         this.menu = Roo.menu.MenuMgr.get(this.menu);
6788     }
6789     // register listeners first!!  - so render can be captured..
6790     Roo.util.Observable.call(this);
6791     if(renderTo){
6792         this.render(renderTo);
6793     }
6794     
6795   
6796 };
6797
6798 Roo.extend(Roo.Button, Roo.util.Observable, {
6799     /**
6800      * 
6801      */
6802     
6803     /**
6804      * Read-only. True if this button is hidden
6805      * @type Boolean
6806      */
6807     hidden : false,
6808     /**
6809      * Read-only. True if this button is disabled
6810      * @type Boolean
6811      */
6812     disabled : false,
6813     /**
6814      * Read-only. True if this button is pressed (only if enableToggle = true)
6815      * @type Boolean
6816      */
6817     pressed : false,
6818
6819     /**
6820      * @cfg {Number} tabIndex 
6821      * The DOM tabIndex for this button (defaults to undefined)
6822      */
6823     tabIndex : undefined,
6824
6825     /**
6826      * @cfg {Boolean} enableToggle
6827      * True to enable pressed/not pressed toggling (defaults to false)
6828      */
6829     enableToggle: false,
6830     /**
6831      * @cfg {Mixed} menu
6832      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6833      */
6834     menu : undefined,
6835     /**
6836      * @cfg {String} menuAlign
6837      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6838      */
6839     menuAlign : "tl-bl?",
6840
6841     /**
6842      * @cfg {String} iconCls
6843      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6844      */
6845     iconCls : undefined,
6846     /**
6847      * @cfg {String} type
6848      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6849      */
6850     type : 'button',
6851
6852     // private
6853     menuClassTarget: 'tr',
6854
6855     /**
6856      * @cfg {String} clickEvent
6857      * The type of event to map to the button's event handler (defaults to 'click')
6858      */
6859     clickEvent : 'click',
6860
6861     /**
6862      * @cfg {Boolean} handleMouseEvents
6863      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6864      */
6865     handleMouseEvents : true,
6866
6867     /**
6868      * @cfg {String} tooltipType
6869      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6870      */
6871     tooltipType : 'qtip',
6872
6873     /**
6874      * @cfg {String} cls
6875      * A CSS class to apply to the button's main element.
6876      */
6877     
6878     /**
6879      * @cfg {Roo.Template} template (Optional)
6880      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6881      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6882      * require code modifications if required elements (e.g. a button) aren't present.
6883      */
6884
6885     // private
6886     render : function(renderTo){
6887         var btn;
6888         if(this.hideParent){
6889             this.parentEl = Roo.get(renderTo);
6890         }
6891         if(!this.dhconfig){
6892             if(!this.template){
6893                 if(!Roo.Button.buttonTemplate){
6894                     // hideous table template
6895                     Roo.Button.buttonTemplate = new Roo.Template(
6896                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6897                         '<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>',
6898                         "</tr></tbody></table>");
6899                 }
6900                 this.template = Roo.Button.buttonTemplate;
6901             }
6902             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6903             var btnEl = btn.child("button:first");
6904             btnEl.on('focus', this.onFocus, this);
6905             btnEl.on('blur', this.onBlur, this);
6906             if(this.cls){
6907                 btn.addClass(this.cls);
6908             }
6909             if(this.icon){
6910                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6911             }
6912             if(this.iconCls){
6913                 btnEl.addClass(this.iconCls);
6914                 if(!this.cls){
6915                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6916                 }
6917             }
6918             if(this.tabIndex !== undefined){
6919                 btnEl.dom.tabIndex = this.tabIndex;
6920             }
6921             if(this.tooltip){
6922                 if(typeof this.tooltip == 'object'){
6923                     Roo.QuickTips.tips(Roo.apply({
6924                           target: btnEl.id
6925                     }, this.tooltip));
6926                 } else {
6927                     btnEl.dom[this.tooltipType] = this.tooltip;
6928                 }
6929             }
6930         }else{
6931             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6932         }
6933         this.el = btn;
6934         if(this.id){
6935             this.el.dom.id = this.el.id = this.id;
6936         }
6937         if(this.menu){
6938             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6939             this.menu.on("show", this.onMenuShow, this);
6940             this.menu.on("hide", this.onMenuHide, this);
6941         }
6942         btn.addClass("x-btn");
6943         if(Roo.isIE && !Roo.isIE7){
6944             this.autoWidth.defer(1, this);
6945         }else{
6946             this.autoWidth();
6947         }
6948         if(this.handleMouseEvents){
6949             btn.on("mouseover", this.onMouseOver, this);
6950             btn.on("mouseout", this.onMouseOut, this);
6951             btn.on("mousedown", this.onMouseDown, this);
6952         }
6953         btn.on(this.clickEvent, this.onClick, this);
6954         //btn.on("mouseup", this.onMouseUp, this);
6955         if(this.hidden){
6956             this.hide();
6957         }
6958         if(this.disabled){
6959             this.disable();
6960         }
6961         Roo.ButtonToggleMgr.register(this);
6962         if(this.pressed){
6963             this.el.addClass("x-btn-pressed");
6964         }
6965         if(this.repeat){
6966             var repeater = new Roo.util.ClickRepeater(btn,
6967                 typeof this.repeat == "object" ? this.repeat : {}
6968             );
6969             repeater.on("click", this.onClick,  this);
6970         }
6971         
6972         this.fireEvent('render', this);
6973         
6974     },
6975     /**
6976      * Returns the button's underlying element
6977      * @return {Roo.Element} The element
6978      */
6979     getEl : function(){
6980         return this.el;  
6981     },
6982     
6983     /**
6984      * Destroys this Button and removes any listeners.
6985      */
6986     destroy : function(){
6987         Roo.ButtonToggleMgr.unregister(this);
6988         this.el.removeAllListeners();
6989         this.purgeListeners();
6990         this.el.remove();
6991     },
6992
6993     // private
6994     autoWidth : function(){
6995         if(this.el){
6996             this.el.setWidth("auto");
6997             if(Roo.isIE7 && Roo.isStrict){
6998                 var ib = this.el.child('button');
6999                 if(ib && ib.getWidth() > 20){
7000                     ib.clip();
7001                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7002                 }
7003             }
7004             if(this.minWidth){
7005                 if(this.hidden){
7006                     this.el.beginMeasure();
7007                 }
7008                 if(this.el.getWidth() < this.minWidth){
7009                     this.el.setWidth(this.minWidth);
7010                 }
7011                 if(this.hidden){
7012                     this.el.endMeasure();
7013                 }
7014             }
7015         }
7016     },
7017
7018     /**
7019      * Assigns this button's click handler
7020      * @param {Function} handler The function to call when the button is clicked
7021      * @param {Object} scope (optional) Scope for the function passed in
7022      */
7023     setHandler : function(handler, scope){
7024         this.handler = handler;
7025         this.scope = scope;  
7026     },
7027     
7028     /**
7029      * Sets this button's text
7030      * @param {String} text The button text
7031      */
7032     setText : function(text){
7033         this.text = text;
7034         if(this.el){
7035             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7036         }
7037         this.autoWidth();
7038     },
7039     
7040     /**
7041      * Gets the text for this button
7042      * @return {String} The button text
7043      */
7044     getText : function(){
7045         return this.text;  
7046     },
7047     
7048     /**
7049      * Show this button
7050      */
7051     show: function(){
7052         this.hidden = false;
7053         if(this.el){
7054             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7055         }
7056     },
7057     
7058     /**
7059      * Hide this button
7060      */
7061     hide: function(){
7062         this.hidden = true;
7063         if(this.el){
7064             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7065         }
7066     },
7067     
7068     /**
7069      * Convenience function for boolean show/hide
7070      * @param {Boolean} visible True to show, false to hide
7071      */
7072     setVisible: function(visible){
7073         if(visible) {
7074             this.show();
7075         }else{
7076             this.hide();
7077         }
7078     },
7079     
7080     /**
7081      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7082      * @param {Boolean} state (optional) Force a particular state
7083      */
7084     toggle : function(state){
7085         state = state === undefined ? !this.pressed : state;
7086         if(state != this.pressed){
7087             if(state){
7088                 this.el.addClass("x-btn-pressed");
7089                 this.pressed = true;
7090                 this.fireEvent("toggle", this, true);
7091             }else{
7092                 this.el.removeClass("x-btn-pressed");
7093                 this.pressed = false;
7094                 this.fireEvent("toggle", this, false);
7095             }
7096             if(this.toggleHandler){
7097                 this.toggleHandler.call(this.scope || this, this, state);
7098             }
7099         }
7100     },
7101     
7102     /**
7103      * Focus the button
7104      */
7105     focus : function(){
7106         this.el.child('button:first').focus();
7107     },
7108     
7109     /**
7110      * Disable this button
7111      */
7112     disable : function(){
7113         if(this.el){
7114             this.el.addClass("x-btn-disabled");
7115         }
7116         this.disabled = true;
7117     },
7118     
7119     /**
7120      * Enable this button
7121      */
7122     enable : function(){
7123         if(this.el){
7124             this.el.removeClass("x-btn-disabled");
7125         }
7126         this.disabled = false;
7127     },
7128
7129     /**
7130      * Convenience function for boolean enable/disable
7131      * @param {Boolean} enabled True to enable, false to disable
7132      */
7133     setDisabled : function(v){
7134         this[v !== true ? "enable" : "disable"]();
7135     },
7136
7137     // private
7138     onClick : function(e)
7139     {
7140         if(e){
7141             e.preventDefault();
7142         }
7143         if(e.button != 0){
7144             return;
7145         }
7146         if(!this.disabled){
7147             if(this.enableToggle){
7148                 this.toggle();
7149             }
7150             if(this.menu && !this.menu.isVisible()){
7151                 this.menu.show(this.el, this.menuAlign);
7152             }
7153             this.fireEvent("click", this, e);
7154             if(this.handler){
7155                 this.el.removeClass("x-btn-over");
7156                 this.handler.call(this.scope || this, this, e);
7157             }
7158         }
7159     },
7160     // private
7161     onMouseOver : function(e){
7162         if(!this.disabled){
7163             this.el.addClass("x-btn-over");
7164             this.fireEvent('mouseover', this, e);
7165         }
7166     },
7167     // private
7168     onMouseOut : function(e){
7169         if(!e.within(this.el,  true)){
7170             this.el.removeClass("x-btn-over");
7171             this.fireEvent('mouseout', this, e);
7172         }
7173     },
7174     // private
7175     onFocus : function(e){
7176         if(!this.disabled){
7177             this.el.addClass("x-btn-focus");
7178         }
7179     },
7180     // private
7181     onBlur : function(e){
7182         this.el.removeClass("x-btn-focus");
7183     },
7184     // private
7185     onMouseDown : function(e){
7186         if(!this.disabled && e.button == 0){
7187             this.el.addClass("x-btn-click");
7188             Roo.get(document).on('mouseup', this.onMouseUp, this);
7189         }
7190     },
7191     // private
7192     onMouseUp : function(e){
7193         if(e.button == 0){
7194             this.el.removeClass("x-btn-click");
7195             Roo.get(document).un('mouseup', this.onMouseUp, this);
7196         }
7197     },
7198     // private
7199     onMenuShow : function(e){
7200         this.el.addClass("x-btn-menu-active");
7201     },
7202     // private
7203     onMenuHide : function(e){
7204         this.el.removeClass("x-btn-menu-active");
7205     }   
7206 });
7207
7208 // Private utility class used by Button
7209 Roo.ButtonToggleMgr = function(){
7210    var groups = {};
7211    
7212    function toggleGroup(btn, state){
7213        if(state){
7214            var g = groups[btn.toggleGroup];
7215            for(var i = 0, l = g.length; i < l; i++){
7216                if(g[i] != btn){
7217                    g[i].toggle(false);
7218                }
7219            }
7220        }
7221    }
7222    
7223    return {
7224        register : function(btn){
7225            if(!btn.toggleGroup){
7226                return;
7227            }
7228            var g = groups[btn.toggleGroup];
7229            if(!g){
7230                g = groups[btn.toggleGroup] = [];
7231            }
7232            g.push(btn);
7233            btn.on("toggle", toggleGroup);
7234        },
7235        
7236        unregister : function(btn){
7237            if(!btn.toggleGroup){
7238                return;
7239            }
7240            var g = groups[btn.toggleGroup];
7241            if(g){
7242                g.remove(btn);
7243                btn.un("toggle", toggleGroup);
7244            }
7245        }
7246    };
7247 }();/*
7248  * Based on:
7249  * Ext JS Library 1.1.1
7250  * Copyright(c) 2006-2007, Ext JS, LLC.
7251  *
7252  * Originally Released Under LGPL - original licence link has changed is not relivant.
7253  *
7254  * Fork - LGPL
7255  * <script type="text/javascript">
7256  */
7257  
7258 /**
7259  * @class Roo.SplitButton
7260  * @extends Roo.Button
7261  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7262  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7263  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7264  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7265  * @cfg {String} arrowTooltip The title attribute of the arrow
7266  * @constructor
7267  * Create a new menu button
7268  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7269  * @param {Object} config The config object
7270  */
7271 Roo.SplitButton = function(renderTo, config){
7272     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7273     /**
7274      * @event arrowclick
7275      * Fires when this button's arrow is clicked
7276      * @param {SplitButton} this
7277      * @param {EventObject} e The click event
7278      */
7279     this.addEvents({"arrowclick":true});
7280 };
7281
7282 Roo.extend(Roo.SplitButton, Roo.Button, {
7283     render : function(renderTo){
7284         // this is one sweet looking template!
7285         var tpl = new Roo.Template(
7286             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7287             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7288             '<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>',
7289             "</tbody></table></td><td>",
7290             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7291             '<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>',
7292             "</tbody></table></td></tr></table>"
7293         );
7294         var btn = tpl.append(renderTo, [this.text, this.type], true);
7295         var btnEl = btn.child("button");
7296         if(this.cls){
7297             btn.addClass(this.cls);
7298         }
7299         if(this.icon){
7300             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7301         }
7302         if(this.iconCls){
7303             btnEl.addClass(this.iconCls);
7304             if(!this.cls){
7305                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7306             }
7307         }
7308         this.el = btn;
7309         if(this.handleMouseEvents){
7310             btn.on("mouseover", this.onMouseOver, this);
7311             btn.on("mouseout", this.onMouseOut, this);
7312             btn.on("mousedown", this.onMouseDown, this);
7313             btn.on("mouseup", this.onMouseUp, this);
7314         }
7315         btn.on(this.clickEvent, this.onClick, this);
7316         if(this.tooltip){
7317             if(typeof this.tooltip == 'object'){
7318                 Roo.QuickTips.tips(Roo.apply({
7319                       target: btnEl.id
7320                 }, this.tooltip));
7321             } else {
7322                 btnEl.dom[this.tooltipType] = this.tooltip;
7323             }
7324         }
7325         if(this.arrowTooltip){
7326             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7327         }
7328         if(this.hidden){
7329             this.hide();
7330         }
7331         if(this.disabled){
7332             this.disable();
7333         }
7334         if(this.pressed){
7335             this.el.addClass("x-btn-pressed");
7336         }
7337         if(Roo.isIE && !Roo.isIE7){
7338             this.autoWidth.defer(1, this);
7339         }else{
7340             this.autoWidth();
7341         }
7342         if(this.menu){
7343             this.menu.on("show", this.onMenuShow, this);
7344             this.menu.on("hide", this.onMenuHide, this);
7345         }
7346         this.fireEvent('render', this);
7347     },
7348
7349     // private
7350     autoWidth : function(){
7351         if(this.el){
7352             var tbl = this.el.child("table:first");
7353             var tbl2 = this.el.child("table:last");
7354             this.el.setWidth("auto");
7355             tbl.setWidth("auto");
7356             if(Roo.isIE7 && Roo.isStrict){
7357                 var ib = this.el.child('button:first');
7358                 if(ib && ib.getWidth() > 20){
7359                     ib.clip();
7360                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7361                 }
7362             }
7363             if(this.minWidth){
7364                 if(this.hidden){
7365                     this.el.beginMeasure();
7366                 }
7367                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7368                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7369                 }
7370                 if(this.hidden){
7371                     this.el.endMeasure();
7372                 }
7373             }
7374             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7375         } 
7376     },
7377     /**
7378      * Sets this button's click handler
7379      * @param {Function} handler The function to call when the button is clicked
7380      * @param {Object} scope (optional) Scope for the function passed above
7381      */
7382     setHandler : function(handler, scope){
7383         this.handler = handler;
7384         this.scope = scope;  
7385     },
7386     
7387     /**
7388      * Sets this button's arrow click handler
7389      * @param {Function} handler The function to call when the arrow is clicked
7390      * @param {Object} scope (optional) Scope for the function passed above
7391      */
7392     setArrowHandler : function(handler, scope){
7393         this.arrowHandler = handler;
7394         this.scope = scope;  
7395     },
7396     
7397     /**
7398      * Focus the button
7399      */
7400     focus : function(){
7401         if(this.el){
7402             this.el.child("button:first").focus();
7403         }
7404     },
7405
7406     // private
7407     onClick : function(e){
7408         e.preventDefault();
7409         if(!this.disabled){
7410             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7411                 if(this.menu && !this.menu.isVisible()){
7412                     this.menu.show(this.el, this.menuAlign);
7413                 }
7414                 this.fireEvent("arrowclick", this, e);
7415                 if(this.arrowHandler){
7416                     this.arrowHandler.call(this.scope || this, this, e);
7417                 }
7418             }else{
7419                 this.fireEvent("click", this, e);
7420                 if(this.handler){
7421                     this.handler.call(this.scope || this, this, e);
7422                 }
7423             }
7424         }
7425     },
7426     // private
7427     onMouseDown : function(e){
7428         if(!this.disabled){
7429             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7430         }
7431     },
7432     // private
7433     onMouseUp : function(e){
7434         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7435     }   
7436 });
7437
7438
7439 // backwards compat
7440 Roo.MenuButton = Roo.SplitButton;/*
7441  * Based on:
7442  * Ext JS Library 1.1.1
7443  * Copyright(c) 2006-2007, Ext JS, LLC.
7444  *
7445  * Originally Released Under LGPL - original licence link has changed is not relivant.
7446  *
7447  * Fork - LGPL
7448  * <script type="text/javascript">
7449  */
7450
7451 /**
7452  * @class Roo.Toolbar
7453  * Basic Toolbar class.
7454  * @constructor
7455  * Creates a new Toolbar
7456  * @param {Object} container The config object
7457  */ 
7458 Roo.Toolbar = function(container, buttons, config)
7459 {
7460     /// old consturctor format still supported..
7461     if(container instanceof Array){ // omit the container for later rendering
7462         buttons = container;
7463         config = buttons;
7464         container = null;
7465     }
7466     if (typeof(container) == 'object' && container.xtype) {
7467         config = container;
7468         container = config.container;
7469         buttons = config.buttons || []; // not really - use items!!
7470     }
7471     var xitems = [];
7472     if (config && config.items) {
7473         xitems = config.items;
7474         delete config.items;
7475     }
7476     Roo.apply(this, config);
7477     this.buttons = buttons;
7478     
7479     if(container){
7480         this.render(container);
7481     }
7482     this.xitems = xitems;
7483     Roo.each(xitems, function(b) {
7484         this.add(b);
7485     }, this);
7486     
7487 };
7488
7489 Roo.Toolbar.prototype = {
7490     /**
7491      * @cfg {Array} items
7492      * array of button configs or elements to add (will be converted to a MixedCollection)
7493      */
7494     
7495     /**
7496      * @cfg {String/HTMLElement/Element} container
7497      * The id or element that will contain the toolbar
7498      */
7499     // private
7500     render : function(ct){
7501         this.el = Roo.get(ct);
7502         if(this.cls){
7503             this.el.addClass(this.cls);
7504         }
7505         // using a table allows for vertical alignment
7506         // 100% width is needed by Safari...
7507         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7508         this.tr = this.el.child("tr", true);
7509         var autoId = 0;
7510         this.items = new Roo.util.MixedCollection(false, function(o){
7511             return o.id || ("item" + (++autoId));
7512         });
7513         if(this.buttons){
7514             this.add.apply(this, this.buttons);
7515             delete this.buttons;
7516         }
7517     },
7518
7519     /**
7520      * Adds element(s) to the toolbar -- this function takes a variable number of 
7521      * arguments of mixed type and adds them to the toolbar.
7522      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7523      * <ul>
7524      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7525      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7526      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7527      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7528      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7529      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7530      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7531      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7532      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7533      * </ul>
7534      * @param {Mixed} arg2
7535      * @param {Mixed} etc.
7536      */
7537     add : function(){
7538         var a = arguments, l = a.length;
7539         for(var i = 0; i < l; i++){
7540             this._add(a[i]);
7541         }
7542     },
7543     // private..
7544     _add : function(el) {
7545         
7546         if (el.xtype) {
7547             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7548         }
7549         
7550         if (el.applyTo){ // some kind of form field
7551             return this.addField(el);
7552         } 
7553         if (el.render){ // some kind of Toolbar.Item
7554             return this.addItem(el);
7555         }
7556         if (typeof el == "string"){ // string
7557             if(el == "separator" || el == "-"){
7558                 return this.addSeparator();
7559             }
7560             if (el == " "){
7561                 return this.addSpacer();
7562             }
7563             if(el == "->"){
7564                 return this.addFill();
7565             }
7566             return this.addText(el);
7567             
7568         }
7569         if(el.tagName){ // element
7570             return this.addElement(el);
7571         }
7572         if(typeof el == "object"){ // must be button config?
7573             return this.addButton(el);
7574         }
7575         // and now what?!?!
7576         return false;
7577         
7578     },
7579     
7580     /**
7581      * Add an Xtype element
7582      * @param {Object} xtype Xtype Object
7583      * @return {Object} created Object
7584      */
7585     addxtype : function(e){
7586         return this.add(e);  
7587     },
7588     
7589     /**
7590      * Returns the Element for this toolbar.
7591      * @return {Roo.Element}
7592      */
7593     getEl : function(){
7594         return this.el;  
7595     },
7596     
7597     /**
7598      * Adds a separator
7599      * @return {Roo.Toolbar.Item} The separator item
7600      */
7601     addSeparator : function(){
7602         return this.addItem(new Roo.Toolbar.Separator());
7603     },
7604
7605     /**
7606      * Adds a spacer element
7607      * @return {Roo.Toolbar.Spacer} The spacer item
7608      */
7609     addSpacer : function(){
7610         return this.addItem(new Roo.Toolbar.Spacer());
7611     },
7612
7613     /**
7614      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7615      * @return {Roo.Toolbar.Fill} The fill item
7616      */
7617     addFill : function(){
7618         return this.addItem(new Roo.Toolbar.Fill());
7619     },
7620
7621     /**
7622      * Adds any standard HTML element to the toolbar
7623      * @param {String/HTMLElement/Element} el The element or id of the element to add
7624      * @return {Roo.Toolbar.Item} The element's item
7625      */
7626     addElement : function(el){
7627         return this.addItem(new Roo.Toolbar.Item(el));
7628     },
7629     /**
7630      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7631      * @type Roo.util.MixedCollection  
7632      */
7633     items : false,
7634      
7635     /**
7636      * Adds any Toolbar.Item or subclass
7637      * @param {Roo.Toolbar.Item} item
7638      * @return {Roo.Toolbar.Item} The item
7639      */
7640     addItem : function(item){
7641         var td = this.nextBlock();
7642         item.render(td);
7643         this.items.add(item);
7644         return item;
7645     },
7646     
7647     /**
7648      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7649      * @param {Object/Array} config A button config or array of configs
7650      * @return {Roo.Toolbar.Button/Array}
7651      */
7652     addButton : function(config){
7653         if(config instanceof Array){
7654             var buttons = [];
7655             for(var i = 0, len = config.length; i < len; i++) {
7656                 buttons.push(this.addButton(config[i]));
7657             }
7658             return buttons;
7659         }
7660         var b = config;
7661         if(!(config instanceof Roo.Toolbar.Button)){
7662             b = config.split ?
7663                 new Roo.Toolbar.SplitButton(config) :
7664                 new Roo.Toolbar.Button(config);
7665         }
7666         var td = this.nextBlock();
7667         b.render(td);
7668         this.items.add(b);
7669         return b;
7670     },
7671     
7672     /**
7673      * Adds text to the toolbar
7674      * @param {String} text The text to add
7675      * @return {Roo.Toolbar.Item} The element's item
7676      */
7677     addText : function(text){
7678         return this.addItem(new Roo.Toolbar.TextItem(text));
7679     },
7680     
7681     /**
7682      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7683      * @param {Number} index The index where the item is to be inserted
7684      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7685      * @return {Roo.Toolbar.Button/Item}
7686      */
7687     insertButton : function(index, item){
7688         if(item instanceof Array){
7689             var buttons = [];
7690             for(var i = 0, len = item.length; i < len; i++) {
7691                buttons.push(this.insertButton(index + i, item[i]));
7692             }
7693             return buttons;
7694         }
7695         if (!(item instanceof Roo.Toolbar.Button)){
7696            item = new Roo.Toolbar.Button(item);
7697         }
7698         var td = document.createElement("td");
7699         this.tr.insertBefore(td, this.tr.childNodes[index]);
7700         item.render(td);
7701         this.items.insert(index, item);
7702         return item;
7703     },
7704     
7705     /**
7706      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7707      * @param {Object} config
7708      * @return {Roo.Toolbar.Item} The element's item
7709      */
7710     addDom : function(config, returnEl){
7711         var td = this.nextBlock();
7712         Roo.DomHelper.overwrite(td, config);
7713         var ti = new Roo.Toolbar.Item(td.firstChild);
7714         ti.render(td);
7715         this.items.add(ti);
7716         return ti;
7717     },
7718
7719     /**
7720      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7721      * @type Roo.util.MixedCollection  
7722      */
7723     fields : false,
7724     
7725     /**
7726      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7727      * Note: the field should not have been rendered yet. For a field that has already been
7728      * rendered, use {@link #addElement}.
7729      * @param {Roo.form.Field} field
7730      * @return {Roo.ToolbarItem}
7731      */
7732      
7733       
7734     addField : function(field) {
7735         if (!this.fields) {
7736             var autoId = 0;
7737             this.fields = new Roo.util.MixedCollection(false, function(o){
7738                 return o.id || ("item" + (++autoId));
7739             });
7740
7741         }
7742         
7743         var td = this.nextBlock();
7744         field.render(td);
7745         var ti = new Roo.Toolbar.Item(td.firstChild);
7746         ti.render(td);
7747         this.items.add(ti);
7748         this.fields.add(field);
7749         return ti;
7750     },
7751     /**
7752      * Hide the toolbar
7753      * @method hide
7754      */
7755      
7756       
7757     hide : function()
7758     {
7759         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7760         this.el.child('div').hide();
7761     },
7762     /**
7763      * Show the toolbar
7764      * @method show
7765      */
7766     show : function()
7767     {
7768         this.el.child('div').show();
7769     },
7770       
7771     // private
7772     nextBlock : function(){
7773         var td = document.createElement("td");
7774         this.tr.appendChild(td);
7775         return td;
7776     },
7777
7778     // private
7779     destroy : function(){
7780         if(this.items){ // rendered?
7781             Roo.destroy.apply(Roo, this.items.items);
7782         }
7783         if(this.fields){ // rendered?
7784             Roo.destroy.apply(Roo, this.fields.items);
7785         }
7786         Roo.Element.uncache(this.el, this.tr);
7787     }
7788 };
7789
7790 /**
7791  * @class Roo.Toolbar.Item
7792  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7793  * @constructor
7794  * Creates a new Item
7795  * @param {HTMLElement} el 
7796  */
7797 Roo.Toolbar.Item = function(el){
7798     var cfg = {};
7799     if (typeof (el.xtype) != 'undefined') {
7800         cfg = el;
7801         el = cfg.el;
7802     }
7803     
7804     this.el = Roo.getDom(el);
7805     this.id = Roo.id(this.el);
7806     this.hidden = false;
7807     
7808     this.addEvents({
7809          /**
7810              * @event render
7811              * Fires when the button is rendered
7812              * @param {Button} this
7813              */
7814         'render': true
7815     });
7816     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7817 };
7818 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7819 //Roo.Toolbar.Item.prototype = {
7820     
7821     /**
7822      * Get this item's HTML Element
7823      * @return {HTMLElement}
7824      */
7825     getEl : function(){
7826        return this.el;  
7827     },
7828
7829     // private
7830     render : function(td){
7831         
7832          this.td = td;
7833         td.appendChild(this.el);
7834         
7835         this.fireEvent('render', this);
7836     },
7837     
7838     /**
7839      * Removes and destroys this item.
7840      */
7841     destroy : function(){
7842         this.td.parentNode.removeChild(this.td);
7843     },
7844     
7845     /**
7846      * Shows this item.
7847      */
7848     show: function(){
7849         this.hidden = false;
7850         this.td.style.display = "";
7851     },
7852     
7853     /**
7854      * Hides this item.
7855      */
7856     hide: function(){
7857         this.hidden = true;
7858         this.td.style.display = "none";
7859     },
7860     
7861     /**
7862      * Convenience function for boolean show/hide.
7863      * @param {Boolean} visible true to show/false to hide
7864      */
7865     setVisible: function(visible){
7866         if(visible) {
7867             this.show();
7868         }else{
7869             this.hide();
7870         }
7871     },
7872     
7873     /**
7874      * Try to focus this item.
7875      */
7876     focus : function(){
7877         Roo.fly(this.el).focus();
7878     },
7879     
7880     /**
7881      * Disables this item.
7882      */
7883     disable : function(){
7884         Roo.fly(this.td).addClass("x-item-disabled");
7885         this.disabled = true;
7886         this.el.disabled = true;
7887     },
7888     
7889     /**
7890      * Enables this item.
7891      */
7892     enable : function(){
7893         Roo.fly(this.td).removeClass("x-item-disabled");
7894         this.disabled = false;
7895         this.el.disabled = false;
7896     }
7897 });
7898
7899
7900 /**
7901  * @class Roo.Toolbar.Separator
7902  * @extends Roo.Toolbar.Item
7903  * A simple toolbar separator class
7904  * @constructor
7905  * Creates a new Separator
7906  */
7907 Roo.Toolbar.Separator = function(cfg){
7908     
7909     var s = document.createElement("span");
7910     s.className = "ytb-sep";
7911     if (cfg) {
7912         cfg.el = s;
7913     }
7914     
7915     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7916 };
7917 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7918     enable:Roo.emptyFn,
7919     disable:Roo.emptyFn,
7920     focus:Roo.emptyFn
7921 });
7922
7923 /**
7924  * @class Roo.Toolbar.Spacer
7925  * @extends Roo.Toolbar.Item
7926  * A simple element that adds extra horizontal space to a toolbar.
7927  * @constructor
7928  * Creates a new Spacer
7929  */
7930 Roo.Toolbar.Spacer = function(cfg){
7931     var s = document.createElement("div");
7932     s.className = "ytb-spacer";
7933     if (cfg) {
7934         cfg.el = s;
7935     }
7936     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7937 };
7938 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Fill
7946  * @extends Roo.Toolbar.Spacer
7947  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7948  * @constructor
7949  * Creates a new Spacer
7950  */
7951 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7952     // private
7953     render : function(td){
7954         td.style.width = '100%';
7955         Roo.Toolbar.Fill.superclass.render.call(this, td);
7956     }
7957 });
7958
7959 /**
7960  * @class Roo.Toolbar.TextItem
7961  * @extends Roo.Toolbar.Item
7962  * A simple class that renders text directly into a toolbar.
7963  * @constructor
7964  * Creates a new TextItem
7965  * @param {String} text
7966  */
7967 Roo.Toolbar.TextItem = function(cfg){
7968     var  text = cfg || "";
7969     if (typeof(cfg) == 'object') {
7970         text = cfg.text || "";
7971     }  else {
7972         cfg = null;
7973     }
7974     var s = document.createElement("span");
7975     s.className = "ytb-text";
7976     s.innerHTML = text;
7977     if (cfg) {
7978         cfg.el  = s;
7979     }
7980     
7981     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7982 };
7983 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7984     
7985      
7986     enable:Roo.emptyFn,
7987     disable:Roo.emptyFn,
7988     focus:Roo.emptyFn
7989 });
7990
7991 /**
7992  * @class Roo.Toolbar.Button
7993  * @extends Roo.Button
7994  * A button that renders into a toolbar.
7995  * @constructor
7996  * Creates a new Button
7997  * @param {Object} config A standard {@link Roo.Button} config object
7998  */
7999 Roo.Toolbar.Button = function(config){
8000     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
8001 };
8002 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
8003     render : function(td){
8004         this.td = td;
8005         Roo.Toolbar.Button.superclass.render.call(this, td);
8006     },
8007     
8008     /**
8009      * Removes and destroys this button
8010      */
8011     destroy : function(){
8012         Roo.Toolbar.Button.superclass.destroy.call(this);
8013         this.td.parentNode.removeChild(this.td);
8014     },
8015     
8016     /**
8017      * Shows this button
8018      */
8019     show: function(){
8020         this.hidden = false;
8021         this.td.style.display = "";
8022     },
8023     
8024     /**
8025      * Hides this button
8026      */
8027     hide: function(){
8028         this.hidden = true;
8029         this.td.style.display = "none";
8030     },
8031
8032     /**
8033      * Disables this item
8034      */
8035     disable : function(){
8036         Roo.fly(this.td).addClass("x-item-disabled");
8037         this.disabled = true;
8038     },
8039
8040     /**
8041      * Enables this item
8042      */
8043     enable : function(){
8044         Roo.fly(this.td).removeClass("x-item-disabled");
8045         this.disabled = false;
8046     }
8047 });
8048 // backwards compat
8049 Roo.ToolbarButton = Roo.Toolbar.Button;
8050
8051 /**
8052  * @class Roo.Toolbar.SplitButton
8053  * @extends Roo.SplitButton
8054  * A menu button that renders into a toolbar.
8055  * @constructor
8056  * Creates a new SplitButton
8057  * @param {Object} config A standard {@link Roo.SplitButton} config object
8058  */
8059 Roo.Toolbar.SplitButton = function(config){
8060     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8061 };
8062 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8063     render : function(td){
8064         this.td = td;
8065         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8066     },
8067     
8068     /**
8069      * Removes and destroys this button
8070      */
8071     destroy : function(){
8072         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8073         this.td.parentNode.removeChild(this.td);
8074     },
8075     
8076     /**
8077      * Shows this button
8078      */
8079     show: function(){
8080         this.hidden = false;
8081         this.td.style.display = "";
8082     },
8083     
8084     /**
8085      * Hides this button
8086      */
8087     hide: function(){
8088         this.hidden = true;
8089         this.td.style.display = "none";
8090     }
8091 });
8092
8093 // backwards compat
8094 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8095  * Based on:
8096  * Ext JS Library 1.1.1
8097  * Copyright(c) 2006-2007, Ext JS, LLC.
8098  *
8099  * Originally Released Under LGPL - original licence link has changed is not relivant.
8100  *
8101  * Fork - LGPL
8102  * <script type="text/javascript">
8103  */
8104  
8105 /**
8106  * @class Roo.PagingToolbar
8107  * @extends Roo.Toolbar
8108  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8109  * @constructor
8110  * Create a new PagingToolbar
8111  * @param {Object} config The config object
8112  */
8113 Roo.PagingToolbar = function(el, ds, config)
8114 {
8115     // old args format still supported... - xtype is prefered..
8116     if (typeof(el) == 'object' && el.xtype) {
8117         // created from xtype...
8118         config = el;
8119         ds = el.dataSource;
8120         el = config.container;
8121     }
8122     var items = [];
8123     if (config.items) {
8124         items = config.items;
8125         config.items = [];
8126     }
8127     
8128     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8129     this.ds = ds;
8130     this.cursor = 0;
8131     this.renderButtons(this.el);
8132     this.bind(ds);
8133     
8134     // supprot items array.
8135    
8136     Roo.each(items, function(e) {
8137         this.add(Roo.factory(e));
8138     },this);
8139     
8140 };
8141
8142 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8143     /**
8144      * @cfg {Roo.data.Store} dataSource
8145      * The underlying data store providing the paged data
8146      */
8147     /**
8148      * @cfg {String/HTMLElement/Element} container
8149      * container The id or element that will contain the toolbar
8150      */
8151     /**
8152      * @cfg {Boolean} displayInfo
8153      * True to display the displayMsg (defaults to false)
8154      */
8155     /**
8156      * @cfg {Number} pageSize
8157      * The number of records to display per page (defaults to 20)
8158      */
8159     pageSize: 20,
8160     /**
8161      * @cfg {String} displayMsg
8162      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8163      */
8164     displayMsg : 'Displaying {0} - {1} of {2}',
8165     /**
8166      * @cfg {String} emptyMsg
8167      * The message to display when no records are found (defaults to "No data to display")
8168      */
8169     emptyMsg : 'No data to display',
8170     /**
8171      * Customizable piece of the default paging text (defaults to "Page")
8172      * @type String
8173      */
8174     beforePageText : "Page",
8175     /**
8176      * Customizable piece of the default paging text (defaults to "of %0")
8177      * @type String
8178      */
8179     afterPageText : "of {0}",
8180     /**
8181      * Customizable piece of the default paging text (defaults to "First Page")
8182      * @type String
8183      */
8184     firstText : "First Page",
8185     /**
8186      * Customizable piece of the default paging text (defaults to "Previous Page")
8187      * @type String
8188      */
8189     prevText : "Previous Page",
8190     /**
8191      * Customizable piece of the default paging text (defaults to "Next Page")
8192      * @type String
8193      */
8194     nextText : "Next Page",
8195     /**
8196      * Customizable piece of the default paging text (defaults to "Last Page")
8197      * @type String
8198      */
8199     lastText : "Last Page",
8200     /**
8201      * Customizable piece of the default paging text (defaults to "Refresh")
8202      * @type String
8203      */
8204     refreshText : "Refresh",
8205
8206     // private
8207     renderButtons : function(el){
8208         Roo.PagingToolbar.superclass.render.call(this, el);
8209         this.first = this.addButton({
8210             tooltip: this.firstText,
8211             cls: "x-btn-icon x-grid-page-first",
8212             disabled: true,
8213             handler: this.onClick.createDelegate(this, ["first"])
8214         });
8215         this.prev = this.addButton({
8216             tooltip: this.prevText,
8217             cls: "x-btn-icon x-grid-page-prev",
8218             disabled: true,
8219             handler: this.onClick.createDelegate(this, ["prev"])
8220         });
8221         //this.addSeparator();
8222         this.add(this.beforePageText);
8223         this.field = Roo.get(this.addDom({
8224            tag: "input",
8225            type: "text",
8226            size: "3",
8227            value: "1",
8228            cls: "x-grid-page-number"
8229         }).el);
8230         this.field.on("keydown", this.onPagingKeydown, this);
8231         this.field.on("focus", function(){this.dom.select();});
8232         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8233         this.field.setHeight(18);
8234         //this.addSeparator();
8235         this.next = this.addButton({
8236             tooltip: this.nextText,
8237             cls: "x-btn-icon x-grid-page-next",
8238             disabled: true,
8239             handler: this.onClick.createDelegate(this, ["next"])
8240         });
8241         this.last = this.addButton({
8242             tooltip: this.lastText,
8243             cls: "x-btn-icon x-grid-page-last",
8244             disabled: true,
8245             handler: this.onClick.createDelegate(this, ["last"])
8246         });
8247         //this.addSeparator();
8248         this.loading = this.addButton({
8249             tooltip: this.refreshText,
8250             cls: "x-btn-icon x-grid-loading",
8251             handler: this.onClick.createDelegate(this, ["refresh"])
8252         });
8253
8254         if(this.displayInfo){
8255             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8256         }
8257     },
8258
8259     // private
8260     updateInfo : function(){
8261         if(this.displayEl){
8262             var count = this.ds.getCount();
8263             var msg = count == 0 ?
8264                 this.emptyMsg :
8265                 String.format(
8266                     this.displayMsg,
8267                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8268                 );
8269             this.displayEl.update(msg);
8270         }
8271     },
8272
8273     // private
8274     onLoad : function(ds, r, o){
8275        this.cursor = o.params ? o.params.start : 0;
8276        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8277
8278        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8279        this.field.dom.value = ap;
8280        this.first.setDisabled(ap == 1);
8281        this.prev.setDisabled(ap == 1);
8282        this.next.setDisabled(ap == ps);
8283        this.last.setDisabled(ap == ps);
8284        this.loading.enable();
8285        this.updateInfo();
8286     },
8287
8288     // private
8289     getPageData : function(){
8290         var total = this.ds.getTotalCount();
8291         return {
8292             total : total,
8293             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8294             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8295         };
8296     },
8297
8298     // private
8299     onLoadError : function(){
8300         this.loading.enable();
8301     },
8302
8303     // private
8304     onPagingKeydown : function(e){
8305         var k = e.getKey();
8306         var d = this.getPageData();
8307         if(k == e.RETURN){
8308             var v = this.field.dom.value, pageNum;
8309             if(!v || isNaN(pageNum = parseInt(v, 10))){
8310                 this.field.dom.value = d.activePage;
8311                 return;
8312             }
8313             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8314             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8315             e.stopEvent();
8316         }
8317         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))
8318         {
8319           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8320           this.field.dom.value = pageNum;
8321           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8322           e.stopEvent();
8323         }
8324         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8325         {
8326           var v = this.field.dom.value, pageNum; 
8327           var increment = (e.shiftKey) ? 10 : 1;
8328           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8329             increment *= -1;
8330           }
8331           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8332             this.field.dom.value = d.activePage;
8333             return;
8334           }
8335           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8336           {
8337             this.field.dom.value = parseInt(v, 10) + increment;
8338             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8340           }
8341           e.stopEvent();
8342         }
8343     },
8344
8345     // private
8346     beforeLoad : function(){
8347         if(this.loading){
8348             this.loading.disable();
8349         }
8350     },
8351
8352     // private
8353     onClick : function(which){
8354         var ds = this.ds;
8355         switch(which){
8356             case "first":
8357                 ds.load({params:{start: 0, limit: this.pageSize}});
8358             break;
8359             case "prev":
8360                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8361             break;
8362             case "next":
8363                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8364             break;
8365             case "last":
8366                 var total = ds.getTotalCount();
8367                 var extra = total % this.pageSize;
8368                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8369                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8370             break;
8371             case "refresh":
8372                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8373             break;
8374         }
8375     },
8376
8377     /**
8378      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8379      * @param {Roo.data.Store} store The data store to unbind
8380      */
8381     unbind : function(ds){
8382         ds.un("beforeload", this.beforeLoad, this);
8383         ds.un("load", this.onLoad, this);
8384         ds.un("loadexception", this.onLoadError, this);
8385         ds.un("remove", this.updateInfo, this);
8386         ds.un("add", this.updateInfo, this);
8387         this.ds = undefined;
8388     },
8389
8390     /**
8391      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8392      * @param {Roo.data.Store} store The data store to bind
8393      */
8394     bind : function(ds){
8395         ds.on("beforeload", this.beforeLoad, this);
8396         ds.on("load", this.onLoad, this);
8397         ds.on("loadexception", this.onLoadError, this);
8398         ds.on("remove", this.updateInfo, this);
8399         ds.on("add", this.updateInfo, this);
8400         this.ds = ds;
8401     }
8402 });/*
8403  * Based on:
8404  * Ext JS Library 1.1.1
8405  * Copyright(c) 2006-2007, Ext JS, LLC.
8406  *
8407  * Originally Released Under LGPL - original licence link has changed is not relivant.
8408  *
8409  * Fork - LGPL
8410  * <script type="text/javascript">
8411  */
8412
8413 /**
8414  * @class Roo.Resizable
8415  * @extends Roo.util.Observable
8416  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8417  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8418  * 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
8419  * the element will be wrapped for you automatically.</p>
8420  * <p>Here is the list of valid resize handles:</p>
8421  * <pre>
8422 Value   Description
8423 ------  -------------------
8424  'n'     north
8425  's'     south
8426  'e'     east
8427  'w'     west
8428  'nw'    northwest
8429  'sw'    southwest
8430  'se'    southeast
8431  'ne'    northeast
8432  'hd'    horizontal drag
8433  'all'   all
8434 </pre>
8435  * <p>Here's an example showing the creation of a typical Resizable:</p>
8436  * <pre><code>
8437 var resizer = new Roo.Resizable("element-id", {
8438     handles: 'all',
8439     minWidth: 200,
8440     minHeight: 100,
8441     maxWidth: 500,
8442     maxHeight: 400,
8443     pinned: true
8444 });
8445 resizer.on("resize", myHandler);
8446 </code></pre>
8447  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8448  * resizer.east.setDisplayed(false);</p>
8449  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8450  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8451  * resize operation's new size (defaults to [0, 0])
8452  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8453  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8454  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8455  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8456  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8457  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8458  * @cfg {Number} width The width of the element in pixels (defaults to null)
8459  * @cfg {Number} height The height of the element in pixels (defaults to null)
8460  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8461  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8462  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8463  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8464  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8465  * in favor of the handles config option (defaults to false)
8466  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8467  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8468  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8469  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8470  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8471  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8472  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8473  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8474  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8475  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8476  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8477  * @constructor
8478  * Create a new resizable component
8479  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8480  * @param {Object} config configuration options
8481   */
8482 Roo.Resizable = function(el, config)
8483 {
8484     this.el = Roo.get(el);
8485
8486     if(config && config.wrap){
8487         config.resizeChild = this.el;
8488         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8489         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8490         this.el.setStyle("overflow", "hidden");
8491         this.el.setPositioning(config.resizeChild.getPositioning());
8492         config.resizeChild.clearPositioning();
8493         if(!config.width || !config.height){
8494             var csize = config.resizeChild.getSize();
8495             this.el.setSize(csize.width, csize.height);
8496         }
8497         if(config.pinned && !config.adjustments){
8498             config.adjustments = "auto";
8499         }
8500     }
8501
8502     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8503     this.proxy.unselectable();
8504     this.proxy.enableDisplayMode('block');
8505
8506     Roo.apply(this, config);
8507
8508     if(this.pinned){
8509         this.disableTrackOver = true;
8510         this.el.addClass("x-resizable-pinned");
8511     }
8512     // if the element isn't positioned, make it relative
8513     var position = this.el.getStyle("position");
8514     if(position != "absolute" && position != "fixed"){
8515         this.el.setStyle("position", "relative");
8516     }
8517     if(!this.handles){ // no handles passed, must be legacy style
8518         this.handles = 's,e,se';
8519         if(this.multiDirectional){
8520             this.handles += ',n,w';
8521         }
8522     }
8523     if(this.handles == "all"){
8524         this.handles = "n s e w ne nw se sw";
8525     }
8526     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8527     var ps = Roo.Resizable.positions;
8528     for(var i = 0, len = hs.length; i < len; i++){
8529         if(hs[i] && ps[hs[i]]){
8530             var pos = ps[hs[i]];
8531             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8532         }
8533     }
8534     // legacy
8535     this.corner = this.southeast;
8536     
8537     // updateBox = the box can move..
8538     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8539         this.updateBox = true;
8540     }
8541
8542     this.activeHandle = null;
8543
8544     if(this.resizeChild){
8545         if(typeof this.resizeChild == "boolean"){
8546             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8547         }else{
8548             this.resizeChild = Roo.get(this.resizeChild, true);
8549         }
8550     }
8551     
8552     if(this.adjustments == "auto"){
8553         var rc = this.resizeChild;
8554         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8555         if(rc && (hw || hn)){
8556             rc.position("relative");
8557             rc.setLeft(hw ? hw.el.getWidth() : 0);
8558             rc.setTop(hn ? hn.el.getHeight() : 0);
8559         }
8560         this.adjustments = [
8561             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8562             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8563         ];
8564     }
8565
8566     if(this.draggable){
8567         this.dd = this.dynamic ?
8568             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8569         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8570     }
8571
8572     // public events
8573     this.addEvents({
8574         /**
8575          * @event beforeresize
8576          * Fired before resize is allowed. Set enabled to false to cancel resize.
8577          * @param {Roo.Resizable} this
8578          * @param {Roo.EventObject} e The mousedown event
8579          */
8580         "beforeresize" : true,
8581         /**
8582          * @event resizing
8583          * Fired a resizing.
8584          * @param {Roo.Resizable} this
8585          * @param {Number} x The new x position
8586          * @param {Number} y The new y position
8587          * @param {Number} w The new w width
8588          * @param {Number} h The new h hight
8589          * @param {Roo.EventObject} e The mouseup event
8590          */
8591         "resizing" : true,
8592         /**
8593          * @event resize
8594          * Fired after a resize.
8595          * @param {Roo.Resizable} this
8596          * @param {Number} width The new width
8597          * @param {Number} height The new height
8598          * @param {Roo.EventObject} e The mouseup event
8599          */
8600         "resize" : true
8601     });
8602
8603     if(this.width !== null && this.height !== null){
8604         this.resizeTo(this.width, this.height);
8605     }else{
8606         this.updateChildSize();
8607     }
8608     if(Roo.isIE){
8609         this.el.dom.style.zoom = 1;
8610     }
8611     Roo.Resizable.superclass.constructor.call(this);
8612 };
8613
8614 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8615         resizeChild : false,
8616         adjustments : [0, 0],
8617         minWidth : 5,
8618         minHeight : 5,
8619         maxWidth : 10000,
8620         maxHeight : 10000,
8621         enabled : true,
8622         animate : false,
8623         duration : .35,
8624         dynamic : false,
8625         handles : false,
8626         multiDirectional : false,
8627         disableTrackOver : false,
8628         easing : 'easeOutStrong',
8629         widthIncrement : 0,
8630         heightIncrement : 0,
8631         pinned : false,
8632         width : null,
8633         height : null,
8634         preserveRatio : false,
8635         transparent: false,
8636         minX: 0,
8637         minY: 0,
8638         draggable: false,
8639
8640         /**
8641          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8642          */
8643         constrainTo: undefined,
8644         /**
8645          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8646          */
8647         resizeRegion: undefined,
8648
8649
8650     /**
8651      * Perform a manual resize
8652      * @param {Number} width
8653      * @param {Number} height
8654      */
8655     resizeTo : function(width, height){
8656         this.el.setSize(width, height);
8657         this.updateChildSize();
8658         this.fireEvent("resize", this, width, height, null);
8659     },
8660
8661     // private
8662     startSizing : function(e, handle){
8663         this.fireEvent("beforeresize", this, e);
8664         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8665
8666             if(!this.overlay){
8667                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8668                 this.overlay.unselectable();
8669                 this.overlay.enableDisplayMode("block");
8670                 this.overlay.on("mousemove", this.onMouseMove, this);
8671                 this.overlay.on("mouseup", this.onMouseUp, this);
8672             }
8673             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8674
8675             this.resizing = true;
8676             this.startBox = this.el.getBox();
8677             this.startPoint = e.getXY();
8678             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8679                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8680
8681             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8682             this.overlay.show();
8683
8684             if(this.constrainTo) {
8685                 var ct = Roo.get(this.constrainTo);
8686                 this.resizeRegion = ct.getRegion().adjust(
8687                     ct.getFrameWidth('t'),
8688                     ct.getFrameWidth('l'),
8689                     -ct.getFrameWidth('b'),
8690                     -ct.getFrameWidth('r')
8691                 );
8692             }
8693
8694             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8695             this.proxy.show();
8696             this.proxy.setBox(this.startBox);
8697             if(!this.dynamic){
8698                 this.proxy.setStyle('visibility', 'visible');
8699             }
8700         }
8701     },
8702
8703     // private
8704     onMouseDown : function(handle, e){
8705         if(this.enabled){
8706             e.stopEvent();
8707             this.activeHandle = handle;
8708             this.startSizing(e, handle);
8709         }
8710     },
8711
8712     // private
8713     onMouseUp : function(e){
8714         var size = this.resizeElement();
8715         this.resizing = false;
8716         this.handleOut();
8717         this.overlay.hide();
8718         this.proxy.hide();
8719         this.fireEvent("resize", this, size.width, size.height, e);
8720     },
8721
8722     // private
8723     updateChildSize : function(){
8724         
8725         if(this.resizeChild){
8726             var el = this.el;
8727             var child = this.resizeChild;
8728             var adj = this.adjustments;
8729             if(el.dom.offsetWidth){
8730                 var b = el.getSize(true);
8731                 child.setSize(b.width+adj[0], b.height+adj[1]);
8732             }
8733             // Second call here for IE
8734             // The first call enables instant resizing and
8735             // the second call corrects scroll bars if they
8736             // exist
8737             if(Roo.isIE){
8738                 setTimeout(function(){
8739                     if(el.dom.offsetWidth){
8740                         var b = el.getSize(true);
8741                         child.setSize(b.width+adj[0], b.height+adj[1]);
8742                     }
8743                 }, 10);
8744             }
8745         }
8746     },
8747
8748     // private
8749     snap : function(value, inc, min){
8750         if(!inc || !value) {
8751             return value;
8752         }
8753         var newValue = value;
8754         var m = value % inc;
8755         if(m > 0){
8756             if(m > (inc/2)){
8757                 newValue = value + (inc-m);
8758             }else{
8759                 newValue = value - m;
8760             }
8761         }
8762         return Math.max(min, newValue);
8763     },
8764
8765     // private
8766     resizeElement : function(){
8767         var box = this.proxy.getBox();
8768         if(this.updateBox){
8769             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8770         }else{
8771             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8772         }
8773         this.updateChildSize();
8774         if(!this.dynamic){
8775             this.proxy.hide();
8776         }
8777         return box;
8778     },
8779
8780     // private
8781     constrain : function(v, diff, m, mx){
8782         if(v - diff < m){
8783             diff = v - m;
8784         }else if(v - diff > mx){
8785             diff = mx - v;
8786         }
8787         return diff;
8788     },
8789
8790     // private
8791     onMouseMove : function(e){
8792         
8793         if(this.enabled){
8794             try{// try catch so if something goes wrong the user doesn't get hung
8795
8796             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8797                 return;
8798             }
8799
8800             //var curXY = this.startPoint;
8801             var curSize = this.curSize || this.startBox;
8802             var x = this.startBox.x, y = this.startBox.y;
8803             var ox = x, oy = y;
8804             var w = curSize.width, h = curSize.height;
8805             var ow = w, oh = h;
8806             var mw = this.minWidth, mh = this.minHeight;
8807             var mxw = this.maxWidth, mxh = this.maxHeight;
8808             var wi = this.widthIncrement;
8809             var hi = this.heightIncrement;
8810
8811             var eventXY = e.getXY();
8812             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8813             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8814
8815             var pos = this.activeHandle.position;
8816
8817             switch(pos){
8818                 case "east":
8819                     w += diffX;
8820                     w = Math.min(Math.max(mw, w), mxw);
8821                     break;
8822              
8823                 case "south":
8824                     h += diffY;
8825                     h = Math.min(Math.max(mh, h), mxh);
8826                     break;
8827                 case "southeast":
8828                     w += diffX;
8829                     h += diffY;
8830                     w = Math.min(Math.max(mw, w), mxw);
8831                     h = Math.min(Math.max(mh, h), mxh);
8832                     break;
8833                 case "north":
8834                     diffY = this.constrain(h, diffY, mh, mxh);
8835                     y += diffY;
8836                     h -= diffY;
8837                     break;
8838                 case "hdrag":
8839                     
8840                     if (wi) {
8841                         var adiffX = Math.abs(diffX);
8842                         var sub = (adiffX % wi); // how much 
8843                         if (sub > (wi/2)) { // far enough to snap
8844                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8845                         } else {
8846                             // remove difference.. 
8847                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8848                         }
8849                     }
8850                     x += diffX;
8851                     x = Math.max(this.minX, x);
8852                     break;
8853                 case "west":
8854                     diffX = this.constrain(w, diffX, mw, mxw);
8855                     x += diffX;
8856                     w -= diffX;
8857                     break;
8858                 case "northeast":
8859                     w += diffX;
8860                     w = Math.min(Math.max(mw, w), mxw);
8861                     diffY = this.constrain(h, diffY, mh, mxh);
8862                     y += diffY;
8863                     h -= diffY;
8864                     break;
8865                 case "northwest":
8866                     diffX = this.constrain(w, diffX, mw, mxw);
8867                     diffY = this.constrain(h, diffY, mh, mxh);
8868                     y += diffY;
8869                     h -= diffY;
8870                     x += diffX;
8871                     w -= diffX;
8872                     break;
8873                case "southwest":
8874                     diffX = this.constrain(w, diffX, mw, mxw);
8875                     h += diffY;
8876                     h = Math.min(Math.max(mh, h), mxh);
8877                     x += diffX;
8878                     w -= diffX;
8879                     break;
8880             }
8881
8882             var sw = this.snap(w, wi, mw);
8883             var sh = this.snap(h, hi, mh);
8884             if(sw != w || sh != h){
8885                 switch(pos){
8886                     case "northeast":
8887                         y -= sh - h;
8888                     break;
8889                     case "north":
8890                         y -= sh - h;
8891                         break;
8892                     case "southwest":
8893                         x -= sw - w;
8894                     break;
8895                     case "west":
8896                         x -= sw - w;
8897                         break;
8898                     case "northwest":
8899                         x -= sw - w;
8900                         y -= sh - h;
8901                     break;
8902                 }
8903                 w = sw;
8904                 h = sh;
8905             }
8906
8907             if(this.preserveRatio){
8908                 switch(pos){
8909                     case "southeast":
8910                     case "east":
8911                         h = oh * (w/ow);
8912                         h = Math.min(Math.max(mh, h), mxh);
8913                         w = ow * (h/oh);
8914                        break;
8915                     case "south":
8916                         w = ow * (h/oh);
8917                         w = Math.min(Math.max(mw, w), mxw);
8918                         h = oh * (w/ow);
8919                         break;
8920                     case "northeast":
8921                         w = ow * (h/oh);
8922                         w = Math.min(Math.max(mw, w), mxw);
8923                         h = oh * (w/ow);
8924                     break;
8925                     case "north":
8926                         var tw = w;
8927                         w = ow * (h/oh);
8928                         w = Math.min(Math.max(mw, w), mxw);
8929                         h = oh * (w/ow);
8930                         x += (tw - w) / 2;
8931                         break;
8932                     case "southwest":
8933                         h = oh * (w/ow);
8934                         h = Math.min(Math.max(mh, h), mxh);
8935                         var tw = w;
8936                         w = ow * (h/oh);
8937                         x += tw - w;
8938                         break;
8939                     case "west":
8940                         var th = h;
8941                         h = oh * (w/ow);
8942                         h = Math.min(Math.max(mh, h), mxh);
8943                         y += (th - h) / 2;
8944                         var tw = w;
8945                         w = ow * (h/oh);
8946                         x += tw - w;
8947                        break;
8948                     case "northwest":
8949                         var tw = w;
8950                         var th = h;
8951                         h = oh * (w/ow);
8952                         h = Math.min(Math.max(mh, h), mxh);
8953                         w = ow * (h/oh);
8954                         y += th - h;
8955                         x += tw - w;
8956                        break;
8957
8958                 }
8959             }
8960             if (pos == 'hdrag') {
8961                 w = ow;
8962             }
8963             this.proxy.setBounds(x, y, w, h);
8964             if(this.dynamic){
8965                 this.resizeElement();
8966             }
8967             }catch(e){}
8968         }
8969         this.fireEvent("resizing", this, x, y, w, h, e);
8970     },
8971
8972     // private
8973     handleOver : function(){
8974         if(this.enabled){
8975             this.el.addClass("x-resizable-over");
8976         }
8977     },
8978
8979     // private
8980     handleOut : function(){
8981         if(!this.resizing){
8982             this.el.removeClass("x-resizable-over");
8983         }
8984     },
8985
8986     /**
8987      * Returns the element this component is bound to.
8988      * @return {Roo.Element}
8989      */
8990     getEl : function(){
8991         return this.el;
8992     },
8993
8994     /**
8995      * Returns the resizeChild element (or null).
8996      * @return {Roo.Element}
8997      */
8998     getResizeChild : function(){
8999         return this.resizeChild;
9000     },
9001     groupHandler : function()
9002     {
9003         
9004     },
9005     /**
9006      * Destroys this resizable. If the element was wrapped and
9007      * removeEl is not true then the element remains.
9008      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9009      */
9010     destroy : function(removeEl){
9011         this.proxy.remove();
9012         if(this.overlay){
9013             this.overlay.removeAllListeners();
9014             this.overlay.remove();
9015         }
9016         var ps = Roo.Resizable.positions;
9017         for(var k in ps){
9018             if(typeof ps[k] != "function" && this[ps[k]]){
9019                 var h = this[ps[k]];
9020                 h.el.removeAllListeners();
9021                 h.el.remove();
9022             }
9023         }
9024         if(removeEl){
9025             this.el.update("");
9026             this.el.remove();
9027         }
9028     }
9029 });
9030
9031 // private
9032 // hash to map config positions to true positions
9033 Roo.Resizable.positions = {
9034     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9035     hd: "hdrag"
9036 };
9037
9038 // private
9039 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9040     if(!this.tpl){
9041         // only initialize the template if resizable is used
9042         var tpl = Roo.DomHelper.createTemplate(
9043             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9044         );
9045         tpl.compile();
9046         Roo.Resizable.Handle.prototype.tpl = tpl;
9047     }
9048     this.position = pos;
9049     this.rz = rz;
9050     // show north drag fro topdra
9051     var handlepos = pos == 'hdrag' ? 'north' : pos;
9052     
9053     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9054     if (pos == 'hdrag') {
9055         this.el.setStyle('cursor', 'pointer');
9056     }
9057     this.el.unselectable();
9058     if(transparent){
9059         this.el.setOpacity(0);
9060     }
9061     this.el.on("mousedown", this.onMouseDown, this);
9062     if(!disableTrackOver){
9063         this.el.on("mouseover", this.onMouseOver, this);
9064         this.el.on("mouseout", this.onMouseOut, this);
9065     }
9066 };
9067
9068 // private
9069 Roo.Resizable.Handle.prototype = {
9070     afterResize : function(rz){
9071         Roo.log('after?');
9072         // do nothing
9073     },
9074     // private
9075     onMouseDown : function(e){
9076         this.rz.onMouseDown(this, e);
9077     },
9078     // private
9079     onMouseOver : function(e){
9080         this.rz.handleOver(this, e);
9081     },
9082     // private
9083     onMouseOut : function(e){
9084         this.rz.handleOut(this, e);
9085     }
9086 };/*
9087  * Based on:
9088  * Ext JS Library 1.1.1
9089  * Copyright(c) 2006-2007, Ext JS, LLC.
9090  *
9091  * Originally Released Under LGPL - original licence link has changed is not relivant.
9092  *
9093  * Fork - LGPL
9094  * <script type="text/javascript">
9095  */
9096
9097 /**
9098  * @class Roo.Editor
9099  * @extends Roo.Component
9100  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9101  * @constructor
9102  * Create a new Editor
9103  * @param {Roo.form.Field} field The Field object (or descendant)
9104  * @param {Object} config The config object
9105  */
9106 Roo.Editor = function(field, config){
9107     Roo.Editor.superclass.constructor.call(this, config);
9108     this.field = field;
9109     this.addEvents({
9110         /**
9111              * @event beforestartedit
9112              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9113              * false from the handler of this event.
9114              * @param {Editor} this
9115              * @param {Roo.Element} boundEl The underlying element bound to this editor
9116              * @param {Mixed} value The field value being set
9117              */
9118         "beforestartedit" : true,
9119         /**
9120              * @event startedit
9121              * Fires when this editor is displayed
9122              * @param {Roo.Element} boundEl The underlying element bound to this editor
9123              * @param {Mixed} value The starting field value
9124              */
9125         "startedit" : true,
9126         /**
9127              * @event beforecomplete
9128              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9129              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9130              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9131              * event will not fire since no edit actually occurred.
9132              * @param {Editor} this
9133              * @param {Mixed} value The current field value
9134              * @param {Mixed} startValue The original field value
9135              */
9136         "beforecomplete" : true,
9137         /**
9138              * @event complete
9139              * Fires after editing is complete and any changed value has been written to the underlying field.
9140              * @param {Editor} this
9141              * @param {Mixed} value The current field value
9142              * @param {Mixed} startValue The original field value
9143              */
9144         "complete" : true,
9145         /**
9146          * @event specialkey
9147          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9148          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9149          * @param {Roo.form.Field} this
9150          * @param {Roo.EventObject} e The event object
9151          */
9152         "specialkey" : true
9153     });
9154 };
9155
9156 Roo.extend(Roo.Editor, Roo.Component, {
9157     /**
9158      * @cfg {Boolean/String} autosize
9159      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9160      * or "height" to adopt the height only (defaults to false)
9161      */
9162     /**
9163      * @cfg {Boolean} revertInvalid
9164      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9165      * validation fails (defaults to true)
9166      */
9167     /**
9168      * @cfg {Boolean} ignoreNoChange
9169      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9170      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9171      * will never be ignored.
9172      */
9173     /**
9174      * @cfg {Boolean} hideEl
9175      * False to keep the bound element visible while the editor is displayed (defaults to true)
9176      */
9177     /**
9178      * @cfg {Mixed} value
9179      * The data value of the underlying field (defaults to "")
9180      */
9181     value : "",
9182     /**
9183      * @cfg {String} alignment
9184      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9185      */
9186     alignment: "c-c?",
9187     /**
9188      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9189      * for bottom-right shadow (defaults to "frame")
9190      */
9191     shadow : "frame",
9192     /**
9193      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9194      */
9195     constrain : false,
9196     /**
9197      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9198      */
9199     completeOnEnter : false,
9200     /**
9201      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9202      */
9203     cancelOnEsc : false,
9204     /**
9205      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9206      */
9207     updateEl : false,
9208
9209     // private
9210     onRender : function(ct, position){
9211         this.el = new Roo.Layer({
9212             shadow: this.shadow,
9213             cls: "x-editor",
9214             parentEl : ct,
9215             shim : this.shim,
9216             shadowOffset:4,
9217             id: this.id,
9218             constrain: this.constrain
9219         });
9220         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9221         if(this.field.msgTarget != 'title'){
9222             this.field.msgTarget = 'qtip';
9223         }
9224         this.field.render(this.el);
9225         if(Roo.isGecko){
9226             this.field.el.dom.setAttribute('autocomplete', 'off');
9227         }
9228         this.field.on("specialkey", this.onSpecialKey, this);
9229         if(this.swallowKeys){
9230             this.field.el.swallowEvent(['keydown','keypress']);
9231         }
9232         this.field.show();
9233         this.field.on("blur", this.onBlur, this);
9234         if(this.field.grow){
9235             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9236         }
9237     },
9238
9239     onSpecialKey : function(field, e)
9240     {
9241         //Roo.log('editor onSpecialKey');
9242         if(this.completeOnEnter && e.getKey() == e.ENTER){
9243             e.stopEvent();
9244             this.completeEdit();
9245             return;
9246         }
9247         // do not fire special key otherwise it might hide close the editor...
9248         if(e.getKey() == e.ENTER){    
9249             return;
9250         }
9251         if(this.cancelOnEsc && e.getKey() == e.ESC){
9252             this.cancelEdit();
9253             return;
9254         } 
9255         this.fireEvent('specialkey', field, e);
9256     
9257     },
9258
9259     /**
9260      * Starts the editing process and shows the editor.
9261      * @param {String/HTMLElement/Element} el The element to edit
9262      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9263       * to the innerHTML of el.
9264      */
9265     startEdit : function(el, value){
9266         if(this.editing){
9267             this.completeEdit();
9268         }
9269         this.boundEl = Roo.get(el);
9270         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9271         if(!this.rendered){
9272             this.render(this.parentEl || document.body);
9273         }
9274         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9275             return;
9276         }
9277         this.startValue = v;
9278         this.field.setValue(v);
9279         if(this.autoSize){
9280             var sz = this.boundEl.getSize();
9281             switch(this.autoSize){
9282                 case "width":
9283                 this.setSize(sz.width,  "");
9284                 break;
9285                 case "height":
9286                 this.setSize("",  sz.height);
9287                 break;
9288                 default:
9289                 this.setSize(sz.width,  sz.height);
9290             }
9291         }
9292         this.el.alignTo(this.boundEl, this.alignment);
9293         this.editing = true;
9294         if(Roo.QuickTips){
9295             Roo.QuickTips.disable();
9296         }
9297         this.show();
9298     },
9299
9300     /**
9301      * Sets the height and width of this editor.
9302      * @param {Number} width The new width
9303      * @param {Number} height The new height
9304      */
9305     setSize : function(w, h){
9306         this.field.setSize(w, h);
9307         if(this.el){
9308             this.el.sync();
9309         }
9310     },
9311
9312     /**
9313      * Realigns the editor to the bound field based on the current alignment config value.
9314      */
9315     realign : function(){
9316         this.el.alignTo(this.boundEl, this.alignment);
9317     },
9318
9319     /**
9320      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9321      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9322      */
9323     completeEdit : function(remainVisible){
9324         if(!this.editing){
9325             return;
9326         }
9327         var v = this.getValue();
9328         if(this.revertInvalid !== false && !this.field.isValid()){
9329             v = this.startValue;
9330             this.cancelEdit(true);
9331         }
9332         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9333             this.editing = false;
9334             this.hide();
9335             return;
9336         }
9337         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9338             this.editing = false;
9339             if(this.updateEl && this.boundEl){
9340                 this.boundEl.update(v);
9341             }
9342             if(remainVisible !== true){
9343                 this.hide();
9344             }
9345             this.fireEvent("complete", this, v, this.startValue);
9346         }
9347     },
9348
9349     // private
9350     onShow : function(){
9351         this.el.show();
9352         if(this.hideEl !== false){
9353             this.boundEl.hide();
9354         }
9355         this.field.show();
9356         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9357             this.fixIEFocus = true;
9358             this.deferredFocus.defer(50, this);
9359         }else{
9360             this.field.focus();
9361         }
9362         this.fireEvent("startedit", this.boundEl, this.startValue);
9363     },
9364
9365     deferredFocus : function(){
9366         if(this.editing){
9367             this.field.focus();
9368         }
9369     },
9370
9371     /**
9372      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9373      * reverted to the original starting value.
9374      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9375      * cancel (defaults to false)
9376      */
9377     cancelEdit : function(remainVisible){
9378         if(this.editing){
9379             this.setValue(this.startValue);
9380             if(remainVisible !== true){
9381                 this.hide();
9382             }
9383         }
9384     },
9385
9386     // private
9387     onBlur : function(){
9388         if(this.allowBlur !== true && this.editing){
9389             this.completeEdit();
9390         }
9391     },
9392
9393     // private
9394     onHide : function(){
9395         if(this.editing){
9396             this.completeEdit();
9397             return;
9398         }
9399         this.field.blur();
9400         if(this.field.collapse){
9401             this.field.collapse();
9402         }
9403         this.el.hide();
9404         if(this.hideEl !== false){
9405             this.boundEl.show();
9406         }
9407         if(Roo.QuickTips){
9408             Roo.QuickTips.enable();
9409         }
9410     },
9411
9412     /**
9413      * Sets the data value of the editor
9414      * @param {Mixed} value Any valid value supported by the underlying field
9415      */
9416     setValue : function(v){
9417         this.field.setValue(v);
9418     },
9419
9420     /**
9421      * Gets the data value of the editor
9422      * @return {Mixed} The data value
9423      */
9424     getValue : function(){
9425         return this.field.getValue();
9426     }
9427 });/*
9428  * Based on:
9429  * Ext JS Library 1.1.1
9430  * Copyright(c) 2006-2007, Ext JS, LLC.
9431  *
9432  * Originally Released Under LGPL - original licence link has changed is not relivant.
9433  *
9434  * Fork - LGPL
9435  * <script type="text/javascript">
9436  */
9437  
9438 /**
9439  * @class Roo.BasicDialog
9440  * @extends Roo.util.Observable
9441  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9442  * <pre><code>
9443 var dlg = new Roo.BasicDialog("my-dlg", {
9444     height: 200,
9445     width: 300,
9446     minHeight: 100,
9447     minWidth: 150,
9448     modal: true,
9449     proxyDrag: true,
9450     shadow: true
9451 });
9452 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9453 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9454 dlg.addButton('Cancel', dlg.hide, dlg);
9455 dlg.show();
9456 </code></pre>
9457   <b>A Dialog should always be a direct child of the body element.</b>
9458  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9459  * @cfg {String} title Default text to display in the title bar (defaults to null)
9460  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9461  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9462  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9463  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9464  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9465  * (defaults to null with no animation)
9466  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9467  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9468  * property for valid values (defaults to 'all')
9469  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9470  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9471  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9472  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9473  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9474  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9475  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9476  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9477  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9478  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9479  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9480  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9481  * draggable = true (defaults to false)
9482  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9483  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9484  * shadow (defaults to false)
9485  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9486  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9487  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9488  * @cfg {Array} buttons Array of buttons
9489  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9490  * @constructor
9491  * Create a new BasicDialog.
9492  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9493  * @param {Object} config Configuration options
9494  */
9495 Roo.BasicDialog = function(el, config){
9496     this.el = Roo.get(el);
9497     var dh = Roo.DomHelper;
9498     if(!this.el && config && config.autoCreate){
9499         if(typeof config.autoCreate == "object"){
9500             if(!config.autoCreate.id){
9501                 config.autoCreate.id = el;
9502             }
9503             this.el = dh.append(document.body,
9504                         config.autoCreate, true);
9505         }else{
9506             this.el = dh.append(document.body,
9507                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9508         }
9509     }
9510     el = this.el;
9511     el.setDisplayed(true);
9512     el.hide = this.hideAction;
9513     this.id = el.id;
9514     el.addClass("x-dlg");
9515
9516     Roo.apply(this, config);
9517
9518     this.proxy = el.createProxy("x-dlg-proxy");
9519     this.proxy.hide = this.hideAction;
9520     this.proxy.setOpacity(.5);
9521     this.proxy.hide();
9522
9523     if(config.width){
9524         el.setWidth(config.width);
9525     }
9526     if(config.height){
9527         el.setHeight(config.height);
9528     }
9529     this.size = el.getSize();
9530     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9531         this.xy = [config.x,config.y];
9532     }else{
9533         this.xy = el.getCenterXY(true);
9534     }
9535     /** The header element @type Roo.Element */
9536     this.header = el.child("> .x-dlg-hd");
9537     /** The body element @type Roo.Element */
9538     this.body = el.child("> .x-dlg-bd");
9539     /** The footer element @type Roo.Element */
9540     this.footer = el.child("> .x-dlg-ft");
9541
9542     if(!this.header){
9543         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9544     }
9545     if(!this.body){
9546         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9547     }
9548
9549     this.header.unselectable();
9550     if(this.title){
9551         this.header.update(this.title);
9552     }
9553     // this element allows the dialog to be focused for keyboard event
9554     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9555     this.focusEl.swallowEvent("click", true);
9556
9557     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9558
9559     // wrap the body and footer for special rendering
9560     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9561     if(this.footer){
9562         this.bwrap.dom.appendChild(this.footer.dom);
9563     }
9564
9565     this.bg = this.el.createChild({
9566         tag: "div", cls:"x-dlg-bg",
9567         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9568     });
9569     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9570
9571
9572     if(this.autoScroll !== false && !this.autoTabs){
9573         this.body.setStyle("overflow", "auto");
9574     }
9575
9576     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9577
9578     if(this.closable !== false){
9579         this.el.addClass("x-dlg-closable");
9580         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9581         this.close.on("click", this.closeClick, this);
9582         this.close.addClassOnOver("x-dlg-close-over");
9583     }
9584     if(this.collapsible !== false){
9585         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9586         this.collapseBtn.on("click", this.collapseClick, this);
9587         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9588         this.header.on("dblclick", this.collapseClick, this);
9589     }
9590     if(this.resizable !== false){
9591         this.el.addClass("x-dlg-resizable");
9592         this.resizer = new Roo.Resizable(el, {
9593             minWidth: this.minWidth || 80,
9594             minHeight:this.minHeight || 80,
9595             handles: this.resizeHandles || "all",
9596             pinned: true
9597         });
9598         this.resizer.on("beforeresize", this.beforeResize, this);
9599         this.resizer.on("resize", this.onResize, this);
9600     }
9601     if(this.draggable !== false){
9602         el.addClass("x-dlg-draggable");
9603         if (!this.proxyDrag) {
9604             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9605         }
9606         else {
9607             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9608         }
9609         dd.setHandleElId(this.header.id);
9610         dd.endDrag = this.endMove.createDelegate(this);
9611         dd.startDrag = this.startMove.createDelegate(this);
9612         dd.onDrag = this.onDrag.createDelegate(this);
9613         dd.scroll = false;
9614         this.dd = dd;
9615     }
9616     if(this.modal){
9617         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9618         this.mask.enableDisplayMode("block");
9619         this.mask.hide();
9620         this.el.addClass("x-dlg-modal");
9621     }
9622     if(this.shadow){
9623         this.shadow = new Roo.Shadow({
9624             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9625             offset : this.shadowOffset
9626         });
9627     }else{
9628         this.shadowOffset = 0;
9629     }
9630     if(Roo.useShims && this.shim !== false){
9631         this.shim = this.el.createShim();
9632         this.shim.hide = this.hideAction;
9633         this.shim.hide();
9634     }else{
9635         this.shim = false;
9636     }
9637     if(this.autoTabs){
9638         this.initTabs();
9639     }
9640     if (this.buttons) { 
9641         var bts= this.buttons;
9642         this.buttons = [];
9643         Roo.each(bts, function(b) {
9644             this.addButton(b);
9645         }, this);
9646     }
9647     
9648     
9649     this.addEvents({
9650         /**
9651          * @event keydown
9652          * Fires when a key is pressed
9653          * @param {Roo.BasicDialog} this
9654          * @param {Roo.EventObject} e
9655          */
9656         "keydown" : true,
9657         /**
9658          * @event move
9659          * Fires when this dialog is moved by the user.
9660          * @param {Roo.BasicDialog} this
9661          * @param {Number} x The new page X
9662          * @param {Number} y The new page Y
9663          */
9664         "move" : true,
9665         /**
9666          * @event resize
9667          * Fires when this dialog is resized by the user.
9668          * @param {Roo.BasicDialog} this
9669          * @param {Number} width The new width
9670          * @param {Number} height The new height
9671          */
9672         "resize" : true,
9673         /**
9674          * @event beforehide
9675          * Fires before this dialog is hidden.
9676          * @param {Roo.BasicDialog} this
9677          */
9678         "beforehide" : true,
9679         /**
9680          * @event hide
9681          * Fires when this dialog is hidden.
9682          * @param {Roo.BasicDialog} this
9683          */
9684         "hide" : true,
9685         /**
9686          * @event beforeshow
9687          * Fires before this dialog is shown.
9688          * @param {Roo.BasicDialog} this
9689          */
9690         "beforeshow" : true,
9691         /**
9692          * @event show
9693          * Fires when this dialog is shown.
9694          * @param {Roo.BasicDialog} this
9695          */
9696         "show" : true
9697     });
9698     el.on("keydown", this.onKeyDown, this);
9699     el.on("mousedown", this.toFront, this);
9700     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9701     this.el.hide();
9702     Roo.DialogManager.register(this);
9703     Roo.BasicDialog.superclass.constructor.call(this);
9704 };
9705
9706 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9707     shadowOffset: Roo.isIE ? 6 : 5,
9708     minHeight: 80,
9709     minWidth: 200,
9710     minButtonWidth: 75,
9711     defaultButton: null,
9712     buttonAlign: "right",
9713     tabTag: 'div',
9714     firstShow: true,
9715
9716     /**
9717      * Sets the dialog title text
9718      * @param {String} text The title text to display
9719      * @return {Roo.BasicDialog} this
9720      */
9721     setTitle : function(text){
9722         this.header.update(text);
9723         return this;
9724     },
9725
9726     // private
9727     closeClick : function(){
9728         this.hide();
9729     },
9730
9731     // private
9732     collapseClick : function(){
9733         this[this.collapsed ? "expand" : "collapse"]();
9734     },
9735
9736     /**
9737      * Collapses the dialog to its minimized state (only the title bar is visible).
9738      * Equivalent to the user clicking the collapse dialog button.
9739      */
9740     collapse : function(){
9741         if(!this.collapsed){
9742             this.collapsed = true;
9743             this.el.addClass("x-dlg-collapsed");
9744             this.restoreHeight = this.el.getHeight();
9745             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9746         }
9747     },
9748
9749     /**
9750      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9751      * clicking the expand dialog button.
9752      */
9753     expand : function(){
9754         if(this.collapsed){
9755             this.collapsed = false;
9756             this.el.removeClass("x-dlg-collapsed");
9757             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9758         }
9759     },
9760
9761     /**
9762      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9763      * @return {Roo.TabPanel} The tabs component
9764      */
9765     initTabs : function(){
9766         var tabs = this.getTabs();
9767         while(tabs.getTab(0)){
9768             tabs.removeTab(0);
9769         }
9770         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9771             var dom = el.dom;
9772             tabs.addTab(Roo.id(dom), dom.title);
9773             dom.title = "";
9774         });
9775         tabs.activate(0);
9776         return tabs;
9777     },
9778
9779     // private
9780     beforeResize : function(){
9781         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9782     },
9783
9784     // private
9785     onResize : function(){
9786         this.refreshSize();
9787         this.syncBodyHeight();
9788         this.adjustAssets();
9789         this.focus();
9790         this.fireEvent("resize", this, this.size.width, this.size.height);
9791     },
9792
9793     // private
9794     onKeyDown : function(e){
9795         if(this.isVisible()){
9796             this.fireEvent("keydown", this, e);
9797         }
9798     },
9799
9800     /**
9801      * Resizes the dialog.
9802      * @param {Number} width
9803      * @param {Number} height
9804      * @return {Roo.BasicDialog} this
9805      */
9806     resizeTo : function(width, height){
9807         this.el.setSize(width, height);
9808         this.size = {width: width, height: height};
9809         this.syncBodyHeight();
9810         if(this.fixedcenter){
9811             this.center();
9812         }
9813         if(this.isVisible()){
9814             this.constrainXY();
9815             this.adjustAssets();
9816         }
9817         this.fireEvent("resize", this, width, height);
9818         return this;
9819     },
9820
9821
9822     /**
9823      * Resizes the dialog to fit the specified content size.
9824      * @param {Number} width
9825      * @param {Number} height
9826      * @return {Roo.BasicDialog} this
9827      */
9828     setContentSize : function(w, h){
9829         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9830         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9831         //if(!this.el.isBorderBox()){
9832             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9833             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9834         //}
9835         if(this.tabs){
9836             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9837             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9838         }
9839         this.resizeTo(w, h);
9840         return this;
9841     },
9842
9843     /**
9844      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9845      * executed in response to a particular key being pressed while the dialog is active.
9846      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9847      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9848      * @param {Function} fn The function to call
9849      * @param {Object} scope (optional) The scope of the function
9850      * @return {Roo.BasicDialog} this
9851      */
9852     addKeyListener : function(key, fn, scope){
9853         var keyCode, shift, ctrl, alt;
9854         if(typeof key == "object" && !(key instanceof Array)){
9855             keyCode = key["key"];
9856             shift = key["shift"];
9857             ctrl = key["ctrl"];
9858             alt = key["alt"];
9859         }else{
9860             keyCode = key;
9861         }
9862         var handler = function(dlg, e){
9863             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9864                 var k = e.getKey();
9865                 if(keyCode instanceof Array){
9866                     for(var i = 0, len = keyCode.length; i < len; i++){
9867                         if(keyCode[i] == k){
9868                           fn.call(scope || window, dlg, k, e);
9869                           return;
9870                         }
9871                     }
9872                 }else{
9873                     if(k == keyCode){
9874                         fn.call(scope || window, dlg, k, e);
9875                     }
9876                 }
9877             }
9878         };
9879         this.on("keydown", handler);
9880         return this;
9881     },
9882
9883     /**
9884      * Returns the TabPanel component (creates it if it doesn't exist).
9885      * Note: If you wish to simply check for the existence of tabs without creating them,
9886      * check for a null 'tabs' property.
9887      * @return {Roo.TabPanel} The tabs component
9888      */
9889     getTabs : function(){
9890         if(!this.tabs){
9891             this.el.addClass("x-dlg-auto-tabs");
9892             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9893             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9894         }
9895         return this.tabs;
9896     },
9897
9898     /**
9899      * Adds a button to the footer section of the dialog.
9900      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9901      * object or a valid Roo.DomHelper element config
9902      * @param {Function} handler The function called when the button is clicked
9903      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9904      * @return {Roo.Button} The new button
9905      */
9906     addButton : function(config, handler, scope){
9907         var dh = Roo.DomHelper;
9908         if(!this.footer){
9909             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9910         }
9911         if(!this.btnContainer){
9912             var tb = this.footer.createChild({
9913
9914                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9915                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9916             }, null, true);
9917             this.btnContainer = tb.firstChild.firstChild.firstChild;
9918         }
9919         var bconfig = {
9920             handler: handler,
9921             scope: scope,
9922             minWidth: this.minButtonWidth,
9923             hideParent:true
9924         };
9925         if(typeof config == "string"){
9926             bconfig.text = config;
9927         }else{
9928             if(config.tag){
9929                 bconfig.dhconfig = config;
9930             }else{
9931                 Roo.apply(bconfig, config);
9932             }
9933         }
9934         var fc = false;
9935         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9936             bconfig.position = Math.max(0, bconfig.position);
9937             fc = this.btnContainer.childNodes[bconfig.position];
9938         }
9939          
9940         var btn = new Roo.Button(
9941             fc ? 
9942                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9943                 : this.btnContainer.appendChild(document.createElement("td")),
9944             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9945             bconfig
9946         );
9947         this.syncBodyHeight();
9948         if(!this.buttons){
9949             /**
9950              * Array of all the buttons that have been added to this dialog via addButton
9951              * @type Array
9952              */
9953             this.buttons = [];
9954         }
9955         this.buttons.push(btn);
9956         return btn;
9957     },
9958
9959     /**
9960      * Sets the default button to be focused when the dialog is displayed.
9961      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9962      * @return {Roo.BasicDialog} this
9963      */
9964     setDefaultButton : function(btn){
9965         this.defaultButton = btn;
9966         return this;
9967     },
9968
9969     // private
9970     getHeaderFooterHeight : function(safe){
9971         var height = 0;
9972         if(this.header){
9973            height += this.header.getHeight();
9974         }
9975         if(this.footer){
9976            var fm = this.footer.getMargins();
9977             height += (this.footer.getHeight()+fm.top+fm.bottom);
9978         }
9979         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9980         height += this.centerBg.getPadding("tb");
9981         return height;
9982     },
9983
9984     // private
9985     syncBodyHeight : function()
9986     {
9987         var bd = this.body, // the text
9988             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9989             bw = this.bwrap;
9990         var height = this.size.height - this.getHeaderFooterHeight(false);
9991         bd.setHeight(height-bd.getMargins("tb"));
9992         var hh = this.header.getHeight();
9993         var h = this.size.height-hh;
9994         cb.setHeight(h);
9995         
9996         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9997         bw.setHeight(h-cb.getPadding("tb"));
9998         
9999         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
10000         bd.setWidth(bw.getWidth(true));
10001         if(this.tabs){
10002             this.tabs.syncHeight();
10003             if(Roo.isIE){
10004                 this.tabs.el.repaint();
10005             }
10006         }
10007     },
10008
10009     /**
10010      * Restores the previous state of the dialog if Roo.state is configured.
10011      * @return {Roo.BasicDialog} this
10012      */
10013     restoreState : function(){
10014         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
10015         if(box && box.width){
10016             this.xy = [box.x, box.y];
10017             this.resizeTo(box.width, box.height);
10018         }
10019         return this;
10020     },
10021
10022     // private
10023     beforeShow : function(){
10024         this.expand();
10025         if(this.fixedcenter){
10026             this.xy = this.el.getCenterXY(true);
10027         }
10028         if(this.modal){
10029             Roo.get(document.body).addClass("x-body-masked");
10030             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10031             this.mask.show();
10032         }
10033         this.constrainXY();
10034     },
10035
10036     // private
10037     animShow : function(){
10038         var b = Roo.get(this.animateTarget).getBox();
10039         this.proxy.setSize(b.width, b.height);
10040         this.proxy.setLocation(b.x, b.y);
10041         this.proxy.show();
10042         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10043                     true, .35, this.showEl.createDelegate(this));
10044     },
10045
10046     /**
10047      * Shows the dialog.
10048      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10049      * @return {Roo.BasicDialog} this
10050      */
10051     show : function(animateTarget){
10052         if (this.fireEvent("beforeshow", this) === false){
10053             return;
10054         }
10055         if(this.syncHeightBeforeShow){
10056             this.syncBodyHeight();
10057         }else if(this.firstShow){
10058             this.firstShow = false;
10059             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10060         }
10061         this.animateTarget = animateTarget || this.animateTarget;
10062         if(!this.el.isVisible()){
10063             this.beforeShow();
10064             if(this.animateTarget && Roo.get(this.animateTarget)){
10065                 this.animShow();
10066             }else{
10067                 this.showEl();
10068             }
10069         }
10070         return this;
10071     },
10072
10073     // private
10074     showEl : function(){
10075         this.proxy.hide();
10076         this.el.setXY(this.xy);
10077         this.el.show();
10078         this.adjustAssets(true);
10079         this.toFront();
10080         this.focus();
10081         // IE peekaboo bug - fix found by Dave Fenwick
10082         if(Roo.isIE){
10083             this.el.repaint();
10084         }
10085         this.fireEvent("show", this);
10086     },
10087
10088     /**
10089      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10090      * dialog itself will receive focus.
10091      */
10092     focus : function(){
10093         if(this.defaultButton){
10094             this.defaultButton.focus();
10095         }else{
10096             this.focusEl.focus();
10097         }
10098     },
10099
10100     // private
10101     constrainXY : function(){
10102         if(this.constraintoviewport !== false){
10103             if(!this.viewSize){
10104                 if(this.container){
10105                     var s = this.container.getSize();
10106                     this.viewSize = [s.width, s.height];
10107                 }else{
10108                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10109                 }
10110             }
10111             var s = Roo.get(this.container||document).getScroll();
10112
10113             var x = this.xy[0], y = this.xy[1];
10114             var w = this.size.width, h = this.size.height;
10115             var vw = this.viewSize[0], vh = this.viewSize[1];
10116             // only move it if it needs it
10117             var moved = false;
10118             // first validate right/bottom
10119             if(x + w > vw+s.left){
10120                 x = vw - w;
10121                 moved = true;
10122             }
10123             if(y + h > vh+s.top){
10124                 y = vh - h;
10125                 moved = true;
10126             }
10127             // then make sure top/left isn't negative
10128             if(x < s.left){
10129                 x = s.left;
10130                 moved = true;
10131             }
10132             if(y < s.top){
10133                 y = s.top;
10134                 moved = true;
10135             }
10136             if(moved){
10137                 // cache xy
10138                 this.xy = [x, y];
10139                 if(this.isVisible()){
10140                     this.el.setLocation(x, y);
10141                     this.adjustAssets();
10142                 }
10143             }
10144         }
10145     },
10146
10147     // private
10148     onDrag : function(){
10149         if(!this.proxyDrag){
10150             this.xy = this.el.getXY();
10151             this.adjustAssets();
10152         }
10153     },
10154
10155     // private
10156     adjustAssets : function(doShow){
10157         var x = this.xy[0], y = this.xy[1];
10158         var w = this.size.width, h = this.size.height;
10159         if(doShow === true){
10160             if(this.shadow){
10161                 this.shadow.show(this.el);
10162             }
10163             if(this.shim){
10164                 this.shim.show();
10165             }
10166         }
10167         if(this.shadow && this.shadow.isVisible()){
10168             this.shadow.show(this.el);
10169         }
10170         if(this.shim && this.shim.isVisible()){
10171             this.shim.setBounds(x, y, w, h);
10172         }
10173     },
10174
10175     // private
10176     adjustViewport : function(w, h){
10177         if(!w || !h){
10178             w = Roo.lib.Dom.getViewWidth();
10179             h = Roo.lib.Dom.getViewHeight();
10180         }
10181         // cache the size
10182         this.viewSize = [w, h];
10183         if(this.modal && this.mask.isVisible()){
10184             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10185             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10186         }
10187         if(this.isVisible()){
10188             this.constrainXY();
10189         }
10190     },
10191
10192     /**
10193      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10194      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10195      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10196      */
10197     destroy : function(removeEl){
10198         if(this.isVisible()){
10199             this.animateTarget = null;
10200             this.hide();
10201         }
10202         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10203         if(this.tabs){
10204             this.tabs.destroy(removeEl);
10205         }
10206         Roo.destroy(
10207              this.shim,
10208              this.proxy,
10209              this.resizer,
10210              this.close,
10211              this.mask
10212         );
10213         if(this.dd){
10214             this.dd.unreg();
10215         }
10216         if(this.buttons){
10217            for(var i = 0, len = this.buttons.length; i < len; i++){
10218                this.buttons[i].destroy();
10219            }
10220         }
10221         this.el.removeAllListeners();
10222         if(removeEl === true){
10223             this.el.update("");
10224             this.el.remove();
10225         }
10226         Roo.DialogManager.unregister(this);
10227     },
10228
10229     // private
10230     startMove : function(){
10231         if(this.proxyDrag){
10232             this.proxy.show();
10233         }
10234         if(this.constraintoviewport !== false){
10235             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10236         }
10237     },
10238
10239     // private
10240     endMove : function(){
10241         if(!this.proxyDrag){
10242             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10243         }else{
10244             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10245             this.proxy.hide();
10246         }
10247         this.refreshSize();
10248         this.adjustAssets();
10249         this.focus();
10250         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10251     },
10252
10253     /**
10254      * Brings this dialog to the front of any other visible dialogs
10255      * @return {Roo.BasicDialog} this
10256      */
10257     toFront : function(){
10258         Roo.DialogManager.bringToFront(this);
10259         return this;
10260     },
10261
10262     /**
10263      * Sends this dialog to the back (under) of any other visible dialogs
10264      * @return {Roo.BasicDialog} this
10265      */
10266     toBack : function(){
10267         Roo.DialogManager.sendToBack(this);
10268         return this;
10269     },
10270
10271     /**
10272      * Centers this dialog in the viewport
10273      * @return {Roo.BasicDialog} this
10274      */
10275     center : function(){
10276         var xy = this.el.getCenterXY(true);
10277         this.moveTo(xy[0], xy[1]);
10278         return this;
10279     },
10280
10281     /**
10282      * Moves the dialog's top-left corner to the specified point
10283      * @param {Number} x
10284      * @param {Number} y
10285      * @return {Roo.BasicDialog} this
10286      */
10287     moveTo : function(x, y){
10288         this.xy = [x,y];
10289         if(this.isVisible()){
10290             this.el.setXY(this.xy);
10291             this.adjustAssets();
10292         }
10293         return this;
10294     },
10295
10296     /**
10297      * Aligns the dialog to the specified element
10298      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10299      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10300      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10301      * @return {Roo.BasicDialog} this
10302      */
10303     alignTo : function(element, position, offsets){
10304         this.xy = this.el.getAlignToXY(element, position, offsets);
10305         if(this.isVisible()){
10306             this.el.setXY(this.xy);
10307             this.adjustAssets();
10308         }
10309         return this;
10310     },
10311
10312     /**
10313      * Anchors an element to another element and realigns it when the window is resized.
10314      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10315      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10316      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10317      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10318      * is a number, it is used as the buffer delay (defaults to 50ms).
10319      * @return {Roo.BasicDialog} this
10320      */
10321     anchorTo : function(el, alignment, offsets, monitorScroll){
10322         var action = function(){
10323             this.alignTo(el, alignment, offsets);
10324         };
10325         Roo.EventManager.onWindowResize(action, this);
10326         var tm = typeof monitorScroll;
10327         if(tm != 'undefined'){
10328             Roo.EventManager.on(window, 'scroll', action, this,
10329                 {buffer: tm == 'number' ? monitorScroll : 50});
10330         }
10331         action.call(this);
10332         return this;
10333     },
10334
10335     /**
10336      * Returns true if the dialog is visible
10337      * @return {Boolean}
10338      */
10339     isVisible : function(){
10340         return this.el.isVisible();
10341     },
10342
10343     // private
10344     animHide : function(callback){
10345         var b = Roo.get(this.animateTarget).getBox();
10346         this.proxy.show();
10347         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10348         this.el.hide();
10349         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10350                     this.hideEl.createDelegate(this, [callback]));
10351     },
10352
10353     /**
10354      * Hides the dialog.
10355      * @param {Function} callback (optional) Function to call when the dialog is hidden
10356      * @return {Roo.BasicDialog} this
10357      */
10358     hide : function(callback){
10359         if (this.fireEvent("beforehide", this) === false){
10360             return;
10361         }
10362         if(this.shadow){
10363             this.shadow.hide();
10364         }
10365         if(this.shim) {
10366           this.shim.hide();
10367         }
10368         // sometimes animateTarget seems to get set.. causing problems...
10369         // this just double checks..
10370         if(this.animateTarget && Roo.get(this.animateTarget)) {
10371            this.animHide(callback);
10372         }else{
10373             this.el.hide();
10374             this.hideEl(callback);
10375         }
10376         return this;
10377     },
10378
10379     // private
10380     hideEl : function(callback){
10381         this.proxy.hide();
10382         if(this.modal){
10383             this.mask.hide();
10384             Roo.get(document.body).removeClass("x-body-masked");
10385         }
10386         this.fireEvent("hide", this);
10387         if(typeof callback == "function"){
10388             callback();
10389         }
10390     },
10391
10392     // private
10393     hideAction : function(){
10394         this.setLeft("-10000px");
10395         this.setTop("-10000px");
10396         this.setStyle("visibility", "hidden");
10397     },
10398
10399     // private
10400     refreshSize : function(){
10401         this.size = this.el.getSize();
10402         this.xy = this.el.getXY();
10403         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10404     },
10405
10406     // private
10407     // z-index is managed by the DialogManager and may be overwritten at any time
10408     setZIndex : function(index){
10409         if(this.modal){
10410             this.mask.setStyle("z-index", index);
10411         }
10412         if(this.shim){
10413             this.shim.setStyle("z-index", ++index);
10414         }
10415         if(this.shadow){
10416             this.shadow.setZIndex(++index);
10417         }
10418         this.el.setStyle("z-index", ++index);
10419         if(this.proxy){
10420             this.proxy.setStyle("z-index", ++index);
10421         }
10422         if(this.resizer){
10423             this.resizer.proxy.setStyle("z-index", ++index);
10424         }
10425
10426         this.lastZIndex = index;
10427     },
10428
10429     /**
10430      * Returns the element for this dialog
10431      * @return {Roo.Element} The underlying dialog Element
10432      */
10433     getEl : function(){
10434         return this.el;
10435     }
10436 });
10437
10438 /**
10439  * @class Roo.DialogManager
10440  * Provides global access to BasicDialogs that have been created and
10441  * support for z-indexing (layering) multiple open dialogs.
10442  */
10443 Roo.DialogManager = function(){
10444     var list = {};
10445     var accessList = [];
10446     var front = null;
10447
10448     // private
10449     var sortDialogs = function(d1, d2){
10450         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10451     };
10452
10453     // private
10454     var orderDialogs = function(){
10455         accessList.sort(sortDialogs);
10456         var seed = Roo.DialogManager.zseed;
10457         for(var i = 0, len = accessList.length; i < len; i++){
10458             var dlg = accessList[i];
10459             if(dlg){
10460                 dlg.setZIndex(seed + (i*10));
10461             }
10462         }
10463     };
10464
10465     return {
10466         /**
10467          * The starting z-index for BasicDialogs (defaults to 9000)
10468          * @type Number The z-index value
10469          */
10470         zseed : 9000,
10471
10472         // private
10473         register : function(dlg){
10474             list[dlg.id] = dlg;
10475             accessList.push(dlg);
10476         },
10477
10478         // private
10479         unregister : function(dlg){
10480             delete list[dlg.id];
10481             var i=0;
10482             var len=0;
10483             if(!accessList.indexOf){
10484                 for(  i = 0, len = accessList.length; i < len; i++){
10485                     if(accessList[i] == dlg){
10486                         accessList.splice(i, 1);
10487                         return;
10488                     }
10489                 }
10490             }else{
10491                  i = accessList.indexOf(dlg);
10492                 if(i != -1){
10493                     accessList.splice(i, 1);
10494                 }
10495             }
10496         },
10497
10498         /**
10499          * Gets a registered dialog by id
10500          * @param {String/Object} id The id of the dialog or a dialog
10501          * @return {Roo.BasicDialog} this
10502          */
10503         get : function(id){
10504             return typeof id == "object" ? id : list[id];
10505         },
10506
10507         /**
10508          * Brings the specified dialog to the front
10509          * @param {String/Object} dlg The id of the dialog or a dialog
10510          * @return {Roo.BasicDialog} this
10511          */
10512         bringToFront : function(dlg){
10513             dlg = this.get(dlg);
10514             if(dlg != front){
10515                 front = dlg;
10516                 dlg._lastAccess = new Date().getTime();
10517                 orderDialogs();
10518             }
10519             return dlg;
10520         },
10521
10522         /**
10523          * Sends the specified dialog to the back
10524          * @param {String/Object} dlg The id of the dialog or a dialog
10525          * @return {Roo.BasicDialog} this
10526          */
10527         sendToBack : function(dlg){
10528             dlg = this.get(dlg);
10529             dlg._lastAccess = -(new Date().getTime());
10530             orderDialogs();
10531             return dlg;
10532         },
10533
10534         /**
10535          * Hides all dialogs
10536          */
10537         hideAll : function(){
10538             for(var id in list){
10539                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10540                     list[id].hide();
10541                 }
10542             }
10543         }
10544     };
10545 }();
10546
10547 /**
10548  * @class Roo.LayoutDialog
10549  * @extends Roo.BasicDialog
10550  * Dialog which provides adjustments for working with a layout in a Dialog.
10551  * Add your necessary layout config options to the dialog's config.<br>
10552  * Example usage (including a nested layout):
10553  * <pre><code>
10554 if(!dialog){
10555     dialog = new Roo.LayoutDialog("download-dlg", {
10556         modal: true,
10557         width:600,
10558         height:450,
10559         shadow:true,
10560         minWidth:500,
10561         minHeight:350,
10562         autoTabs:true,
10563         proxyDrag:true,
10564         // layout config merges with the dialog config
10565         center:{
10566             tabPosition: "top",
10567             alwaysShowTabs: true
10568         }
10569     });
10570     dialog.addKeyListener(27, dialog.hide, dialog);
10571     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10572     dialog.addButton("Build It!", this.getDownload, this);
10573
10574     // we can even add nested layouts
10575     var innerLayout = new Roo.BorderLayout("dl-inner", {
10576         east: {
10577             initialSize: 200,
10578             autoScroll:true,
10579             split:true
10580         },
10581         center: {
10582             autoScroll:true
10583         }
10584     });
10585     innerLayout.beginUpdate();
10586     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10587     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10588     innerLayout.endUpdate(true);
10589
10590     var layout = dialog.getLayout();
10591     layout.beginUpdate();
10592     layout.add("center", new Roo.ContentPanel("standard-panel",
10593                         {title: "Download the Source", fitToFrame:true}));
10594     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10595                {title: "Build your own roo.js"}));
10596     layout.getRegion("center").showPanel(sp);
10597     layout.endUpdate();
10598 }
10599 </code></pre>
10600     * @constructor
10601     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10602     * @param {Object} config configuration options
10603   */
10604 Roo.LayoutDialog = function(el, cfg){
10605     
10606     var config=  cfg;
10607     if (typeof(cfg) == 'undefined') {
10608         config = Roo.apply({}, el);
10609         // not sure why we use documentElement here.. - it should always be body.
10610         // IE7 borks horribly if we use documentElement.
10611         // webkit also does not like documentElement - it creates a body element...
10612         el = Roo.get( document.body || document.documentElement ).createChild();
10613         //config.autoCreate = true;
10614     }
10615     
10616     
10617     config.autoTabs = false;
10618     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10619     this.body.setStyle({overflow:"hidden", position:"relative"});
10620     this.layout = new Roo.BorderLayout(this.body.dom, config);
10621     this.layout.monitorWindowResize = false;
10622     this.el.addClass("x-dlg-auto-layout");
10623     // fix case when center region overwrites center function
10624     this.center = Roo.BasicDialog.prototype.center;
10625     this.on("show", this.layout.layout, this.layout, true);
10626     if (config.items) {
10627         var xitems = config.items;
10628         delete config.items;
10629         Roo.each(xitems, this.addxtype, this);
10630     }
10631     
10632     
10633 };
10634 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10635     /**
10636      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10637      * @deprecated
10638      */
10639     endUpdate : function(){
10640         this.layout.endUpdate();
10641     },
10642
10643     /**
10644      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10645      *  @deprecated
10646      */
10647     beginUpdate : function(){
10648         this.layout.beginUpdate();
10649     },
10650
10651     /**
10652      * Get the BorderLayout for this dialog
10653      * @return {Roo.BorderLayout}
10654      */
10655     getLayout : function(){
10656         return this.layout;
10657     },
10658
10659     showEl : function(){
10660         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10661         if(Roo.isIE7){
10662             this.layout.layout();
10663         }
10664     },
10665
10666     // private
10667     // Use the syncHeightBeforeShow config option to control this automatically
10668     syncBodyHeight : function(){
10669         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10670         if(this.layout){this.layout.layout();}
10671     },
10672     
10673       /**
10674      * Add an xtype element (actually adds to the layout.)
10675      * @return {Object} xdata xtype object data.
10676      */
10677     
10678     addxtype : function(c) {
10679         return this.layout.addxtype(c);
10680     }
10681 });/*
10682  * Based on:
10683  * Ext JS Library 1.1.1
10684  * Copyright(c) 2006-2007, Ext JS, LLC.
10685  *
10686  * Originally Released Under LGPL - original licence link has changed is not relivant.
10687  *
10688  * Fork - LGPL
10689  * <script type="text/javascript">
10690  */
10691  
10692 /**
10693  * @class Roo.MessageBox
10694  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10695  * Example usage:
10696  *<pre><code>
10697 // Basic alert:
10698 Roo.Msg.alert('Status', 'Changes saved successfully.');
10699
10700 // Prompt for user data:
10701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10702     if (btn == 'ok'){
10703         // process text value...
10704     }
10705 });
10706
10707 // Show a dialog using config options:
10708 Roo.Msg.show({
10709    title:'Save Changes?',
10710    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10711    buttons: Roo.Msg.YESNOCANCEL,
10712    fn: processResult,
10713    animEl: 'elId'
10714 });
10715 </code></pre>
10716  * @singleton
10717  */
10718 Roo.MessageBox = function(){
10719     var dlg, opt, mask, waitTimer;
10720     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10721     var buttons, activeTextEl, bwidth;
10722
10723     // private
10724     var handleButton = function(button){
10725         dlg.hide();
10726         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10727     };
10728
10729     // private
10730     var handleHide = function(){
10731         if(opt && opt.cls){
10732             dlg.el.removeClass(opt.cls);
10733         }
10734         if(waitTimer){
10735             Roo.TaskMgr.stop(waitTimer);
10736             waitTimer = null;
10737         }
10738     };
10739
10740     // private
10741     var updateButtons = function(b){
10742         var width = 0;
10743         if(!b){
10744             buttons["ok"].hide();
10745             buttons["cancel"].hide();
10746             buttons["yes"].hide();
10747             buttons["no"].hide();
10748             dlg.footer.dom.style.display = 'none';
10749             return width;
10750         }
10751         dlg.footer.dom.style.display = '';
10752         for(var k in buttons){
10753             if(typeof buttons[k] != "function"){
10754                 if(b[k]){
10755                     buttons[k].show();
10756                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10757                     width += buttons[k].el.getWidth()+15;
10758                 }else{
10759                     buttons[k].hide();
10760                 }
10761             }
10762         }
10763         return width;
10764     };
10765
10766     // private
10767     var handleEsc = function(d, k, e){
10768         if(opt && opt.closable !== false){
10769             dlg.hide();
10770         }
10771         if(e){
10772             e.stopEvent();
10773         }
10774     };
10775
10776     return {
10777         /**
10778          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10779          * @return {Roo.BasicDialog} The BasicDialog element
10780          */
10781         getDialog : function(){
10782            if(!dlg){
10783                 dlg = new Roo.BasicDialog("x-msg-box", {
10784                     autoCreate : true,
10785                     shadow: true,
10786                     draggable: true,
10787                     resizable:false,
10788                     constraintoviewport:false,
10789                     fixedcenter:true,
10790                     collapsible : false,
10791                     shim:true,
10792                     modal: true,
10793                     width:400, height:100,
10794                     buttonAlign:"center",
10795                     closeClick : function(){
10796                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10797                             handleButton("no");
10798                         }else{
10799                             handleButton("cancel");
10800                         }
10801                     }
10802                 });
10803                 dlg.on("hide", handleHide);
10804                 mask = dlg.mask;
10805                 dlg.addKeyListener(27, handleEsc);
10806                 buttons = {};
10807                 var bt = this.buttonText;
10808                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10809                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10810                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10811                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10812                 bodyEl = dlg.body.createChild({
10813
10814                     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>'
10815                 });
10816                 msgEl = bodyEl.dom.firstChild;
10817                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10818                 textboxEl.enableDisplayMode();
10819                 textboxEl.addKeyListener([10,13], function(){
10820                     if(dlg.isVisible() && opt && opt.buttons){
10821                         if(opt.buttons.ok){
10822                             handleButton("ok");
10823                         }else if(opt.buttons.yes){
10824                             handleButton("yes");
10825                         }
10826                     }
10827                 });
10828                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10829                 textareaEl.enableDisplayMode();
10830                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10831                 progressEl.enableDisplayMode();
10832                 var pf = progressEl.dom.firstChild;
10833                 if (pf) {
10834                     pp = Roo.get(pf.firstChild);
10835                     pp.setHeight(pf.offsetHeight);
10836                 }
10837                 
10838             }
10839             return dlg;
10840         },
10841
10842         /**
10843          * Updates the message box body text
10844          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10845          * the XHTML-compliant non-breaking space character '&amp;#160;')
10846          * @return {Roo.MessageBox} This message box
10847          */
10848         updateText : function(text){
10849             if(!dlg.isVisible() && !opt.width){
10850                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10851             }
10852             msgEl.innerHTML = text || '&#160;';
10853       
10854             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10855             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10856             var w = Math.max(
10857                     Math.min(opt.width || cw , this.maxWidth), 
10858                     Math.max(opt.minWidth || this.minWidth, bwidth)
10859             );
10860             if(opt.prompt){
10861                 activeTextEl.setWidth(w);
10862             }
10863             if(dlg.isVisible()){
10864                 dlg.fixedcenter = false;
10865             }
10866             // to big, make it scroll. = But as usual stupid IE does not support
10867             // !important..
10868             
10869             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10870                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10871                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10872             } else {
10873                 bodyEl.dom.style.height = '';
10874                 bodyEl.dom.style.overflowY = '';
10875             }
10876             if (cw > w) {
10877                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10878             } else {
10879                 bodyEl.dom.style.overflowX = '';
10880             }
10881             
10882             dlg.setContentSize(w, bodyEl.getHeight());
10883             if(dlg.isVisible()){
10884                 dlg.fixedcenter = true;
10885             }
10886             return this;
10887         },
10888
10889         /**
10890          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10891          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10892          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10893          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10894          * @return {Roo.MessageBox} This message box
10895          */
10896         updateProgress : function(value, text){
10897             if(text){
10898                 this.updateText(text);
10899             }
10900             if (pp) { // weird bug on my firefox - for some reason this is not defined
10901                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10902             }
10903             return this;
10904         },        
10905
10906         /**
10907          * Returns true if the message box is currently displayed
10908          * @return {Boolean} True if the message box is visible, else false
10909          */
10910         isVisible : function(){
10911             return dlg && dlg.isVisible();  
10912         },
10913
10914         /**
10915          * Hides the message box if it is displayed
10916          */
10917         hide : function(){
10918             if(this.isVisible()){
10919                 dlg.hide();
10920             }  
10921         },
10922
10923         /**
10924          * Displays a new message box, or reinitializes an existing message box, based on the config options
10925          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10926          * The following config object properties are supported:
10927          * <pre>
10928 Property    Type             Description
10929 ----------  ---------------  ------------------------------------------------------------------------------------
10930 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10931                                    closes (defaults to undefined)
10932 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10933                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10934 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10935                                    progress and wait dialogs will ignore this property and always hide the
10936                                    close button as they can only be closed programmatically.
10937 cls               String           A custom CSS class to apply to the message box element
10938 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10939                                    displayed (defaults to 75)
10940 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10941                                    function will be btn (the name of the button that was clicked, if applicable,
10942                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10943                                    Progress and wait dialogs will ignore this option since they do not respond to
10944                                    user actions and can only be closed programmatically, so any required function
10945                                    should be called by the same code after it closes the dialog.
10946 icon              String           A CSS class that provides a background image to be used as an icon for
10947                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10948 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10949 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10950 modal             Boolean          False to allow user interaction with the page while the message box is
10951                                    displayed (defaults to true)
10952 msg               String           A string that will replace the existing message box body text (defaults
10953                                    to the XHTML-compliant non-breaking space character '&#160;')
10954 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10955 progress          Boolean          True to display a progress bar (defaults to false)
10956 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10957 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10958 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10959 title             String           The title text
10960 value             String           The string value to set into the active textbox element if displayed
10961 wait              Boolean          True to display a progress bar (defaults to false)
10962 width             Number           The width of the dialog in pixels
10963 </pre>
10964          *
10965          * Example usage:
10966          * <pre><code>
10967 Roo.Msg.show({
10968    title: 'Address',
10969    msg: 'Please enter your address:',
10970    width: 300,
10971    buttons: Roo.MessageBox.OKCANCEL,
10972    multiline: true,
10973    fn: saveAddress,
10974    animEl: 'addAddressBtn'
10975 });
10976 </code></pre>
10977          * @param {Object} config Configuration options
10978          * @return {Roo.MessageBox} This message box
10979          */
10980         show : function(options)
10981         {
10982             
10983             // this causes nightmares if you show one dialog after another
10984             // especially on callbacks..
10985              
10986             if(this.isVisible()){
10987                 
10988                 this.hide();
10989                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10990                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10991                 Roo.log("New Dialog Message:" +  options.msg )
10992                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10993                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10994                 
10995             }
10996             var d = this.getDialog();
10997             opt = options;
10998             d.setTitle(opt.title || "&#160;");
10999             d.close.setDisplayed(opt.closable !== false);
11000             activeTextEl = textboxEl;
11001             opt.prompt = opt.prompt || (opt.multiline ? true : false);
11002             if(opt.prompt){
11003                 if(opt.multiline){
11004                     textboxEl.hide();
11005                     textareaEl.show();
11006                     textareaEl.setHeight(typeof opt.multiline == "number" ?
11007                         opt.multiline : this.defaultTextHeight);
11008                     activeTextEl = textareaEl;
11009                 }else{
11010                     textboxEl.show();
11011                     textareaEl.hide();
11012                 }
11013             }else{
11014                 textboxEl.hide();
11015                 textareaEl.hide();
11016             }
11017             progressEl.setDisplayed(opt.progress === true);
11018             this.updateProgress(0);
11019             activeTextEl.dom.value = opt.value || "";
11020             if(opt.prompt){
11021                 dlg.setDefaultButton(activeTextEl);
11022             }else{
11023                 var bs = opt.buttons;
11024                 var db = null;
11025                 if(bs && bs.ok){
11026                     db = buttons["ok"];
11027                 }else if(bs && bs.yes){
11028                     db = buttons["yes"];
11029                 }
11030                 dlg.setDefaultButton(db);
11031             }
11032             bwidth = updateButtons(opt.buttons);
11033             this.updateText(opt.msg);
11034             if(opt.cls){
11035                 d.el.addClass(opt.cls);
11036             }
11037             d.proxyDrag = opt.proxyDrag === true;
11038             d.modal = opt.modal !== false;
11039             d.mask = opt.modal !== false ? mask : false;
11040             if(!d.isVisible()){
11041                 // force it to the end of the z-index stack so it gets a cursor in FF
11042                 document.body.appendChild(dlg.el.dom);
11043                 d.animateTarget = null;
11044                 d.show(options.animEl);
11045             }
11046             return this;
11047         },
11048
11049         /**
11050          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11051          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11052          * and closing the message box when the process is complete.
11053          * @param {String} title The title bar text
11054          * @param {String} msg The message box body text
11055          * @return {Roo.MessageBox} This message box
11056          */
11057         progress : function(title, msg){
11058             this.show({
11059                 title : title,
11060                 msg : msg,
11061                 buttons: false,
11062                 progress:true,
11063                 closable:false,
11064                 minWidth: this.minProgressWidth,
11065                 modal : true
11066             });
11067             return this;
11068         },
11069
11070         /**
11071          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11072          * If a callback function is passed it will be called after the user clicks the button, and the
11073          * id of the button that was clicked will be passed as the only parameter to the callback
11074          * (could also be the top-right close button).
11075          * @param {String} title The title bar text
11076          * @param {String} msg The message box body text
11077          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11078          * @param {Object} scope (optional) The scope of the callback function
11079          * @return {Roo.MessageBox} This message box
11080          */
11081         alert : function(title, msg, fn, scope){
11082             this.show({
11083                 title : title,
11084                 msg : msg,
11085                 buttons: this.OK,
11086                 fn: fn,
11087                 scope : scope,
11088                 modal : true
11089             });
11090             return this;
11091         },
11092
11093         /**
11094          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11095          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11096          * You are responsible for closing the message box when the process is complete.
11097          * @param {String} msg The message box body text
11098          * @param {String} title (optional) The title bar text
11099          * @return {Roo.MessageBox} This message box
11100          */
11101         wait : function(msg, title){
11102             this.show({
11103                 title : title,
11104                 msg : msg,
11105                 buttons: false,
11106                 closable:false,
11107                 progress:true,
11108                 modal:true,
11109                 width:300,
11110                 wait:true
11111             });
11112             waitTimer = Roo.TaskMgr.start({
11113                 run: function(i){
11114                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11115                 },
11116                 interval: 1000
11117             });
11118             return this;
11119         },
11120
11121         /**
11122          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11123          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11124          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11125          * @param {String} title The title bar text
11126          * @param {String} msg The message box body text
11127          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11128          * @param {Object} scope (optional) The scope of the callback function
11129          * @return {Roo.MessageBox} This message box
11130          */
11131         confirm : function(title, msg, fn, scope){
11132             this.show({
11133                 title : title,
11134                 msg : msg,
11135                 buttons: this.YESNO,
11136                 fn: fn,
11137                 scope : scope,
11138                 modal : true
11139             });
11140             return this;
11141         },
11142
11143         /**
11144          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11145          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11146          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11147          * (could also be the top-right close button) and the text that was entered will be passed as the two
11148          * parameters to the callback.
11149          * @param {String} title The title bar text
11150          * @param {String} msg The message box body text
11151          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11152          * @param {Object} scope (optional) The scope of the callback function
11153          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11154          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11155          * @return {Roo.MessageBox} This message box
11156          */
11157         prompt : function(title, msg, fn, scope, multiline){
11158             this.show({
11159                 title : title,
11160                 msg : msg,
11161                 buttons: this.OKCANCEL,
11162                 fn: fn,
11163                 minWidth:250,
11164                 scope : scope,
11165                 prompt:true,
11166                 multiline: multiline,
11167                 modal : true
11168             });
11169             return this;
11170         },
11171
11172         /**
11173          * Button config that displays a single OK button
11174          * @type Object
11175          */
11176         OK : {ok:true},
11177         /**
11178          * Button config that displays Yes and No buttons
11179          * @type Object
11180          */
11181         YESNO : {yes:true, no:true},
11182         /**
11183          * Button config that displays OK and Cancel buttons
11184          * @type Object
11185          */
11186         OKCANCEL : {ok:true, cancel:true},
11187         /**
11188          * Button config that displays Yes, No and Cancel buttons
11189          * @type Object
11190          */
11191         YESNOCANCEL : {yes:true, no:true, cancel:true},
11192
11193         /**
11194          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11195          * @type Number
11196          */
11197         defaultTextHeight : 75,
11198         /**
11199          * The maximum width in pixels of the message box (defaults to 600)
11200          * @type Number
11201          */
11202         maxWidth : 600,
11203         /**
11204          * The minimum width in pixels of the message box (defaults to 100)
11205          * @type Number
11206          */
11207         minWidth : 100,
11208         /**
11209          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11210          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11211          * @type Number
11212          */
11213         minProgressWidth : 250,
11214         /**
11215          * An object containing the default button text strings that can be overriden for localized language support.
11216          * Supported properties are: ok, cancel, yes and no.
11217          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11218          * @type Object
11219          */
11220         buttonText : {
11221             ok : "OK",
11222             cancel : "Cancel",
11223             yes : "Yes",
11224             no : "No"
11225         }
11226     };
11227 }();
11228
11229 /**
11230  * Shorthand for {@link Roo.MessageBox}
11231  */
11232 Roo.Msg = Roo.MessageBox;/*
11233  * Based on:
11234  * Ext JS Library 1.1.1
11235  * Copyright(c) 2006-2007, Ext JS, LLC.
11236  *
11237  * Originally Released Under LGPL - original licence link has changed is not relivant.
11238  *
11239  * Fork - LGPL
11240  * <script type="text/javascript">
11241  */
11242 /**
11243  * @class Roo.QuickTips
11244  * Provides attractive and customizable tooltips for any element.
11245  * @singleton
11246  */
11247 Roo.QuickTips = function(){
11248     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11249     var ce, bd, xy, dd;
11250     var visible = false, disabled = true, inited = false;
11251     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11252     
11253     var onOver = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         var t = e.getTarget();
11258         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11259             return;
11260         }
11261         if(ce && t == ce.el){
11262             clearTimeout(hideProc);
11263             return;
11264         }
11265         if(t && tagEls[t.id]){
11266             tagEls[t.id].el = t;
11267             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11268             return;
11269         }
11270         var ttp, et = Roo.fly(t);
11271         var ns = cfg.namespace;
11272         if(tm.interceptTitles && t.title){
11273             ttp = t.title;
11274             t.qtip = ttp;
11275             t.removeAttribute("title");
11276             e.preventDefault();
11277         }else{
11278             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11279         }
11280         if(ttp){
11281             showProc = show.defer(tm.showDelay, tm, [{
11282                 el: t, 
11283                 text: ttp.replace(/\\n/g,'<br/>'),
11284                 width: et.getAttributeNS(ns, cfg.width),
11285                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11286                 title: et.getAttributeNS(ns, cfg.title),
11287                     cls: et.getAttributeNS(ns, cfg.cls)
11288             }]);
11289         }
11290     };
11291     
11292     var onOut = function(e){
11293         clearTimeout(showProc);
11294         var t = e.getTarget();
11295         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11296             hideProc = setTimeout(hide, tm.hideDelay);
11297         }
11298     };
11299     
11300     var onMove = function(e){
11301         if(disabled){
11302             return;
11303         }
11304         xy = e.getXY();
11305         xy[1] += 18;
11306         if(tm.trackMouse && ce){
11307             el.setXY(xy);
11308         }
11309     };
11310     
11311     var onDown = function(e){
11312         clearTimeout(showProc);
11313         clearTimeout(hideProc);
11314         if(!e.within(el)){
11315             if(tm.hideOnClick){
11316                 hide();
11317                 tm.disable();
11318                 tm.enable.defer(100, tm);
11319             }
11320         }
11321     };
11322     
11323     var getPad = function(){
11324         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11325     };
11326
11327     var show = function(o){
11328         if(disabled){
11329             return;
11330         }
11331         clearTimeout(dismissProc);
11332         ce = o;
11333         if(removeCls){ // in case manually hidden
11334             el.removeClass(removeCls);
11335             removeCls = null;
11336         }
11337         if(ce.cls){
11338             el.addClass(ce.cls);
11339             removeCls = ce.cls;
11340         }
11341         if(ce.title){
11342             tipTitle.update(ce.title);
11343             tipTitle.show();
11344         }else{
11345             tipTitle.update('');
11346             tipTitle.hide();
11347         }
11348         el.dom.style.width  = tm.maxWidth+'px';
11349         //tipBody.dom.style.width = '';
11350         tipBodyText.update(o.text);
11351         var p = getPad(), w = ce.width;
11352         if(!w){
11353             var td = tipBodyText.dom;
11354             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11355             if(aw > tm.maxWidth){
11356                 w = tm.maxWidth;
11357             }else if(aw < tm.minWidth){
11358                 w = tm.minWidth;
11359             }else{
11360                 w = aw;
11361             }
11362         }
11363         //tipBody.setWidth(w);
11364         el.setWidth(parseInt(w, 10) + p);
11365         if(ce.autoHide === false){
11366             close.setDisplayed(true);
11367             if(dd){
11368                 dd.unlock();
11369             }
11370         }else{
11371             close.setDisplayed(false);
11372             if(dd){
11373                 dd.lock();
11374             }
11375         }
11376         if(xy){
11377             el.avoidY = xy[1]-18;
11378             el.setXY(xy);
11379         }
11380         if(tm.animate){
11381             el.setOpacity(.1);
11382             el.setStyle("visibility", "visible");
11383             el.fadeIn({callback: afterShow});
11384         }else{
11385             afterShow();
11386         }
11387     };
11388     
11389     var afterShow = function(){
11390         if(ce){
11391             el.show();
11392             esc.enable();
11393             if(tm.autoDismiss && ce.autoHide !== false){
11394                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11395             }
11396         }
11397     };
11398     
11399     var hide = function(noanim){
11400         clearTimeout(dismissProc);
11401         clearTimeout(hideProc);
11402         ce = null;
11403         if(el.isVisible()){
11404             esc.disable();
11405             if(noanim !== true && tm.animate){
11406                 el.fadeOut({callback: afterHide});
11407             }else{
11408                 afterHide();
11409             } 
11410         }
11411     };
11412     
11413     var afterHide = function(){
11414         el.hide();
11415         if(removeCls){
11416             el.removeClass(removeCls);
11417             removeCls = null;
11418         }
11419     };
11420     
11421     return {
11422         /**
11423         * @cfg {Number} minWidth
11424         * The minimum width of the quick tip (defaults to 40)
11425         */
11426        minWidth : 40,
11427         /**
11428         * @cfg {Number} maxWidth
11429         * The maximum width of the quick tip (defaults to 300)
11430         */
11431        maxWidth : 300,
11432         /**
11433         * @cfg {Boolean} interceptTitles
11434         * True to automatically use the element's DOM title value if available (defaults to false)
11435         */
11436        interceptTitles : false,
11437         /**
11438         * @cfg {Boolean} trackMouse
11439         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11440         */
11441        trackMouse : false,
11442         /**
11443         * @cfg {Boolean} hideOnClick
11444         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11445         */
11446        hideOnClick : true,
11447         /**
11448         * @cfg {Number} showDelay
11449         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11450         */
11451        showDelay : 500,
11452         /**
11453         * @cfg {Number} hideDelay
11454         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11455         */
11456        hideDelay : 200,
11457         /**
11458         * @cfg {Boolean} autoHide
11459         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11460         * Used in conjunction with hideDelay.
11461         */
11462        autoHide : true,
11463         /**
11464         * @cfg {Boolean}
11465         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11466         * (defaults to true).  Used in conjunction with autoDismissDelay.
11467         */
11468        autoDismiss : true,
11469         /**
11470         * @cfg {Number}
11471         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11472         */
11473        autoDismissDelay : 5000,
11474        /**
11475         * @cfg {Boolean} animate
11476         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11477         */
11478        animate : false,
11479
11480        /**
11481         * @cfg {String} title
11482         * Title text to display (defaults to '').  This can be any valid HTML markup.
11483         */
11484         title: '',
11485        /**
11486         * @cfg {String} text
11487         * Body text to display (defaults to '').  This can be any valid HTML markup.
11488         */
11489         text : '',
11490        /**
11491         * @cfg {String} cls
11492         * A CSS class to apply to the base quick tip element (defaults to '').
11493         */
11494         cls : '',
11495        /**
11496         * @cfg {Number} width
11497         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11498         * minWidth or maxWidth.
11499         */
11500         width : null,
11501
11502     /**
11503      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11504      * or display QuickTips in a page.
11505      */
11506        init : function(){
11507           tm = Roo.QuickTips;
11508           cfg = tm.tagConfig;
11509           if(!inited){
11510               if(!Roo.isReady){ // allow calling of init() before onReady
11511                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11512                   return;
11513               }
11514               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11515               el.fxDefaults = {stopFx: true};
11516               // maximum custom styling
11517               //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>');
11518               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>');              
11519               tipTitle = el.child('h3');
11520               tipTitle.enableDisplayMode("block");
11521               tipBody = el.child('div.x-tip-bd');
11522               tipBodyText = el.child('div.x-tip-bd-inner');
11523               //bdLeft = el.child('div.x-tip-bd-left');
11524               //bdRight = el.child('div.x-tip-bd-right');
11525               close = el.child('div.x-tip-close');
11526               close.enableDisplayMode("block");
11527               close.on("click", hide);
11528               var d = Roo.get(document);
11529               d.on("mousedown", onDown);
11530               d.on("mouseover", onOver);
11531               d.on("mouseout", onOut);
11532               d.on("mousemove", onMove);
11533               esc = d.addKeyListener(27, hide);
11534               esc.disable();
11535               if(Roo.dd.DD){
11536                   dd = el.initDD("default", null, {
11537                       onDrag : function(){
11538                           el.sync();  
11539                       }
11540                   });
11541                   dd.setHandleElId(tipTitle.id);
11542                   dd.lock();
11543               }
11544               inited = true;
11545           }
11546           this.enable(); 
11547        },
11548
11549     /**
11550      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11551      * are supported:
11552      * <pre>
11553 Property    Type                   Description
11554 ----------  ---------------------  ------------------------------------------------------------------------
11555 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11556      * </ul>
11557      * @param {Object} config The config object
11558      */
11559        register : function(config){
11560            var cs = config instanceof Array ? config : arguments;
11561            for(var i = 0, len = cs.length; i < len; i++) {
11562                var c = cs[i];
11563                var target = c.target;
11564                if(target){
11565                    if(target instanceof Array){
11566                        for(var j = 0, jlen = target.length; j < jlen; j++){
11567                            tagEls[target[j]] = c;
11568                        }
11569                    }else{
11570                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11571                    }
11572                }
11573            }
11574        },
11575
11576     /**
11577      * Removes this quick tip from its element and destroys it.
11578      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11579      */
11580        unregister : function(el){
11581            delete tagEls[Roo.id(el)];
11582        },
11583
11584     /**
11585      * Enable this quick tip.
11586      */
11587        enable : function(){
11588            if(inited && disabled){
11589                locks.pop();
11590                if(locks.length < 1){
11591                    disabled = false;
11592                }
11593            }
11594        },
11595
11596     /**
11597      * Disable this quick tip.
11598      */
11599        disable : function(){
11600           disabled = true;
11601           clearTimeout(showProc);
11602           clearTimeout(hideProc);
11603           clearTimeout(dismissProc);
11604           if(ce){
11605               hide(true);
11606           }
11607           locks.push(1);
11608        },
11609
11610     /**
11611      * Returns true if the quick tip is enabled, else false.
11612      */
11613        isEnabled : function(){
11614             return !disabled;
11615        },
11616
11617         // private
11618        tagConfig : {
11619            namespace : "roo", // was ext?? this may break..
11620            alt_namespace : "ext",
11621            attribute : "qtip",
11622            width : "width",
11623            target : "target",
11624            title : "qtitle",
11625            hide : "hide",
11626            cls : "qclass"
11627        }
11628    };
11629 }();
11630
11631 // backwards compat
11632 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11633  * Based on:
11634  * Ext JS Library 1.1.1
11635  * Copyright(c) 2006-2007, Ext JS, LLC.
11636  *
11637  * Originally Released Under LGPL - original licence link has changed is not relivant.
11638  *
11639  * Fork - LGPL
11640  * <script type="text/javascript">
11641  */
11642  
11643
11644 /**
11645  * @class Roo.tree.TreePanel
11646  * @extends Roo.data.Tree
11647
11648  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11649  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11650  * @cfg {Boolean} enableDD true to enable drag and drop
11651  * @cfg {Boolean} enableDrag true to enable just drag
11652  * @cfg {Boolean} enableDrop true to enable just drop
11653  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11654  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11655  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11656  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11657  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11658  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11659  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11660  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11661  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11662  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11663  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11664  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11665  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11666  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11667  * @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>
11668  * @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>
11669  * 
11670  * @constructor
11671  * @param {String/HTMLElement/Element} el The container element
11672  * @param {Object} config
11673  */
11674 Roo.tree.TreePanel = function(el, config){
11675     var root = false;
11676     var loader = false;
11677     if (config.root) {
11678         root = config.root;
11679         delete config.root;
11680     }
11681     if (config.loader) {
11682         loader = config.loader;
11683         delete config.loader;
11684     }
11685     
11686     Roo.apply(this, config);
11687     Roo.tree.TreePanel.superclass.constructor.call(this);
11688     this.el = Roo.get(el);
11689     this.el.addClass('x-tree');
11690     //console.log(root);
11691     if (root) {
11692         this.setRootNode( Roo.factory(root, Roo.tree));
11693     }
11694     if (loader) {
11695         this.loader = Roo.factory(loader, Roo.tree);
11696     }
11697    /**
11698     * Read-only. The id of the container element becomes this TreePanel's id.
11699     */
11700     this.id = this.el.id;
11701     this.addEvents({
11702         /**
11703         * @event beforeload
11704         * Fires before a node is loaded, return false to cancel
11705         * @param {Node} node The node being loaded
11706         */
11707         "beforeload" : true,
11708         /**
11709         * @event load
11710         * Fires when a node is loaded
11711         * @param {Node} node The node that was loaded
11712         */
11713         "load" : true,
11714         /**
11715         * @event textchange
11716         * Fires when the text for a node is changed
11717         * @param {Node} node The node
11718         * @param {String} text The new text
11719         * @param {String} oldText The old text
11720         */
11721         "textchange" : true,
11722         /**
11723         * @event beforeexpand
11724         * Fires before a node is expanded, return false to cancel.
11725         * @param {Node} node The node
11726         * @param {Boolean} deep
11727         * @param {Boolean} anim
11728         */
11729         "beforeexpand" : true,
11730         /**
11731         * @event beforecollapse
11732         * Fires before a node is collapsed, return false to cancel.
11733         * @param {Node} node The node
11734         * @param {Boolean} deep
11735         * @param {Boolean} anim
11736         */
11737         "beforecollapse" : true,
11738         /**
11739         * @event expand
11740         * Fires when a node is expanded
11741         * @param {Node} node The node
11742         */
11743         "expand" : true,
11744         /**
11745         * @event disabledchange
11746         * Fires when the disabled status of a node changes
11747         * @param {Node} node The node
11748         * @param {Boolean} disabled
11749         */
11750         "disabledchange" : true,
11751         /**
11752         * @event collapse
11753         * Fires when a node is collapsed
11754         * @param {Node} node The node
11755         */
11756         "collapse" : true,
11757         /**
11758         * @event beforeclick
11759         * Fires before click processing on a node. Return false to cancel the default action.
11760         * @param {Node} node The node
11761         * @param {Roo.EventObject} e The event object
11762         */
11763         "beforeclick":true,
11764         /**
11765         * @event checkchange
11766         * Fires when a node with a checkbox's checked property changes
11767         * @param {Node} this This node
11768         * @param {Boolean} checked
11769         */
11770         "checkchange":true,
11771         /**
11772         * @event click
11773         * Fires when a node is clicked
11774         * @param {Node} node The node
11775         * @param {Roo.EventObject} e The event object
11776         */
11777         "click":true,
11778         /**
11779         * @event dblclick
11780         * Fires when a node is double clicked
11781         * @param {Node} node The node
11782         * @param {Roo.EventObject} e The event object
11783         */
11784         "dblclick":true,
11785         /**
11786         * @event contextmenu
11787         * Fires when a node is right clicked
11788         * @param {Node} node The node
11789         * @param {Roo.EventObject} e The event object
11790         */
11791         "contextmenu":true,
11792         /**
11793         * @event beforechildrenrendered
11794         * Fires right before the child nodes for a node are rendered
11795         * @param {Node} node The node
11796         */
11797         "beforechildrenrendered":true,
11798         /**
11799         * @event startdrag
11800         * Fires when a node starts being dragged
11801         * @param {Roo.tree.TreePanel} this
11802         * @param {Roo.tree.TreeNode} node
11803         * @param {event} e The raw browser event
11804         */ 
11805        "startdrag" : true,
11806        /**
11807         * @event enddrag
11808         * Fires when a drag operation is complete
11809         * @param {Roo.tree.TreePanel} this
11810         * @param {Roo.tree.TreeNode} node
11811         * @param {event} e The raw browser event
11812         */
11813        "enddrag" : true,
11814        /**
11815         * @event dragdrop
11816         * Fires when a dragged node is dropped on a valid DD target
11817         * @param {Roo.tree.TreePanel} this
11818         * @param {Roo.tree.TreeNode} node
11819         * @param {DD} dd The dd it was dropped on
11820         * @param {event} e The raw browser event
11821         */
11822        "dragdrop" : true,
11823        /**
11824         * @event beforenodedrop
11825         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
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 <b>OR</b> you can supply node(s)
11835         * to be inserted by setting them on this object.</li>
11836         * <li>cancel - Set this to true to cancel the drop.</li>
11837         * </ul>
11838         * @param {Object} dropEvent
11839         */
11840        "beforenodedrop" : true,
11841        /**
11842         * @event nodedrop
11843         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11844         * passed to handlers has the following properties:<br />
11845         * <ul style="padding:5px;padding-left:16px;">
11846         * <li>tree - The TreePanel</li>
11847         * <li>target - The node being targeted for the drop</li>
11848         * <li>data - The drag data from the drag source</li>
11849         * <li>point - The point of the drop - append, above or below</li>
11850         * <li>source - The drag source</li>
11851         * <li>rawEvent - Raw mouse event</li>
11852         * <li>dropNode - Dropped node(s).</li>
11853         * </ul>
11854         * @param {Object} dropEvent
11855         */
11856        "nodedrop" : true,
11857         /**
11858         * @event nodedragover
11859         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11860         * passed to handlers has the following properties:<br />
11861         * <ul style="padding:5px;padding-left:16px;">
11862         * <li>tree - The TreePanel</li>
11863         * <li>target - The node being targeted for the drop</li>
11864         * <li>data - The drag data from the drag source</li>
11865         * <li>point - The point of the drop - append, above or below</li>
11866         * <li>source - The drag source</li>
11867         * <li>rawEvent - Raw mouse event</li>
11868         * <li>dropNode - Drop node(s) provided by the source.</li>
11869         * <li>cancel - Set this to true to signal drop not allowed.</li>
11870         * </ul>
11871         * @param {Object} dragOverEvent
11872         */
11873        "nodedragover" : true,
11874        /**
11875         * @event appendnode
11876         * Fires when append node to the tree
11877         * @param {Roo.tree.TreePanel} this
11878         * @param {Roo.tree.TreeNode} node
11879         * @param {Number} index The index of the newly appended node
11880         */
11881        "appendnode" : true
11882         
11883     });
11884     if(this.singleExpand){
11885        this.on("beforeexpand", this.restrictExpand, this);
11886     }
11887     if (this.editor) {
11888         this.editor.tree = this;
11889         this.editor = Roo.factory(this.editor, Roo.tree);
11890     }
11891     
11892     if (this.selModel) {
11893         this.selModel = Roo.factory(this.selModel, Roo.tree);
11894     }
11895    
11896 };
11897 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11898     rootVisible : true,
11899     animate: Roo.enableFx,
11900     lines : true,
11901     enableDD : false,
11902     hlDrop : Roo.enableFx,
11903   
11904     renderer: false,
11905     
11906     rendererTip: false,
11907     // private
11908     restrictExpand : function(node){
11909         var p = node.parentNode;
11910         if(p){
11911             if(p.expandedChild && p.expandedChild.parentNode == p){
11912                 p.expandedChild.collapse();
11913             }
11914             p.expandedChild = node;
11915         }
11916     },
11917
11918     // private override
11919     setRootNode : function(node){
11920         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11921         if(!this.rootVisible){
11922             node.ui = new Roo.tree.RootTreeNodeUI(node);
11923         }
11924         return node;
11925     },
11926
11927     /**
11928      * Returns the container element for this TreePanel
11929      */
11930     getEl : function(){
11931         return this.el;
11932     },
11933
11934     /**
11935      * Returns the default TreeLoader for this TreePanel
11936      */
11937     getLoader : function(){
11938         return this.loader;
11939     },
11940
11941     /**
11942      * Expand all nodes
11943      */
11944     expandAll : function(){
11945         this.root.expand(true);
11946     },
11947
11948     /**
11949      * Collapse all nodes
11950      */
11951     collapseAll : function(){
11952         this.root.collapse(true);
11953     },
11954
11955     /**
11956      * Returns the selection model used by this TreePanel
11957      */
11958     getSelectionModel : function(){
11959         if(!this.selModel){
11960             this.selModel = new Roo.tree.DefaultSelectionModel();
11961         }
11962         return this.selModel;
11963     },
11964
11965     /**
11966      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11967      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11968      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11969      * @return {Array}
11970      */
11971     getChecked : function(a, startNode){
11972         startNode = startNode || this.root;
11973         var r = [];
11974         var f = function(){
11975             if(this.attributes.checked){
11976                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11977             }
11978         }
11979         startNode.cascade(f);
11980         return r;
11981     },
11982
11983     /**
11984      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11985      * @param {String} path
11986      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11987      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11988      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11989      */
11990     expandPath : function(path, attr, callback){
11991         attr = attr || "id";
11992         var keys = path.split(this.pathSeparator);
11993         var curNode = this.root;
11994         if(curNode.attributes[attr] != keys[1]){ // invalid root
11995             if(callback){
11996                 callback(false, null);
11997             }
11998             return;
11999         }
12000         var index = 1;
12001         var f = function(){
12002             if(++index == keys.length){
12003                 if(callback){
12004                     callback(true, curNode);
12005                 }
12006                 return;
12007             }
12008             var c = curNode.findChild(attr, keys[index]);
12009             if(!c){
12010                 if(callback){
12011                     callback(false, curNode);
12012                 }
12013                 return;
12014             }
12015             curNode = c;
12016             c.expand(false, false, f);
12017         };
12018         curNode.expand(false, false, f);
12019     },
12020
12021     /**
12022      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12023      * @param {String} path
12024      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12025      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12026      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12027      */
12028     selectPath : function(path, attr, callback){
12029         attr = attr || "id";
12030         var keys = path.split(this.pathSeparator);
12031         var v = keys.pop();
12032         if(keys.length > 0){
12033             var f = function(success, node){
12034                 if(success && node){
12035                     var n = node.findChild(attr, v);
12036                     if(n){
12037                         n.select();
12038                         if(callback){
12039                             callback(true, n);
12040                         }
12041                     }else if(callback){
12042                         callback(false, n);
12043                     }
12044                 }else{
12045                     if(callback){
12046                         callback(false, n);
12047                     }
12048                 }
12049             };
12050             this.expandPath(keys.join(this.pathSeparator), attr, f);
12051         }else{
12052             this.root.select();
12053             if(callback){
12054                 callback(true, this.root);
12055             }
12056         }
12057     },
12058
12059     getTreeEl : function(){
12060         return this.el;
12061     },
12062
12063     /**
12064      * Trigger rendering of this TreePanel
12065      */
12066     render : function(){
12067         if (this.innerCt) {
12068             return this; // stop it rendering more than once!!
12069         }
12070         
12071         this.innerCt = this.el.createChild({tag:"ul",
12072                cls:"x-tree-root-ct " +
12073                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12074
12075         if(this.containerScroll){
12076             Roo.dd.ScrollManager.register(this.el);
12077         }
12078         if((this.enableDD || this.enableDrop) && !this.dropZone){
12079            /**
12080             * The dropZone used by this tree if drop is enabled
12081             * @type Roo.tree.TreeDropZone
12082             */
12083              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12084                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12085            });
12086         }
12087         if((this.enableDD || this.enableDrag) && !this.dragZone){
12088            /**
12089             * The dragZone used by this tree if drag is enabled
12090             * @type Roo.tree.TreeDragZone
12091             */
12092             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12093                ddGroup: this.ddGroup || "TreeDD",
12094                scroll: this.ddScroll
12095            });
12096         }
12097         this.getSelectionModel().init(this);
12098         if (!this.root) {
12099             Roo.log("ROOT not set in tree");
12100             return this;
12101         }
12102         this.root.render();
12103         if(!this.rootVisible){
12104             this.root.renderChildren();
12105         }
12106         return this;
12107     }
12108 });/*
12109  * Based on:
12110  * Ext JS Library 1.1.1
12111  * Copyright(c) 2006-2007, Ext JS, LLC.
12112  *
12113  * Originally Released Under LGPL - original licence link has changed is not relivant.
12114  *
12115  * Fork - LGPL
12116  * <script type="text/javascript">
12117  */
12118  
12119
12120 /**
12121  * @class Roo.tree.DefaultSelectionModel
12122  * @extends Roo.util.Observable
12123  * The default single selection for a TreePanel.
12124  * @param {Object} cfg Configuration
12125  */
12126 Roo.tree.DefaultSelectionModel = function(cfg){
12127    this.selNode = null;
12128    
12129    
12130    
12131    this.addEvents({
12132        /**
12133         * @event selectionchange
12134         * Fires when the selected node changes
12135         * @param {DefaultSelectionModel} this
12136         * @param {TreeNode} node the new selection
12137         */
12138        "selectionchange" : true,
12139
12140        /**
12141         * @event beforeselect
12142         * Fires before the selected node changes, return false to cancel the change
12143         * @param {DefaultSelectionModel} this
12144         * @param {TreeNode} node the new selection
12145         * @param {TreeNode} node the old selection
12146         */
12147        "beforeselect" : true
12148    });
12149    
12150     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12151 };
12152
12153 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12154     init : function(tree){
12155         this.tree = tree;
12156         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12157         tree.on("click", this.onNodeClick, this);
12158     },
12159     
12160     onNodeClick : function(node, e){
12161         if (e.ctrlKey && this.selNode == node)  {
12162             this.unselect(node);
12163             return;
12164         }
12165         this.select(node);
12166     },
12167     
12168     /**
12169      * Select a node.
12170      * @param {TreeNode} node The node to select
12171      * @return {TreeNode} The selected node
12172      */
12173     select : function(node){
12174         var last = this.selNode;
12175         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12176             if(last){
12177                 last.ui.onSelectedChange(false);
12178             }
12179             this.selNode = node;
12180             node.ui.onSelectedChange(true);
12181             this.fireEvent("selectionchange", this, node, last);
12182         }
12183         return node;
12184     },
12185     
12186     /**
12187      * Deselect a node.
12188      * @param {TreeNode} node The node to unselect
12189      */
12190     unselect : function(node){
12191         if(this.selNode == node){
12192             this.clearSelections();
12193         }    
12194     },
12195     
12196     /**
12197      * Clear all selections
12198      */
12199     clearSelections : function(){
12200         var n = this.selNode;
12201         if(n){
12202             n.ui.onSelectedChange(false);
12203             this.selNode = null;
12204             this.fireEvent("selectionchange", this, null);
12205         }
12206         return n;
12207     },
12208     
12209     /**
12210      * Get the selected node
12211      * @return {TreeNode} The selected node
12212      */
12213     getSelectedNode : function(){
12214         return this.selNode;    
12215     },
12216     
12217     /**
12218      * Returns true if the node is selected
12219      * @param {TreeNode} node The node to check
12220      * @return {Boolean}
12221      */
12222     isSelected : function(node){
12223         return this.selNode == node;  
12224     },
12225
12226     /**
12227      * Selects the node above the selected node in the tree, intelligently walking the nodes
12228      * @return TreeNode The new selection
12229      */
12230     selectPrevious : function(){
12231         var s = this.selNode || this.lastSelNode;
12232         if(!s){
12233             return null;
12234         }
12235         var ps = s.previousSibling;
12236         if(ps){
12237             if(!ps.isExpanded() || ps.childNodes.length < 1){
12238                 return this.select(ps);
12239             } else{
12240                 var lc = ps.lastChild;
12241                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12242                     lc = lc.lastChild;
12243                 }
12244                 return this.select(lc);
12245             }
12246         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12247             return this.select(s.parentNode);
12248         }
12249         return null;
12250     },
12251
12252     /**
12253      * Selects the node above the selected node in the tree, intelligently walking the nodes
12254      * @return TreeNode The new selection
12255      */
12256     selectNext : function(){
12257         var s = this.selNode || this.lastSelNode;
12258         if(!s){
12259             return null;
12260         }
12261         if(s.firstChild && s.isExpanded()){
12262              return this.select(s.firstChild);
12263          }else if(s.nextSibling){
12264              return this.select(s.nextSibling);
12265          }else if(s.parentNode){
12266             var newS = null;
12267             s.parentNode.bubble(function(){
12268                 if(this.nextSibling){
12269                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12270                     return false;
12271                 }
12272             });
12273             return newS;
12274          }
12275         return null;
12276     },
12277
12278     onKeyDown : function(e){
12279         var s = this.selNode || this.lastSelNode;
12280         // undesirable, but required
12281         var sm = this;
12282         if(!s){
12283             return;
12284         }
12285         var k = e.getKey();
12286         switch(k){
12287              case e.DOWN:
12288                  e.stopEvent();
12289                  this.selectNext();
12290              break;
12291              case e.UP:
12292                  e.stopEvent();
12293                  this.selectPrevious();
12294              break;
12295              case e.RIGHT:
12296                  e.preventDefault();
12297                  if(s.hasChildNodes()){
12298                      if(!s.isExpanded()){
12299                          s.expand();
12300                      }else if(s.firstChild){
12301                          this.select(s.firstChild, e);
12302                      }
12303                  }
12304              break;
12305              case e.LEFT:
12306                  e.preventDefault();
12307                  if(s.hasChildNodes() && s.isExpanded()){
12308                      s.collapse();
12309                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12310                      this.select(s.parentNode, e);
12311                  }
12312              break;
12313         };
12314     }
12315 });
12316
12317 /**
12318  * @class Roo.tree.MultiSelectionModel
12319  * @extends Roo.util.Observable
12320  * Multi selection for a TreePanel.
12321  * @param {Object} cfg Configuration
12322  */
12323 Roo.tree.MultiSelectionModel = function(){
12324    this.selNodes = [];
12325    this.selMap = {};
12326    this.addEvents({
12327        /**
12328         * @event selectionchange
12329         * Fires when the selected nodes change
12330         * @param {MultiSelectionModel} this
12331         * @param {Array} nodes Array of the selected nodes
12332         */
12333        "selectionchange" : true
12334    });
12335    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12336    
12337 };
12338
12339 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12340     init : function(tree){
12341         this.tree = tree;
12342         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12343         tree.on("click", this.onNodeClick, this);
12344     },
12345     
12346     onNodeClick : function(node, e){
12347         this.select(node, e, e.ctrlKey);
12348     },
12349     
12350     /**
12351      * Select a node.
12352      * @param {TreeNode} node The node to select
12353      * @param {EventObject} e (optional) An event associated with the selection
12354      * @param {Boolean} keepExisting True to retain existing selections
12355      * @return {TreeNode} The selected node
12356      */
12357     select : function(node, e, keepExisting){
12358         if(keepExisting !== true){
12359             this.clearSelections(true);
12360         }
12361         if(this.isSelected(node)){
12362             this.lastSelNode = node;
12363             return node;
12364         }
12365         this.selNodes.push(node);
12366         this.selMap[node.id] = node;
12367         this.lastSelNode = node;
12368         node.ui.onSelectedChange(true);
12369         this.fireEvent("selectionchange", this, this.selNodes);
12370         return node;
12371     },
12372     
12373     /**
12374      * Deselect a node.
12375      * @param {TreeNode} node The node to unselect
12376      */
12377     unselect : function(node){
12378         if(this.selMap[node.id]){
12379             node.ui.onSelectedChange(false);
12380             var sn = this.selNodes;
12381             var index = -1;
12382             if(sn.indexOf){
12383                 index = sn.indexOf(node);
12384             }else{
12385                 for(var i = 0, len = sn.length; i < len; i++){
12386                     if(sn[i] == node){
12387                         index = i;
12388                         break;
12389                     }
12390                 }
12391             }
12392             if(index != -1){
12393                 this.selNodes.splice(index, 1);
12394             }
12395             delete this.selMap[node.id];
12396             this.fireEvent("selectionchange", this, this.selNodes);
12397         }
12398     },
12399     
12400     /**
12401      * Clear all selections
12402      */
12403     clearSelections : function(suppressEvent){
12404         var sn = this.selNodes;
12405         if(sn.length > 0){
12406             for(var i = 0, len = sn.length; i < len; i++){
12407                 sn[i].ui.onSelectedChange(false);
12408             }
12409             this.selNodes = [];
12410             this.selMap = {};
12411             if(suppressEvent !== true){
12412                 this.fireEvent("selectionchange", this, this.selNodes);
12413             }
12414         }
12415     },
12416     
12417     /**
12418      * Returns true if the node is selected
12419      * @param {TreeNode} node The node to check
12420      * @return {Boolean}
12421      */
12422     isSelected : function(node){
12423         return this.selMap[node.id] ? true : false;  
12424     },
12425     
12426     /**
12427      * Returns an array of the selected nodes
12428      * @return {Array}
12429      */
12430     getSelectedNodes : function(){
12431         return this.selNodes;    
12432     },
12433
12434     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12435
12436     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12437
12438     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12439 });/*
12440  * Based on:
12441  * Ext JS Library 1.1.1
12442  * Copyright(c) 2006-2007, Ext JS, LLC.
12443  *
12444  * Originally Released Under LGPL - original licence link has changed is not relivant.
12445  *
12446  * Fork - LGPL
12447  * <script type="text/javascript">
12448  */
12449  
12450 /**
12451  * @class Roo.tree.TreeNode
12452  * @extends Roo.data.Node
12453  * @cfg {String} text The text for this node
12454  * @cfg {Boolean} expanded true to start the node expanded
12455  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12456  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12457  * @cfg {Boolean} disabled true to start the node disabled
12458  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12459  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12460  * @cfg {String} cls A css class to be added to the node
12461  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12462  * @cfg {String} href URL of the link used for the node (defaults to #)
12463  * @cfg {String} hrefTarget target frame for the link
12464  * @cfg {String} qtip An Ext QuickTip for the node
12465  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12466  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12467  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12468  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12469  * (defaults to undefined with no checkbox rendered)
12470  * @constructor
12471  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12472  */
12473 Roo.tree.TreeNode = function(attributes){
12474     attributes = attributes || {};
12475     if(typeof attributes == "string"){
12476         attributes = {text: attributes};
12477     }
12478     this.childrenRendered = false;
12479     this.rendered = false;
12480     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12481     this.expanded = attributes.expanded === true;
12482     this.isTarget = attributes.isTarget !== false;
12483     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12484     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12485
12486     /**
12487      * Read-only. The text for this node. To change it use setText().
12488      * @type String
12489      */
12490     this.text = attributes.text;
12491     /**
12492      * True if this node is disabled.
12493      * @type Boolean
12494      */
12495     this.disabled = attributes.disabled === true;
12496
12497     this.addEvents({
12498         /**
12499         * @event textchange
12500         * Fires when the text for this node is changed
12501         * @param {Node} this This node
12502         * @param {String} text The new text
12503         * @param {String} oldText The old text
12504         */
12505         "textchange" : true,
12506         /**
12507         * @event beforeexpand
12508         * Fires before this node is expanded, return false to cancel.
12509         * @param {Node} this This node
12510         * @param {Boolean} deep
12511         * @param {Boolean} anim
12512         */
12513         "beforeexpand" : true,
12514         /**
12515         * @event beforecollapse
12516         * Fires before this node is collapsed, return false to cancel.
12517         * @param {Node} this This node
12518         * @param {Boolean} deep
12519         * @param {Boolean} anim
12520         */
12521         "beforecollapse" : true,
12522         /**
12523         * @event expand
12524         * Fires when this node is expanded
12525         * @param {Node} this This node
12526         */
12527         "expand" : true,
12528         /**
12529         * @event disabledchange
12530         * Fires when the disabled status of this node changes
12531         * @param {Node} this This node
12532         * @param {Boolean} disabled
12533         */
12534         "disabledchange" : true,
12535         /**
12536         * @event collapse
12537         * Fires when this node is collapsed
12538         * @param {Node} this This node
12539         */
12540         "collapse" : true,
12541         /**
12542         * @event beforeclick
12543         * Fires before click processing. Return false to cancel the default action.
12544         * @param {Node} this This node
12545         * @param {Roo.EventObject} e The event object
12546         */
12547         "beforeclick":true,
12548         /**
12549         * @event checkchange
12550         * Fires when a node with a checkbox's checked property changes
12551         * @param {Node} this This node
12552         * @param {Boolean} checked
12553         */
12554         "checkchange":true,
12555         /**
12556         * @event click
12557         * Fires when this node is clicked
12558         * @param {Node} this This node
12559         * @param {Roo.EventObject} e The event object
12560         */
12561         "click":true,
12562         /**
12563         * @event dblclick
12564         * Fires when this node is double clicked
12565         * @param {Node} this This node
12566         * @param {Roo.EventObject} e The event object
12567         */
12568         "dblclick":true,
12569         /**
12570         * @event contextmenu
12571         * Fires when this node is right clicked
12572         * @param {Node} this This node
12573         * @param {Roo.EventObject} e The event object
12574         */
12575         "contextmenu":true,
12576         /**
12577         * @event beforechildrenrendered
12578         * Fires right before the child nodes for this node are rendered
12579         * @param {Node} this This node
12580         */
12581         "beforechildrenrendered":true
12582     });
12583
12584     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12585
12586     /**
12587      * Read-only. The UI for this node
12588      * @type TreeNodeUI
12589      */
12590     this.ui = new uiClass(this);
12591     
12592     // finally support items[]
12593     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12594         return;
12595     }
12596     
12597     
12598     Roo.each(this.attributes.items, function(c) {
12599         this.appendChild(Roo.factory(c,Roo.Tree));
12600     }, this);
12601     delete this.attributes.items;
12602     
12603     
12604     
12605 };
12606 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12607     preventHScroll: true,
12608     /**
12609      * Returns true if this node is expanded
12610      * @return {Boolean}
12611      */
12612     isExpanded : function(){
12613         return this.expanded;
12614     },
12615
12616     /**
12617      * Returns the UI object for this node
12618      * @return {TreeNodeUI}
12619      */
12620     getUI : function(){
12621         return this.ui;
12622     },
12623
12624     // private override
12625     setFirstChild : function(node){
12626         var of = this.firstChild;
12627         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12628         if(this.childrenRendered && of && node != of){
12629             of.renderIndent(true, true);
12630         }
12631         if(this.rendered){
12632             this.renderIndent(true, true);
12633         }
12634     },
12635
12636     // private override
12637     setLastChild : function(node){
12638         var ol = this.lastChild;
12639         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12640         if(this.childrenRendered && ol && node != ol){
12641             ol.renderIndent(true, true);
12642         }
12643         if(this.rendered){
12644             this.renderIndent(true, true);
12645         }
12646     },
12647
12648     // these methods are overridden to provide lazy rendering support
12649     // private override
12650     appendChild : function()
12651     {
12652         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12653         if(node && this.childrenRendered){
12654             node.render();
12655         }
12656         this.ui.updateExpandIcon();
12657         return node;
12658     },
12659
12660     // private override
12661     removeChild : function(node){
12662         this.ownerTree.getSelectionModel().unselect(node);
12663         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12664         // if it's been rendered remove dom node
12665         if(this.childrenRendered){
12666             node.ui.remove();
12667         }
12668         if(this.childNodes.length < 1){
12669             this.collapse(false, false);
12670         }else{
12671             this.ui.updateExpandIcon();
12672         }
12673         if(!this.firstChild) {
12674             this.childrenRendered = false;
12675         }
12676         return node;
12677     },
12678
12679     // private override
12680     insertBefore : function(node, refNode){
12681         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12682         if(newNode && refNode && this.childrenRendered){
12683             node.render();
12684         }
12685         this.ui.updateExpandIcon();
12686         return newNode;
12687     },
12688
12689     /**
12690      * Sets the text for this node
12691      * @param {String} text
12692      */
12693     setText : function(text){
12694         var oldText = this.text;
12695         this.text = text;
12696         this.attributes.text = text;
12697         if(this.rendered){ // event without subscribing
12698             this.ui.onTextChange(this, text, oldText);
12699         }
12700         this.fireEvent("textchange", this, text, oldText);
12701     },
12702
12703     /**
12704      * Triggers selection of this node
12705      */
12706     select : function(){
12707         this.getOwnerTree().getSelectionModel().select(this);
12708     },
12709
12710     /**
12711      * Triggers deselection of this node
12712      */
12713     unselect : function(){
12714         this.getOwnerTree().getSelectionModel().unselect(this);
12715     },
12716
12717     /**
12718      * Returns true if this node is selected
12719      * @return {Boolean}
12720      */
12721     isSelected : function(){
12722         return this.getOwnerTree().getSelectionModel().isSelected(this);
12723     },
12724
12725     /**
12726      * Expand this node.
12727      * @param {Boolean} deep (optional) True to expand all children as well
12728      * @param {Boolean} anim (optional) false to cancel the default animation
12729      * @param {Function} callback (optional) A callback to be called when
12730      * expanding this node completes (does not wait for deep expand to complete).
12731      * Called with 1 parameter, this node.
12732      */
12733     expand : function(deep, anim, callback){
12734         if(!this.expanded){
12735             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12736                 return;
12737             }
12738             if(!this.childrenRendered){
12739                 this.renderChildren();
12740             }
12741             this.expanded = true;
12742             
12743             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12744                 this.ui.animExpand(function(){
12745                     this.fireEvent("expand", this);
12746                     if(typeof callback == "function"){
12747                         callback(this);
12748                     }
12749                     if(deep === true){
12750                         this.expandChildNodes(true);
12751                     }
12752                 }.createDelegate(this));
12753                 return;
12754             }else{
12755                 this.ui.expand();
12756                 this.fireEvent("expand", this);
12757                 if(typeof callback == "function"){
12758                     callback(this);
12759                 }
12760             }
12761         }else{
12762            if(typeof callback == "function"){
12763                callback(this);
12764            }
12765         }
12766         if(deep === true){
12767             this.expandChildNodes(true);
12768         }
12769     },
12770
12771     isHiddenRoot : function(){
12772         return this.isRoot && !this.getOwnerTree().rootVisible;
12773     },
12774
12775     /**
12776      * Collapse this node.
12777      * @param {Boolean} deep (optional) True to collapse all children as well
12778      * @param {Boolean} anim (optional) false to cancel the default animation
12779      */
12780     collapse : function(deep, anim){
12781         if(this.expanded && !this.isHiddenRoot()){
12782             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12783                 return;
12784             }
12785             this.expanded = false;
12786             if((this.getOwnerTree().animate && anim !== false) || anim){
12787                 this.ui.animCollapse(function(){
12788                     this.fireEvent("collapse", this);
12789                     if(deep === true){
12790                         this.collapseChildNodes(true);
12791                     }
12792                 }.createDelegate(this));
12793                 return;
12794             }else{
12795                 this.ui.collapse();
12796                 this.fireEvent("collapse", this);
12797             }
12798         }
12799         if(deep === true){
12800             var cs = this.childNodes;
12801             for(var i = 0, len = cs.length; i < len; i++) {
12802                 cs[i].collapse(true, false);
12803             }
12804         }
12805     },
12806
12807     // private
12808     delayedExpand : function(delay){
12809         if(!this.expandProcId){
12810             this.expandProcId = this.expand.defer(delay, this);
12811         }
12812     },
12813
12814     // private
12815     cancelExpand : function(){
12816         if(this.expandProcId){
12817             clearTimeout(this.expandProcId);
12818         }
12819         this.expandProcId = false;
12820     },
12821
12822     /**
12823      * Toggles expanded/collapsed state of the node
12824      */
12825     toggle : function(){
12826         if(this.expanded){
12827             this.collapse();
12828         }else{
12829             this.expand();
12830         }
12831     },
12832
12833     /**
12834      * Ensures all parent nodes are expanded
12835      */
12836     ensureVisible : function(callback){
12837         var tree = this.getOwnerTree();
12838         tree.expandPath(this.parentNode.getPath(), false, function(){
12839             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12840             Roo.callback(callback);
12841         }.createDelegate(this));
12842     },
12843
12844     /**
12845      * Expand all child nodes
12846      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12847      */
12848     expandChildNodes : function(deep){
12849         var cs = this.childNodes;
12850         for(var i = 0, len = cs.length; i < len; i++) {
12851                 cs[i].expand(deep);
12852         }
12853     },
12854
12855     /**
12856      * Collapse all child nodes
12857      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12858      */
12859     collapseChildNodes : function(deep){
12860         var cs = this.childNodes;
12861         for(var i = 0, len = cs.length; i < len; i++) {
12862                 cs[i].collapse(deep);
12863         }
12864     },
12865
12866     /**
12867      * Disables this node
12868      */
12869     disable : function(){
12870         this.disabled = true;
12871         this.unselect();
12872         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12873             this.ui.onDisableChange(this, true);
12874         }
12875         this.fireEvent("disabledchange", this, true);
12876     },
12877
12878     /**
12879      * Enables this node
12880      */
12881     enable : function(){
12882         this.disabled = false;
12883         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12884             this.ui.onDisableChange(this, false);
12885         }
12886         this.fireEvent("disabledchange", this, false);
12887     },
12888
12889     // private
12890     renderChildren : function(suppressEvent){
12891         if(suppressEvent !== false){
12892             this.fireEvent("beforechildrenrendered", this);
12893         }
12894         var cs = this.childNodes;
12895         for(var i = 0, len = cs.length; i < len; i++){
12896             cs[i].render(true);
12897         }
12898         this.childrenRendered = true;
12899     },
12900
12901     // private
12902     sort : function(fn, scope){
12903         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12904         if(this.childrenRendered){
12905             var cs = this.childNodes;
12906             for(var i = 0, len = cs.length; i < len; i++){
12907                 cs[i].render(true);
12908             }
12909         }
12910     },
12911
12912     // private
12913     render : function(bulkRender){
12914         this.ui.render(bulkRender);
12915         if(!this.rendered){
12916             this.rendered = true;
12917             if(this.expanded){
12918                 this.expanded = false;
12919                 this.expand(false, false);
12920             }
12921         }
12922     },
12923
12924     // private
12925     renderIndent : function(deep, refresh){
12926         if(refresh){
12927             this.ui.childIndent = null;
12928         }
12929         this.ui.renderIndent();
12930         if(deep === true && this.childrenRendered){
12931             var cs = this.childNodes;
12932             for(var i = 0, len = cs.length; i < len; i++){
12933                 cs[i].renderIndent(true, refresh);
12934             }
12935         }
12936     }
12937 });/*
12938  * Based on:
12939  * Ext JS Library 1.1.1
12940  * Copyright(c) 2006-2007, Ext JS, LLC.
12941  *
12942  * Originally Released Under LGPL - original licence link has changed is not relivant.
12943  *
12944  * Fork - LGPL
12945  * <script type="text/javascript">
12946  */
12947  
12948 /**
12949  * @class Roo.tree.AsyncTreeNode
12950  * @extends Roo.tree.TreeNode
12951  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12952  * @constructor
12953  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12954  */
12955  Roo.tree.AsyncTreeNode = function(config){
12956     this.loaded = false;
12957     this.loading = false;
12958     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12959     /**
12960     * @event beforeload
12961     * Fires before this node is loaded, return false to cancel
12962     * @param {Node} this This node
12963     */
12964     this.addEvents({'beforeload':true, 'load': true});
12965     /**
12966     * @event load
12967     * Fires when this node is loaded
12968     * @param {Node} this This node
12969     */
12970     /**
12971      * The loader used by this node (defaults to using the tree's defined loader)
12972      * @type TreeLoader
12973      * @property loader
12974      */
12975 };
12976 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12977     expand : function(deep, anim, callback){
12978         if(this.loading){ // if an async load is already running, waiting til it's done
12979             var timer;
12980             var f = function(){
12981                 if(!this.loading){ // done loading
12982                     clearInterval(timer);
12983                     this.expand(deep, anim, callback);
12984                 }
12985             }.createDelegate(this);
12986             timer = setInterval(f, 200);
12987             return;
12988         }
12989         if(!this.loaded){
12990             if(this.fireEvent("beforeload", this) === false){
12991                 return;
12992             }
12993             this.loading = true;
12994             this.ui.beforeLoad(this);
12995             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12996             if(loader){
12997                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12998                 return;
12999             }
13000         }
13001         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
13002     },
13003     
13004     /**
13005      * Returns true if this node is currently loading
13006      * @return {Boolean}
13007      */
13008     isLoading : function(){
13009         return this.loading;  
13010     },
13011     
13012     loadComplete : function(deep, anim, callback){
13013         this.loading = false;
13014         this.loaded = true;
13015         this.ui.afterLoad(this);
13016         this.fireEvent("load", this);
13017         this.expand(deep, anim, callback);
13018     },
13019     
13020     /**
13021      * Returns true if this node has been loaded
13022      * @return {Boolean}
13023      */
13024     isLoaded : function(){
13025         return this.loaded;
13026     },
13027     
13028     hasChildNodes : function(){
13029         if(!this.isLeaf() && !this.loaded){
13030             return true;
13031         }else{
13032             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13033         }
13034     },
13035
13036     /**
13037      * Trigger a reload for this node
13038      * @param {Function} callback
13039      */
13040     reload : function(callback){
13041         this.collapse(false, false);
13042         while(this.firstChild){
13043             this.removeChild(this.firstChild);
13044         }
13045         this.childrenRendered = false;
13046         this.loaded = false;
13047         if(this.isHiddenRoot()){
13048             this.expanded = false;
13049         }
13050         this.expand(false, false, callback);
13051     }
13052 });/*
13053  * Based on:
13054  * Ext JS Library 1.1.1
13055  * Copyright(c) 2006-2007, Ext JS, LLC.
13056  *
13057  * Originally Released Under LGPL - original licence link has changed is not relivant.
13058  *
13059  * Fork - LGPL
13060  * <script type="text/javascript">
13061  */
13062  
13063 /**
13064  * @class Roo.tree.TreeNodeUI
13065  * @constructor
13066  * @param {Object} node The node to render
13067  * The TreeNode UI implementation is separate from the
13068  * tree implementation. Unless you are customizing the tree UI,
13069  * you should never have to use this directly.
13070  */
13071 Roo.tree.TreeNodeUI = function(node){
13072     this.node = node;
13073     this.rendered = false;
13074     this.animating = false;
13075     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13076 };
13077
13078 Roo.tree.TreeNodeUI.prototype = {
13079     removeChild : function(node){
13080         if(this.rendered){
13081             this.ctNode.removeChild(node.ui.getEl());
13082         }
13083     },
13084
13085     beforeLoad : function(){
13086          this.addClass("x-tree-node-loading");
13087     },
13088
13089     afterLoad : function(){
13090          this.removeClass("x-tree-node-loading");
13091     },
13092
13093     onTextChange : function(node, text, oldText){
13094         if(this.rendered){
13095             this.textNode.innerHTML = text;
13096         }
13097     },
13098
13099     onDisableChange : function(node, state){
13100         this.disabled = state;
13101         if(state){
13102             this.addClass("x-tree-node-disabled");
13103         }else{
13104             this.removeClass("x-tree-node-disabled");
13105         }
13106     },
13107
13108     onSelectedChange : function(state){
13109         if(state){
13110             this.focus();
13111             this.addClass("x-tree-selected");
13112         }else{
13113             //this.blur();
13114             this.removeClass("x-tree-selected");
13115         }
13116     },
13117
13118     onMove : function(tree, node, oldParent, newParent, index, refNode){
13119         this.childIndent = null;
13120         if(this.rendered){
13121             var targetNode = newParent.ui.getContainer();
13122             if(!targetNode){//target not rendered
13123                 this.holder = document.createElement("div");
13124                 this.holder.appendChild(this.wrap);
13125                 return;
13126             }
13127             var insertBefore = refNode ? refNode.ui.getEl() : null;
13128             if(insertBefore){
13129                 targetNode.insertBefore(this.wrap, insertBefore);
13130             }else{
13131                 targetNode.appendChild(this.wrap);
13132             }
13133             this.node.renderIndent(true);
13134         }
13135     },
13136
13137     addClass : function(cls){
13138         if(this.elNode){
13139             Roo.fly(this.elNode).addClass(cls);
13140         }
13141     },
13142
13143     removeClass : function(cls){
13144         if(this.elNode){
13145             Roo.fly(this.elNode).removeClass(cls);
13146         }
13147     },
13148
13149     remove : function(){
13150         if(this.rendered){
13151             this.holder = document.createElement("div");
13152             this.holder.appendChild(this.wrap);
13153         }
13154     },
13155
13156     fireEvent : function(){
13157         return this.node.fireEvent.apply(this.node, arguments);
13158     },
13159
13160     initEvents : function(){
13161         this.node.on("move", this.onMove, this);
13162         var E = Roo.EventManager;
13163         var a = this.anchor;
13164
13165         var el = Roo.fly(a, '_treeui');
13166
13167         if(Roo.isOpera){ // opera render bug ignores the CSS
13168             el.setStyle("text-decoration", "none");
13169         }
13170
13171         el.on("click", this.onClick, this);
13172         el.on("dblclick", this.onDblClick, this);
13173
13174         if(this.checkbox){
13175             Roo.EventManager.on(this.checkbox,
13176                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13177         }
13178
13179         el.on("contextmenu", this.onContextMenu, this);
13180
13181         var icon = Roo.fly(this.iconNode);
13182         icon.on("click", this.onClick, this);
13183         icon.on("dblclick", this.onDblClick, this);
13184         icon.on("contextmenu", this.onContextMenu, this);
13185         E.on(this.ecNode, "click", this.ecClick, this, true);
13186
13187         if(this.node.disabled){
13188             this.addClass("x-tree-node-disabled");
13189         }
13190         if(this.node.hidden){
13191             this.addClass("x-tree-node-disabled");
13192         }
13193         var ot = this.node.getOwnerTree();
13194         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13195         if(dd && (!this.node.isRoot || ot.rootVisible)){
13196             Roo.dd.Registry.register(this.elNode, {
13197                 node: this.node,
13198                 handles: this.getDDHandles(),
13199                 isHandle: false
13200             });
13201         }
13202     },
13203
13204     getDDHandles : function(){
13205         return [this.iconNode, this.textNode];
13206     },
13207
13208     hide : function(){
13209         if(this.rendered){
13210             this.wrap.style.display = "none";
13211         }
13212     },
13213
13214     show : function(){
13215         if(this.rendered){
13216             this.wrap.style.display = "";
13217         }
13218     },
13219
13220     onContextMenu : function(e){
13221         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13222             e.preventDefault();
13223             this.focus();
13224             this.fireEvent("contextmenu", this.node, e);
13225         }
13226     },
13227
13228     onClick : function(e){
13229         if(this.dropping){
13230             e.stopEvent();
13231             return;
13232         }
13233         if(this.fireEvent("beforeclick", this.node, e) !== false){
13234             if(!this.disabled && this.node.attributes.href){
13235                 this.fireEvent("click", this.node, e);
13236                 return;
13237             }
13238             e.preventDefault();
13239             if(this.disabled){
13240                 return;
13241             }
13242
13243             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13244                 this.node.toggle();
13245             }
13246
13247             this.fireEvent("click", this.node, e);
13248         }else{
13249             e.stopEvent();
13250         }
13251     },
13252
13253     onDblClick : function(e){
13254         e.preventDefault();
13255         if(this.disabled){
13256             return;
13257         }
13258         if(this.checkbox){
13259             this.toggleCheck();
13260         }
13261         if(!this.animating && this.node.hasChildNodes()){
13262             this.node.toggle();
13263         }
13264         this.fireEvent("dblclick", this.node, e);
13265     },
13266
13267     onCheckChange : function(){
13268         var checked = this.checkbox.checked;
13269         this.node.attributes.checked = checked;
13270         this.fireEvent('checkchange', this.node, checked);
13271     },
13272
13273     ecClick : function(e){
13274         if(!this.animating && this.node.hasChildNodes()){
13275             this.node.toggle();
13276         }
13277     },
13278
13279     startDrop : function(){
13280         this.dropping = true;
13281     },
13282
13283     // delayed drop so the click event doesn't get fired on a drop
13284     endDrop : function(){
13285        setTimeout(function(){
13286            this.dropping = false;
13287        }.createDelegate(this), 50);
13288     },
13289
13290     expand : function(){
13291         this.updateExpandIcon();
13292         this.ctNode.style.display = "";
13293     },
13294
13295     focus : function(){
13296         if(!this.node.preventHScroll){
13297             try{this.anchor.focus();
13298             }catch(e){}
13299         }else if(!Roo.isIE){
13300             try{
13301                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13302                 var l = noscroll.scrollLeft;
13303                 this.anchor.focus();
13304                 noscroll.scrollLeft = l;
13305             }catch(e){}
13306         }
13307     },
13308
13309     toggleCheck : function(value){
13310         var cb = this.checkbox;
13311         if(cb){
13312             cb.checked = (value === undefined ? !cb.checked : value);
13313         }
13314     },
13315
13316     blur : function(){
13317         try{
13318             this.anchor.blur();
13319         }catch(e){}
13320     },
13321
13322     animExpand : function(callback){
13323         var ct = Roo.get(this.ctNode);
13324         ct.stopFx();
13325         if(!this.node.hasChildNodes()){
13326             this.updateExpandIcon();
13327             this.ctNode.style.display = "";
13328             Roo.callback(callback);
13329             return;
13330         }
13331         this.animating = true;
13332         this.updateExpandIcon();
13333
13334         ct.slideIn('t', {
13335            callback : function(){
13336                this.animating = false;
13337                Roo.callback(callback);
13338             },
13339             scope: this,
13340             duration: this.node.ownerTree.duration || .25
13341         });
13342     },
13343
13344     highlight : function(){
13345         var tree = this.node.getOwnerTree();
13346         Roo.fly(this.wrap).highlight(
13347             tree.hlColor || "C3DAF9",
13348             {endColor: tree.hlBaseColor}
13349         );
13350     },
13351
13352     collapse : function(){
13353         this.updateExpandIcon();
13354         this.ctNode.style.display = "none";
13355     },
13356
13357     animCollapse : function(callback){
13358         var ct = Roo.get(this.ctNode);
13359         ct.enableDisplayMode('block');
13360         ct.stopFx();
13361
13362         this.animating = true;
13363         this.updateExpandIcon();
13364
13365         ct.slideOut('t', {
13366             callback : function(){
13367                this.animating = false;
13368                Roo.callback(callback);
13369             },
13370             scope: this,
13371             duration: this.node.ownerTree.duration || .25
13372         });
13373     },
13374
13375     getContainer : function(){
13376         return this.ctNode;
13377     },
13378
13379     getEl : function(){
13380         return this.wrap;
13381     },
13382
13383     appendDDGhost : function(ghostNode){
13384         ghostNode.appendChild(this.elNode.cloneNode(true));
13385     },
13386
13387     getDDRepairXY : function(){
13388         return Roo.lib.Dom.getXY(this.iconNode);
13389     },
13390
13391     onRender : function(){
13392         this.render();
13393     },
13394
13395     render : function(bulkRender){
13396         var n = this.node, a = n.attributes;
13397         var targetNode = n.parentNode ?
13398               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13399
13400         if(!this.rendered){
13401             this.rendered = true;
13402
13403             this.renderElements(n, a, targetNode, bulkRender);
13404
13405             if(a.qtip){
13406                if(this.textNode.setAttributeNS){
13407                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13408                    if(a.qtipTitle){
13409                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13410                    }
13411                }else{
13412                    this.textNode.setAttribute("ext:qtip", a.qtip);
13413                    if(a.qtipTitle){
13414                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13415                    }
13416                }
13417             }else if(a.qtipCfg){
13418                 a.qtipCfg.target = Roo.id(this.textNode);
13419                 Roo.QuickTips.register(a.qtipCfg);
13420             }
13421             this.initEvents();
13422             if(!this.node.expanded){
13423                 this.updateExpandIcon();
13424             }
13425         }else{
13426             if(bulkRender === true) {
13427                 targetNode.appendChild(this.wrap);
13428             }
13429         }
13430     },
13431
13432     renderElements : function(n, a, targetNode, bulkRender)
13433     {
13434         // add some indent caching, this helps performance when rendering a large tree
13435         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13436         var t = n.getOwnerTree();
13437         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13438         if (typeof(n.attributes.html) != 'undefined') {
13439             txt = n.attributes.html;
13440         }
13441         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13442         var cb = typeof a.checked == 'boolean';
13443         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13444         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13445             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13446             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13447             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13448             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13449             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13450              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13451                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13452             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13453             "</li>"];
13454
13455         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13456             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13457                                 n.nextSibling.ui.getEl(), buf.join(""));
13458         }else{
13459             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13460         }
13461
13462         this.elNode = this.wrap.childNodes[0];
13463         this.ctNode = this.wrap.childNodes[1];
13464         var cs = this.elNode.childNodes;
13465         this.indentNode = cs[0];
13466         this.ecNode = cs[1];
13467         this.iconNode = cs[2];
13468         var index = 3;
13469         if(cb){
13470             this.checkbox = cs[3];
13471             index++;
13472         }
13473         this.anchor = cs[index];
13474         this.textNode = cs[index].firstChild;
13475     },
13476
13477     getAnchor : function(){
13478         return this.anchor;
13479     },
13480
13481     getTextEl : function(){
13482         return this.textNode;
13483     },
13484
13485     getIconEl : function(){
13486         return this.iconNode;
13487     },
13488
13489     isChecked : function(){
13490         return this.checkbox ? this.checkbox.checked : false;
13491     },
13492
13493     updateExpandIcon : function(){
13494         if(this.rendered){
13495             var n = this.node, c1, c2;
13496             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13497             var hasChild = n.hasChildNodes();
13498             if(hasChild){
13499                 if(n.expanded){
13500                     cls += "-minus";
13501                     c1 = "x-tree-node-collapsed";
13502                     c2 = "x-tree-node-expanded";
13503                 }else{
13504                     cls += "-plus";
13505                     c1 = "x-tree-node-expanded";
13506                     c2 = "x-tree-node-collapsed";
13507                 }
13508                 if(this.wasLeaf){
13509                     this.removeClass("x-tree-node-leaf");
13510                     this.wasLeaf = false;
13511                 }
13512                 if(this.c1 != c1 || this.c2 != c2){
13513                     Roo.fly(this.elNode).replaceClass(c1, c2);
13514                     this.c1 = c1; this.c2 = c2;
13515                 }
13516             }else{
13517                 // this changes non-leafs into leafs if they have no children.
13518                 // it's not very rational behaviour..
13519                 
13520                 if(!this.wasLeaf && this.node.leaf){
13521                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13522                     delete this.c1;
13523                     delete this.c2;
13524                     this.wasLeaf = true;
13525                 }
13526             }
13527             var ecc = "x-tree-ec-icon "+cls;
13528             if(this.ecc != ecc){
13529                 this.ecNode.className = ecc;
13530                 this.ecc = ecc;
13531             }
13532         }
13533     },
13534
13535     getChildIndent : function(){
13536         if(!this.childIndent){
13537             var buf = [];
13538             var p = this.node;
13539             while(p){
13540                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13541                     if(!p.isLast()) {
13542                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13543                     } else {
13544                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13545                     }
13546                 }
13547                 p = p.parentNode;
13548             }
13549             this.childIndent = buf.join("");
13550         }
13551         return this.childIndent;
13552     },
13553
13554     renderIndent : function(){
13555         if(this.rendered){
13556             var indent = "";
13557             var p = this.node.parentNode;
13558             if(p){
13559                 indent = p.ui.getChildIndent();
13560             }
13561             if(this.indentMarkup != indent){ // don't rerender if not required
13562                 this.indentNode.innerHTML = indent;
13563                 this.indentMarkup = indent;
13564             }
13565             this.updateExpandIcon();
13566         }
13567     }
13568 };
13569
13570 Roo.tree.RootTreeNodeUI = function(){
13571     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13572 };
13573 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13574     render : function(){
13575         if(!this.rendered){
13576             var targetNode = this.node.ownerTree.innerCt.dom;
13577             this.node.expanded = true;
13578             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13579             this.wrap = this.ctNode = targetNode.firstChild;
13580         }
13581     },
13582     collapse : function(){
13583     },
13584     expand : function(){
13585     }
13586 });/*
13587  * Based on:
13588  * Ext JS Library 1.1.1
13589  * Copyright(c) 2006-2007, Ext JS, LLC.
13590  *
13591  * Originally Released Under LGPL - original licence link has changed is not relivant.
13592  *
13593  * Fork - LGPL
13594  * <script type="text/javascript">
13595  */
13596 /**
13597  * @class Roo.tree.TreeLoader
13598  * @extends Roo.util.Observable
13599  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13600  * nodes from a specified URL. The response must be a javascript Array definition
13601  * who's elements are node definition objects. eg:
13602  * <pre><code>
13603 {  success : true,
13604    data :      [
13605    
13606     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13607     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13608     ]
13609 }
13610
13611
13612 </code></pre>
13613  * <br><br>
13614  * The old style respose with just an array is still supported, but not recommended.
13615  * <br><br>
13616  *
13617  * A server request is sent, and child nodes are loaded only when a node is expanded.
13618  * The loading node's id is passed to the server under the parameter name "node" to
13619  * enable the server to produce the correct child nodes.
13620  * <br><br>
13621  * To pass extra parameters, an event handler may be attached to the "beforeload"
13622  * event, and the parameters specified in the TreeLoader's baseParams property:
13623  * <pre><code>
13624     myTreeLoader.on("beforeload", function(treeLoader, node) {
13625         this.baseParams.category = node.attributes.category;
13626     }, this);
13627     
13628 </code></pre>
13629  *
13630  * This would pass an HTTP parameter called "category" to the server containing
13631  * the value of the Node's "category" attribute.
13632  * @constructor
13633  * Creates a new Treeloader.
13634  * @param {Object} config A config object containing config properties.
13635  */
13636 Roo.tree.TreeLoader = function(config){
13637     this.baseParams = {};
13638     this.requestMethod = "POST";
13639     Roo.apply(this, config);
13640
13641     this.addEvents({
13642     
13643         /**
13644          * @event beforeload
13645          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13646          * @param {Object} This TreeLoader object.
13647          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13648          * @param {Object} callback The callback function specified in the {@link #load} call.
13649          */
13650         beforeload : true,
13651         /**
13652          * @event load
13653          * Fires when the node has been successfuly loaded.
13654          * @param {Object} This TreeLoader object.
13655          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13656          * @param {Object} response The response object containing the data from the server.
13657          */
13658         load : true,
13659         /**
13660          * @event loadexception
13661          * Fires if the network request failed.
13662          * @param {Object} This TreeLoader object.
13663          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13664          * @param {Object} response The response object containing the data from the server.
13665          */
13666         loadexception : true,
13667         /**
13668          * @event create
13669          * Fires before a node is created, enabling you to return custom Node types 
13670          * @param {Object} This TreeLoader object.
13671          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13672          */
13673         create : true
13674     });
13675
13676     Roo.tree.TreeLoader.superclass.constructor.call(this);
13677 };
13678
13679 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13680     /**
13681     * @cfg {String} dataUrl The URL from which to request a Json string which
13682     * specifies an array of node definition object representing the child nodes
13683     * to be loaded.
13684     */
13685     /**
13686     * @cfg {String} requestMethod either GET or POST
13687     * defaults to POST (due to BC)
13688     * to be loaded.
13689     */
13690     /**
13691     * @cfg {Object} baseParams (optional) An object containing properties which
13692     * specify HTTP parameters to be passed to each request for child nodes.
13693     */
13694     /**
13695     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13696     * created by this loader. If the attributes sent by the server have an attribute in this object,
13697     * they take priority.
13698     */
13699     /**
13700     * @cfg {Object} uiProviders (optional) An object containing properties which
13701     * 
13702     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13703     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13704     * <i>uiProvider</i> attribute of a returned child node is a string rather
13705     * than a reference to a TreeNodeUI implementation, this that string value
13706     * is used as a property name in the uiProviders object. You can define the provider named
13707     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13708     */
13709     uiProviders : {},
13710
13711     /**
13712     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13713     * child nodes before loading.
13714     */
13715     clearOnLoad : true,
13716
13717     /**
13718     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13719     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13720     * Grid query { data : [ .....] }
13721     */
13722     
13723     root : false,
13724      /**
13725     * @cfg {String} queryParam (optional) 
13726     * Name of the query as it will be passed on the querystring (defaults to 'node')
13727     * eg. the request will be ?node=[id]
13728     */
13729     
13730     
13731     queryParam: false,
13732     
13733     /**
13734      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13735      * This is called automatically when a node is expanded, but may be used to reload
13736      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13737      * @param {Roo.tree.TreeNode} node
13738      * @param {Function} callback
13739      */
13740     load : function(node, callback){
13741         if(this.clearOnLoad){
13742             while(node.firstChild){
13743                 node.removeChild(node.firstChild);
13744             }
13745         }
13746         if(node.attributes.children){ // preloaded json children
13747             var cs = node.attributes.children;
13748             for(var i = 0, len = cs.length; i < len; i++){
13749                 node.appendChild(this.createNode(cs[i]));
13750             }
13751             if(typeof callback == "function"){
13752                 callback();
13753             }
13754         }else if(this.dataUrl){
13755             this.requestData(node, callback);
13756         }
13757     },
13758
13759     getParams: function(node){
13760         var buf = [], bp = this.baseParams;
13761         for(var key in bp){
13762             if(typeof bp[key] != "function"){
13763                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13764             }
13765         }
13766         var n = this.queryParam === false ? 'node' : this.queryParam;
13767         buf.push(n + "=", encodeURIComponent(node.id));
13768         return buf.join("");
13769     },
13770
13771     requestData : function(node, callback){
13772         if(this.fireEvent("beforeload", this, node, callback) !== false){
13773             this.transId = Roo.Ajax.request({
13774                 method:this.requestMethod,
13775                 url: this.dataUrl||this.url,
13776                 success: this.handleResponse,
13777                 failure: this.handleFailure,
13778                 scope: this,
13779                 argument: {callback: callback, node: node},
13780                 params: this.getParams(node)
13781             });
13782         }else{
13783             // if the load is cancelled, make sure we notify
13784             // the node that we are done
13785             if(typeof callback == "function"){
13786                 callback();
13787             }
13788         }
13789     },
13790
13791     isLoading : function(){
13792         return this.transId ? true : false;
13793     },
13794
13795     abort : function(){
13796         if(this.isLoading()){
13797             Roo.Ajax.abort(this.transId);
13798         }
13799     },
13800
13801     // private
13802     createNode : function(attr)
13803     {
13804         // apply baseAttrs, nice idea Corey!
13805         if(this.baseAttrs){
13806             Roo.applyIf(attr, this.baseAttrs);
13807         }
13808         if(this.applyLoader !== false){
13809             attr.loader = this;
13810         }
13811         // uiProvider = depreciated..
13812         
13813         if(typeof(attr.uiProvider) == 'string'){
13814            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13815                 /**  eval:var:attr */ eval(attr.uiProvider);
13816         }
13817         if(typeof(this.uiProviders['default']) != 'undefined') {
13818             attr.uiProvider = this.uiProviders['default'];
13819         }
13820         
13821         this.fireEvent('create', this, attr);
13822         
13823         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13824         return(attr.leaf ?
13825                         new Roo.tree.TreeNode(attr) :
13826                         new Roo.tree.AsyncTreeNode(attr));
13827     },
13828
13829     processResponse : function(response, node, callback)
13830     {
13831         var json = response.responseText;
13832         try {
13833             
13834             var o = Roo.decode(json);
13835             
13836             if (this.root === false && typeof(o.success) != undefined) {
13837                 this.root = 'data'; // the default behaviour for list like data..
13838                 }
13839                 
13840             if (this.root !== false &&  !o.success) {
13841                 // it's a failure condition.
13842                 var a = response.argument;
13843                 this.fireEvent("loadexception", this, a.node, response);
13844                 Roo.log("Load failed - should have a handler really");
13845                 return;
13846             }
13847             
13848             
13849             
13850             if (this.root !== false) {
13851                  o = o[this.root];
13852             }
13853             
13854             for(var i = 0, len = o.length; i < len; i++){
13855                 var n = this.createNode(o[i]);
13856                 if(n){
13857                     node.appendChild(n);
13858                 }
13859             }
13860             if(typeof callback == "function"){
13861                 callback(this, node);
13862             }
13863         }catch(e){
13864             this.handleFailure(response);
13865         }
13866     },
13867
13868     handleResponse : function(response){
13869         this.transId = false;
13870         var a = response.argument;
13871         this.processResponse(response, a.node, a.callback);
13872         this.fireEvent("load", this, a.node, response);
13873     },
13874
13875     handleFailure : function(response)
13876     {
13877         // should handle failure better..
13878         this.transId = false;
13879         var a = response.argument;
13880         this.fireEvent("loadexception", this, a.node, response);
13881         if(typeof a.callback == "function"){
13882             a.callback(this, a.node);
13883         }
13884     }
13885 });/*
13886  * Based on:
13887  * Ext JS Library 1.1.1
13888  * Copyright(c) 2006-2007, Ext JS, LLC.
13889  *
13890  * Originally Released Under LGPL - original licence link has changed is not relivant.
13891  *
13892  * Fork - LGPL
13893  * <script type="text/javascript">
13894  */
13895
13896 /**
13897 * @class Roo.tree.TreeFilter
13898 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13899 * @param {TreePanel} tree
13900 * @param {Object} config (optional)
13901  */
13902 Roo.tree.TreeFilter = function(tree, config){
13903     this.tree = tree;
13904     this.filtered = {};
13905     Roo.apply(this, config);
13906 };
13907
13908 Roo.tree.TreeFilter.prototype = {
13909     clearBlank:false,
13910     reverse:false,
13911     autoClear:false,
13912     remove:false,
13913
13914      /**
13915      * Filter the data by a specific attribute.
13916      * @param {String/RegExp} value Either string that the attribute value
13917      * should start with or a RegExp to test against the attribute
13918      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13919      * @param {TreeNode} startNode (optional) The node to start the filter at.
13920      */
13921     filter : function(value, attr, startNode){
13922         attr = attr || "text";
13923         var f;
13924         if(typeof value == "string"){
13925             var vlen = value.length;
13926             // auto clear empty filter
13927             if(vlen == 0 && this.clearBlank){
13928                 this.clear();
13929                 return;
13930             }
13931             value = value.toLowerCase();
13932             f = function(n){
13933                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13934             };
13935         }else if(value.exec){ // regex?
13936             f = function(n){
13937                 return value.test(n.attributes[attr]);
13938             };
13939         }else{
13940             throw 'Illegal filter type, must be string or regex';
13941         }
13942         this.filterBy(f, null, startNode);
13943         },
13944
13945     /**
13946      * Filter by a function. The passed function will be called with each
13947      * node in the tree (or from the startNode). If the function returns true, the node is kept
13948      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13949      * @param {Function} fn The filter function
13950      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13951      */
13952     filterBy : function(fn, scope, startNode){
13953         startNode = startNode || this.tree.root;
13954         if(this.autoClear){
13955             this.clear();
13956         }
13957         var af = this.filtered, rv = this.reverse;
13958         var f = function(n){
13959             if(n == startNode){
13960                 return true;
13961             }
13962             if(af[n.id]){
13963                 return false;
13964             }
13965             var m = fn.call(scope || n, n);
13966             if(!m || rv){
13967                 af[n.id] = n;
13968                 n.ui.hide();
13969                 return false;
13970             }
13971             return true;
13972         };
13973         startNode.cascade(f);
13974         if(this.remove){
13975            for(var id in af){
13976                if(typeof id != "function"){
13977                    var n = af[id];
13978                    if(n && n.parentNode){
13979                        n.parentNode.removeChild(n);
13980                    }
13981                }
13982            }
13983         }
13984     },
13985
13986     /**
13987      * Clears the current filter. Note: with the "remove" option
13988      * set a filter cannot be cleared.
13989      */
13990     clear : function(){
13991         var t = this.tree;
13992         var af = this.filtered;
13993         for(var id in af){
13994             if(typeof id != "function"){
13995                 var n = af[id];
13996                 if(n){
13997                     n.ui.show();
13998                 }
13999             }
14000         }
14001         this.filtered = {};
14002     }
14003 };
14004 /*
14005  * Based on:
14006  * Ext JS Library 1.1.1
14007  * Copyright(c) 2006-2007, Ext JS, LLC.
14008  *
14009  * Originally Released Under LGPL - original licence link has changed is not relivant.
14010  *
14011  * Fork - LGPL
14012  * <script type="text/javascript">
14013  */
14014  
14015
14016 /**
14017  * @class Roo.tree.TreeSorter
14018  * Provides sorting of nodes in a TreePanel
14019  * 
14020  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14021  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14022  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14023  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14024  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14025  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14026  * @constructor
14027  * @param {TreePanel} tree
14028  * @param {Object} config
14029  */
14030 Roo.tree.TreeSorter = function(tree, config){
14031     Roo.apply(this, config);
14032     tree.on("beforechildrenrendered", this.doSort, this);
14033     tree.on("append", this.updateSort, this);
14034     tree.on("insert", this.updateSort, this);
14035     
14036     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14037     var p = this.property || "text";
14038     var sortType = this.sortType;
14039     var fs = this.folderSort;
14040     var cs = this.caseSensitive === true;
14041     var leafAttr = this.leafAttr || 'leaf';
14042
14043     this.sortFn = function(n1, n2){
14044         if(fs){
14045             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14046                 return 1;
14047             }
14048             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14049                 return -1;
14050             }
14051         }
14052         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14053         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14054         if(v1 < v2){
14055                         return dsc ? +1 : -1;
14056                 }else if(v1 > v2){
14057                         return dsc ? -1 : +1;
14058         }else{
14059                 return 0;
14060         }
14061     };
14062 };
14063
14064 Roo.tree.TreeSorter.prototype = {
14065     doSort : function(node){
14066         node.sort(this.sortFn);
14067     },
14068     
14069     compareNodes : function(n1, n2){
14070         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14071     },
14072     
14073     updateSort : function(tree, node){
14074         if(node.childrenRendered){
14075             this.doSort.defer(1, this, [node]);
14076         }
14077     }
14078 };/*
14079  * Based on:
14080  * Ext JS Library 1.1.1
14081  * Copyright(c) 2006-2007, Ext JS, LLC.
14082  *
14083  * Originally Released Under LGPL - original licence link has changed is not relivant.
14084  *
14085  * Fork - LGPL
14086  * <script type="text/javascript">
14087  */
14088
14089 if(Roo.dd.DropZone){
14090     
14091 Roo.tree.TreeDropZone = function(tree, config){
14092     this.allowParentInsert = false;
14093     this.allowContainerDrop = false;
14094     this.appendOnly = false;
14095     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14096     this.tree = tree;
14097     this.lastInsertClass = "x-tree-no-status";
14098     this.dragOverData = {};
14099 };
14100
14101 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14102     ddGroup : "TreeDD",
14103     scroll:  true,
14104     
14105     expandDelay : 1000,
14106     
14107     expandNode : function(node){
14108         if(node.hasChildNodes() && !node.isExpanded()){
14109             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14110         }
14111     },
14112     
14113     queueExpand : function(node){
14114         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14115     },
14116     
14117     cancelExpand : function(){
14118         if(this.expandProcId){
14119             clearTimeout(this.expandProcId);
14120             this.expandProcId = false;
14121         }
14122     },
14123     
14124     isValidDropPoint : function(n, pt, dd, e, data){
14125         if(!n || !data){ return false; }
14126         var targetNode = n.node;
14127         var dropNode = data.node;
14128         // default drop rules
14129         if(!(targetNode && targetNode.isTarget && pt)){
14130             return false;
14131         }
14132         if(pt == "append" && targetNode.allowChildren === false){
14133             return false;
14134         }
14135         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14136             return false;
14137         }
14138         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14139             return false;
14140         }
14141         // reuse the object
14142         var overEvent = this.dragOverData;
14143         overEvent.tree = this.tree;
14144         overEvent.target = targetNode;
14145         overEvent.data = data;
14146         overEvent.point = pt;
14147         overEvent.source = dd;
14148         overEvent.rawEvent = e;
14149         overEvent.dropNode = dropNode;
14150         overEvent.cancel = false;  
14151         var result = this.tree.fireEvent("nodedragover", overEvent);
14152         return overEvent.cancel === false && result !== false;
14153     },
14154     
14155     getDropPoint : function(e, n, dd)
14156     {
14157         var tn = n.node;
14158         if(tn.isRoot){
14159             return tn.allowChildren !== false ? "append" : false; // always append for root
14160         }
14161         var dragEl = n.ddel;
14162         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14163         var y = Roo.lib.Event.getPageY(e);
14164         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14165         
14166         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14167         var noAppend = tn.allowChildren === false;
14168         if(this.appendOnly || tn.parentNode.allowChildren === false){
14169             return noAppend ? false : "append";
14170         }
14171         var noBelow = false;
14172         if(!this.allowParentInsert){
14173             noBelow = tn.hasChildNodes() && tn.isExpanded();
14174         }
14175         var q = (b - t) / (noAppend ? 2 : 3);
14176         if(y >= t && y < (t + q)){
14177             return "above";
14178         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14179             return "below";
14180         }else{
14181             return "append";
14182         }
14183     },
14184     
14185     onNodeEnter : function(n, dd, e, data)
14186     {
14187         this.cancelExpand();
14188     },
14189     
14190     onNodeOver : function(n, dd, e, data)
14191     {
14192        
14193         var pt = this.getDropPoint(e, n, dd);
14194         var node = n.node;
14195         
14196         // auto node expand check
14197         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14198             this.queueExpand(node);
14199         }else if(pt != "append"){
14200             this.cancelExpand();
14201         }
14202         
14203         // set the insert point style on the target node
14204         var returnCls = this.dropNotAllowed;
14205         if(this.isValidDropPoint(n, pt, dd, e, data)){
14206            if(pt){
14207                var el = n.ddel;
14208                var cls;
14209                if(pt == "above"){
14210                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14211                    cls = "x-tree-drag-insert-above";
14212                }else if(pt == "below"){
14213                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14214                    cls = "x-tree-drag-insert-below";
14215                }else{
14216                    returnCls = "x-tree-drop-ok-append";
14217                    cls = "x-tree-drag-append";
14218                }
14219                if(this.lastInsertClass != cls){
14220                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14221                    this.lastInsertClass = cls;
14222                }
14223            }
14224        }
14225        return returnCls;
14226     },
14227     
14228     onNodeOut : function(n, dd, e, data){
14229         
14230         this.cancelExpand();
14231         this.removeDropIndicators(n);
14232     },
14233     
14234     onNodeDrop : function(n, dd, e, data){
14235         var point = this.getDropPoint(e, n, dd);
14236         var targetNode = n.node;
14237         targetNode.ui.startDrop();
14238         if(!this.isValidDropPoint(n, point, dd, e, data)){
14239             targetNode.ui.endDrop();
14240             return false;
14241         }
14242         // first try to find the drop node
14243         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14244         var dropEvent = {
14245             tree : this.tree,
14246             target: targetNode,
14247             data: data,
14248             point: point,
14249             source: dd,
14250             rawEvent: e,
14251             dropNode: dropNode,
14252             cancel: !dropNode   
14253         };
14254         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14255         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14256             targetNode.ui.endDrop();
14257             return false;
14258         }
14259         // allow target changing
14260         targetNode = dropEvent.target;
14261         if(point == "append" && !targetNode.isExpanded()){
14262             targetNode.expand(false, null, function(){
14263                 this.completeDrop(dropEvent);
14264             }.createDelegate(this));
14265         }else{
14266             this.completeDrop(dropEvent);
14267         }
14268         return true;
14269     },
14270     
14271     completeDrop : function(de){
14272         var ns = de.dropNode, p = de.point, t = de.target;
14273         if(!(ns instanceof Array)){
14274             ns = [ns];
14275         }
14276         var n;
14277         for(var i = 0, len = ns.length; i < len; i++){
14278             n = ns[i];
14279             if(p == "above"){
14280                 t.parentNode.insertBefore(n, t);
14281             }else if(p == "below"){
14282                 t.parentNode.insertBefore(n, t.nextSibling);
14283             }else{
14284                 t.appendChild(n);
14285             }
14286         }
14287         n.ui.focus();
14288         if(this.tree.hlDrop){
14289             n.ui.highlight();
14290         }
14291         t.ui.endDrop();
14292         this.tree.fireEvent("nodedrop", de);
14293     },
14294     
14295     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14296         if(this.tree.hlDrop){
14297             dropNode.ui.focus();
14298             dropNode.ui.highlight();
14299         }
14300         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14301     },
14302     
14303     getTree : function(){
14304         return this.tree;
14305     },
14306     
14307     removeDropIndicators : function(n){
14308         if(n && n.ddel){
14309             var el = n.ddel;
14310             Roo.fly(el).removeClass([
14311                     "x-tree-drag-insert-above",
14312                     "x-tree-drag-insert-below",
14313                     "x-tree-drag-append"]);
14314             this.lastInsertClass = "_noclass";
14315         }
14316     },
14317     
14318     beforeDragDrop : function(target, e, id){
14319         this.cancelExpand();
14320         return true;
14321     },
14322     
14323     afterRepair : function(data){
14324         if(data && Roo.enableFx){
14325             data.node.ui.highlight();
14326         }
14327         this.hideProxy();
14328     } 
14329     
14330 });
14331
14332 }
14333 /*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343  
14344
14345 if(Roo.dd.DragZone){
14346 Roo.tree.TreeDragZone = function(tree, config){
14347     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14348     this.tree = tree;
14349 };
14350
14351 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14352     ddGroup : "TreeDD",
14353    
14354     onBeforeDrag : function(data, e){
14355         var n = data.node;
14356         return n && n.draggable && !n.disabled;
14357     },
14358      
14359     
14360     onInitDrag : function(e){
14361         var data = this.dragData;
14362         this.tree.getSelectionModel().select(data.node);
14363         this.proxy.update("");
14364         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14365         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14366     },
14367     
14368     getRepairXY : function(e, data){
14369         return data.node.ui.getDDRepairXY();
14370     },
14371     
14372     onEndDrag : function(data, e){
14373         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14374         
14375         
14376     },
14377     
14378     onValidDrop : function(dd, e, id){
14379         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14380         this.hideProxy();
14381     },
14382     
14383     beforeInvalidDrop : function(e, id){
14384         // this scrolls the original position back into view
14385         var sm = this.tree.getSelectionModel();
14386         sm.clearSelections();
14387         sm.select(this.dragData.node);
14388     }
14389 });
14390 }/*
14391  * Based on:
14392  * Ext JS Library 1.1.1
14393  * Copyright(c) 2006-2007, Ext JS, LLC.
14394  *
14395  * Originally Released Under LGPL - original licence link has changed is not relivant.
14396  *
14397  * Fork - LGPL
14398  * <script type="text/javascript">
14399  */
14400 /**
14401  * @class Roo.tree.TreeEditor
14402  * @extends Roo.Editor
14403  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14404  * as the editor field.
14405  * @constructor
14406  * @param {Object} config (used to be the tree panel.)
14407  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14408  * 
14409  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14410  * @cfg {Roo.form.TextField|Object} field The field configuration
14411  *
14412  * 
14413  */
14414 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14415     var tree = config;
14416     var field;
14417     if (oldconfig) { // old style..
14418         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14419     } else {
14420         // new style..
14421         tree = config.tree;
14422         config.field = config.field  || {};
14423         config.field.xtype = 'TextField';
14424         field = Roo.factory(config.field, Roo.form);
14425     }
14426     config = config || {};
14427     
14428     
14429     this.addEvents({
14430         /**
14431          * @event beforenodeedit
14432          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14433          * false from the handler of this event.
14434          * @param {Editor} this
14435          * @param {Roo.tree.Node} node 
14436          */
14437         "beforenodeedit" : true
14438     });
14439     
14440     //Roo.log(config);
14441     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14442
14443     this.tree = tree;
14444
14445     tree.on('beforeclick', this.beforeNodeClick, this);
14446     tree.getTreeEl().on('mousedown', this.hide, this);
14447     this.on('complete', this.updateNode, this);
14448     this.on('beforestartedit', this.fitToTree, this);
14449     this.on('startedit', this.bindScroll, this, {delay:10});
14450     this.on('specialkey', this.onSpecialKey, this);
14451 };
14452
14453 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14454     /**
14455      * @cfg {String} alignment
14456      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14457      */
14458     alignment: "l-l",
14459     // inherit
14460     autoSize: false,
14461     /**
14462      * @cfg {Boolean} hideEl
14463      * True to hide the bound element while the editor is displayed (defaults to false)
14464      */
14465     hideEl : false,
14466     /**
14467      * @cfg {String} cls
14468      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14469      */
14470     cls: "x-small-editor x-tree-editor",
14471     /**
14472      * @cfg {Boolean} shim
14473      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14474      */
14475     shim:false,
14476     // inherit
14477     shadow:"frame",
14478     /**
14479      * @cfg {Number} maxWidth
14480      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14481      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14482      * scroll and client offsets into account prior to each edit.
14483      */
14484     maxWidth: 250,
14485
14486     editDelay : 350,
14487
14488     // private
14489     fitToTree : function(ed, el){
14490         var td = this.tree.getTreeEl().dom, nd = el.dom;
14491         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14492             td.scrollLeft = nd.offsetLeft;
14493         }
14494         var w = Math.min(
14495                 this.maxWidth,
14496                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14497         this.setSize(w, '');
14498         
14499         return this.fireEvent('beforenodeedit', this, this.editNode);
14500         
14501     },
14502
14503     // private
14504     triggerEdit : function(node){
14505         this.completeEdit();
14506         this.editNode = node;
14507         this.startEdit(node.ui.textNode, node.text);
14508     },
14509
14510     // private
14511     bindScroll : function(){
14512         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14513     },
14514
14515     // private
14516     beforeNodeClick : function(node, e){
14517         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14518         this.lastClick = new Date();
14519         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14520             e.stopEvent();
14521             this.triggerEdit(node);
14522             return false;
14523         }
14524         return true;
14525     },
14526
14527     // private
14528     updateNode : function(ed, value){
14529         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14530         this.editNode.setText(value);
14531     },
14532
14533     // private
14534     onHide : function(){
14535         Roo.tree.TreeEditor.superclass.onHide.call(this);
14536         if(this.editNode){
14537             this.editNode.ui.focus();
14538         }
14539     },
14540
14541     // private
14542     onSpecialKey : function(field, e){
14543         var k = e.getKey();
14544         if(k == e.ESC){
14545             e.stopEvent();
14546             this.cancelEdit();
14547         }else if(k == e.ENTER && !e.hasModifier()){
14548             e.stopEvent();
14549             this.completeEdit();
14550         }
14551     }
14552 });//<Script type="text/javascript">
14553 /*
14554  * Based on:
14555  * Ext JS Library 1.1.1
14556  * Copyright(c) 2006-2007, Ext JS, LLC.
14557  *
14558  * Originally Released Under LGPL - original licence link has changed is not relivant.
14559  *
14560  * Fork - LGPL
14561  * <script type="text/javascript">
14562  */
14563  
14564 /**
14565  * Not documented??? - probably should be...
14566  */
14567
14568 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14569     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14570     
14571     renderElements : function(n, a, targetNode, bulkRender){
14572         //consel.log("renderElements?");
14573         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14574
14575         var t = n.getOwnerTree();
14576         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14577         
14578         var cols = t.columns;
14579         var bw = t.borderWidth;
14580         var c = cols[0];
14581         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14582          var cb = typeof a.checked == "boolean";
14583         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14584         var colcls = 'x-t-' + tid + '-c0';
14585         var buf = [
14586             '<li class="x-tree-node">',
14587             
14588                 
14589                 '<div class="x-tree-node-el ', a.cls,'">',
14590                     // extran...
14591                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14592                 
14593                 
14594                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14595                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14596                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14597                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14598                            (a.iconCls ? ' '+a.iconCls : ''),
14599                            '" unselectable="on" />',
14600                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14601                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14602                              
14603                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14604                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14605                             '<span unselectable="on" qtip="' + tx + '">',
14606                              tx,
14607                              '</span></a>' ,
14608                     '</div>',
14609                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14610                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14611                  ];
14612         for(var i = 1, len = cols.length; i < len; i++){
14613             c = cols[i];
14614             colcls = 'x-t-' + tid + '-c' +i;
14615             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14616             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14617                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14618                       "</div>");
14619          }
14620          
14621          buf.push(
14622             '</a>',
14623             '<div class="x-clear"></div></div>',
14624             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14625             "</li>");
14626         
14627         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14628             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14629                                 n.nextSibling.ui.getEl(), buf.join(""));
14630         }else{
14631             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14632         }
14633         var el = this.wrap.firstChild;
14634         this.elRow = el;
14635         this.elNode = el.firstChild;
14636         this.ranchor = el.childNodes[1];
14637         this.ctNode = this.wrap.childNodes[1];
14638         var cs = el.firstChild.childNodes;
14639         this.indentNode = cs[0];
14640         this.ecNode = cs[1];
14641         this.iconNode = cs[2];
14642         var index = 3;
14643         if(cb){
14644             this.checkbox = cs[3];
14645             index++;
14646         }
14647         this.anchor = cs[index];
14648         
14649         this.textNode = cs[index].firstChild;
14650         
14651         //el.on("click", this.onClick, this);
14652         //el.on("dblclick", this.onDblClick, this);
14653         
14654         
14655        // console.log(this);
14656     },
14657     initEvents : function(){
14658         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14659         
14660             
14661         var a = this.ranchor;
14662
14663         var el = Roo.get(a);
14664
14665         if(Roo.isOpera){ // opera render bug ignores the CSS
14666             el.setStyle("text-decoration", "none");
14667         }
14668
14669         el.on("click", this.onClick, this);
14670         el.on("dblclick", this.onDblClick, this);
14671         el.on("contextmenu", this.onContextMenu, this);
14672         
14673     },
14674     
14675     /*onSelectedChange : function(state){
14676         if(state){
14677             this.focus();
14678             this.addClass("x-tree-selected");
14679         }else{
14680             //this.blur();
14681             this.removeClass("x-tree-selected");
14682         }
14683     },*/
14684     addClass : function(cls){
14685         if(this.elRow){
14686             Roo.fly(this.elRow).addClass(cls);
14687         }
14688         
14689     },
14690     
14691     
14692     removeClass : function(cls){
14693         if(this.elRow){
14694             Roo.fly(this.elRow).removeClass(cls);
14695         }
14696     }
14697
14698     
14699     
14700 });//<Script type="text/javascript">
14701
14702 /*
14703  * Based on:
14704  * Ext JS Library 1.1.1
14705  * Copyright(c) 2006-2007, Ext JS, LLC.
14706  *
14707  * Originally Released Under LGPL - original licence link has changed is not relivant.
14708  *
14709  * Fork - LGPL
14710  * <script type="text/javascript">
14711  */
14712  
14713
14714 /**
14715  * @class Roo.tree.ColumnTree
14716  * @extends Roo.data.TreePanel
14717  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14718  * @cfg {int} borderWidth  compined right/left border allowance
14719  * @constructor
14720  * @param {String/HTMLElement/Element} el The container element
14721  * @param {Object} config
14722  */
14723 Roo.tree.ColumnTree =  function(el, config)
14724 {
14725    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14726    this.addEvents({
14727         /**
14728         * @event resize
14729         * Fire this event on a container when it resizes
14730         * @param {int} w Width
14731         * @param {int} h Height
14732         */
14733        "resize" : true
14734     });
14735     this.on('resize', this.onResize, this);
14736 };
14737
14738 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14739     //lines:false,
14740     
14741     
14742     borderWidth: Roo.isBorderBox ? 0 : 2, 
14743     headEls : false,
14744     
14745     render : function(){
14746         // add the header.....
14747        
14748         Roo.tree.ColumnTree.superclass.render.apply(this);
14749         
14750         this.el.addClass('x-column-tree');
14751         
14752         this.headers = this.el.createChild(
14753             {cls:'x-tree-headers'},this.innerCt.dom);
14754    
14755         var cols = this.columns, c;
14756         var totalWidth = 0;
14757         this.headEls = [];
14758         var  len = cols.length;
14759         for(var i = 0; i < len; i++){
14760              c = cols[i];
14761              totalWidth += c.width;
14762             this.headEls.push(this.headers.createChild({
14763                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14764                  cn: {
14765                      cls:'x-tree-hd-text',
14766                      html: c.header
14767                  },
14768                  style:'width:'+(c.width-this.borderWidth)+'px;'
14769              }));
14770         }
14771         this.headers.createChild({cls:'x-clear'});
14772         // prevent floats from wrapping when clipped
14773         this.headers.setWidth(totalWidth);
14774         //this.innerCt.setWidth(totalWidth);
14775         this.innerCt.setStyle({ overflow: 'auto' });
14776         this.onResize(this.width, this.height);
14777              
14778         
14779     },
14780     onResize : function(w,h)
14781     {
14782         this.height = h;
14783         this.width = w;
14784         // resize cols..
14785         this.innerCt.setWidth(this.width);
14786         this.innerCt.setHeight(this.height-20);
14787         
14788         // headers...
14789         var cols = this.columns, c;
14790         var totalWidth = 0;
14791         var expEl = false;
14792         var len = cols.length;
14793         for(var i = 0; i < len; i++){
14794             c = cols[i];
14795             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14796                 // it's the expander..
14797                 expEl  = this.headEls[i];
14798                 continue;
14799             }
14800             totalWidth += c.width;
14801             
14802         }
14803         if (expEl) {
14804             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14805         }
14806         this.headers.setWidth(w-20);
14807
14808         
14809         
14810         
14811     }
14812 });
14813 /*
14814  * Based on:
14815  * Ext JS Library 1.1.1
14816  * Copyright(c) 2006-2007, Ext JS, LLC.
14817  *
14818  * Originally Released Under LGPL - original licence link has changed is not relivant.
14819  *
14820  * Fork - LGPL
14821  * <script type="text/javascript">
14822  */
14823  
14824 /**
14825  * @class Roo.menu.Menu
14826  * @extends Roo.util.Observable
14827  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14828  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14829  * @constructor
14830  * Creates a new Menu
14831  * @param {Object} config Configuration options
14832  */
14833 Roo.menu.Menu = function(config){
14834     
14835     Roo.menu.Menu.superclass.constructor.call(this, config);
14836     
14837     this.id = this.id || Roo.id();
14838     this.addEvents({
14839         /**
14840          * @event beforeshow
14841          * Fires before this menu is displayed
14842          * @param {Roo.menu.Menu} this
14843          */
14844         beforeshow : true,
14845         /**
14846          * @event beforehide
14847          * Fires before this menu is hidden
14848          * @param {Roo.menu.Menu} this
14849          */
14850         beforehide : true,
14851         /**
14852          * @event show
14853          * Fires after this menu is displayed
14854          * @param {Roo.menu.Menu} this
14855          */
14856         show : true,
14857         /**
14858          * @event hide
14859          * Fires after this menu is hidden
14860          * @param {Roo.menu.Menu} this
14861          */
14862         hide : true,
14863         /**
14864          * @event click
14865          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14866          * @param {Roo.menu.Menu} this
14867          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14868          * @param {Roo.EventObject} e
14869          */
14870         click : true,
14871         /**
14872          * @event mouseover
14873          * Fires when the mouse is hovering over this menu
14874          * @param {Roo.menu.Menu} this
14875          * @param {Roo.EventObject} e
14876          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14877          */
14878         mouseover : true,
14879         /**
14880          * @event mouseout
14881          * Fires when the mouse exits this menu
14882          * @param {Roo.menu.Menu} this
14883          * @param {Roo.EventObject} e
14884          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14885          */
14886         mouseout : true,
14887         /**
14888          * @event itemclick
14889          * Fires when a menu item contained in this menu is clicked
14890          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14891          * @param {Roo.EventObject} e
14892          */
14893         itemclick: true
14894     });
14895     if (this.registerMenu) {
14896         Roo.menu.MenuMgr.register(this);
14897     }
14898     
14899     var mis = this.items;
14900     this.items = new Roo.util.MixedCollection();
14901     if(mis){
14902         this.add.apply(this, mis);
14903     }
14904 };
14905
14906 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14907     /**
14908      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14909      */
14910     minWidth : 120,
14911     /**
14912      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14913      * for bottom-right shadow (defaults to "sides")
14914      */
14915     shadow : "sides",
14916     /**
14917      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14918      * this menu (defaults to "tl-tr?")
14919      */
14920     subMenuAlign : "tl-tr?",
14921     /**
14922      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14923      * relative to its element of origin (defaults to "tl-bl?")
14924      */
14925     defaultAlign : "tl-bl?",
14926     /**
14927      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14928      */
14929     allowOtherMenus : false,
14930     /**
14931      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14932      */
14933     registerMenu : true,
14934
14935     hidden:true,
14936
14937     // private
14938     render : function(){
14939         if(this.el){
14940             return;
14941         }
14942         var el = this.el = new Roo.Layer({
14943             cls: "x-menu",
14944             shadow:this.shadow,
14945             constrain: false,
14946             parentEl: this.parentEl || document.body,
14947             zindex:15000
14948         });
14949
14950         this.keyNav = new Roo.menu.MenuNav(this);
14951
14952         if(this.plain){
14953             el.addClass("x-menu-plain");
14954         }
14955         if(this.cls){
14956             el.addClass(this.cls);
14957         }
14958         // generic focus element
14959         this.focusEl = el.createChild({
14960             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14961         });
14962         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14963         //disabling touch- as it's causing issues ..
14964         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14965         ul.on('click'   , this.onClick, this);
14966         
14967         
14968         ul.on("mouseover", this.onMouseOver, this);
14969         ul.on("mouseout", this.onMouseOut, this);
14970         this.items.each(function(item){
14971             if (item.hidden) {
14972                 return;
14973             }
14974             
14975             var li = document.createElement("li");
14976             li.className = "x-menu-list-item";
14977             ul.dom.appendChild(li);
14978             item.render(li, this);
14979         }, this);
14980         this.ul = ul;
14981         this.autoWidth();
14982     },
14983
14984     // private
14985     autoWidth : function(){
14986         var el = this.el, ul = this.ul;
14987         if(!el){
14988             return;
14989         }
14990         var w = this.width;
14991         if(w){
14992             el.setWidth(w);
14993         }else if(Roo.isIE){
14994             el.setWidth(this.minWidth);
14995             var t = el.dom.offsetWidth; // force recalc
14996             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14997         }
14998     },
14999
15000     // private
15001     delayAutoWidth : function(){
15002         if(this.rendered){
15003             if(!this.awTask){
15004                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
15005             }
15006             this.awTask.delay(20);
15007         }
15008     },
15009
15010     // private
15011     findTargetItem : function(e){
15012         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
15013         if(t && t.menuItemId){
15014             return this.items.get(t.menuItemId);
15015         }
15016     },
15017
15018     // private
15019     onClick : function(e){
15020         Roo.log("menu.onClick");
15021         var t = this.findTargetItem(e);
15022         if(!t){
15023             return;
15024         }
15025         Roo.log(e);
15026         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
15027             if(t == this.activeItem && t.shouldDeactivate(e)){
15028                 this.activeItem.deactivate();
15029                 delete this.activeItem;
15030                 return;
15031             }
15032             if(t.canActivate){
15033                 this.setActiveItem(t, true);
15034             }
15035             return;
15036             
15037             
15038         }
15039         
15040         t.onClick(e);
15041         this.fireEvent("click", this, t, e);
15042     },
15043
15044     // private
15045     setActiveItem : function(item, autoExpand){
15046         if(item != this.activeItem){
15047             if(this.activeItem){
15048                 this.activeItem.deactivate();
15049             }
15050             this.activeItem = item;
15051             item.activate(autoExpand);
15052         }else if(autoExpand){
15053             item.expandMenu();
15054         }
15055     },
15056
15057     // private
15058     tryActivate : function(start, step){
15059         var items = this.items;
15060         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15061             var item = items.get(i);
15062             if(!item.disabled && item.canActivate){
15063                 this.setActiveItem(item, false);
15064                 return item;
15065             }
15066         }
15067         return false;
15068     },
15069
15070     // private
15071     onMouseOver : function(e){
15072         var t;
15073         if(t = this.findTargetItem(e)){
15074             if(t.canActivate && !t.disabled){
15075                 this.setActiveItem(t, true);
15076             }
15077         }
15078         this.fireEvent("mouseover", this, e, t);
15079     },
15080
15081     // private
15082     onMouseOut : function(e){
15083         var t;
15084         if(t = this.findTargetItem(e)){
15085             if(t == this.activeItem && t.shouldDeactivate(e)){
15086                 this.activeItem.deactivate();
15087                 delete this.activeItem;
15088             }
15089         }
15090         this.fireEvent("mouseout", this, e, t);
15091     },
15092
15093     /**
15094      * Read-only.  Returns true if the menu is currently displayed, else false.
15095      * @type Boolean
15096      */
15097     isVisible : function(){
15098         return this.el && !this.hidden;
15099     },
15100
15101     /**
15102      * Displays this menu relative to another element
15103      * @param {String/HTMLElement/Roo.Element} element The element to align to
15104      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15105      * the element (defaults to this.defaultAlign)
15106      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15107      */
15108     show : function(el, pos, parentMenu){
15109         this.parentMenu = parentMenu;
15110         if(!this.el){
15111             this.render();
15112         }
15113         this.fireEvent("beforeshow", this);
15114         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15115     },
15116
15117     /**
15118      * Displays this menu at a specific xy position
15119      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15120      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15121      */
15122     showAt : function(xy, parentMenu, /* private: */_e){
15123         this.parentMenu = parentMenu;
15124         if(!this.el){
15125             this.render();
15126         }
15127         if(_e !== false){
15128             this.fireEvent("beforeshow", this);
15129             xy = this.el.adjustForConstraints(xy);
15130         }
15131         this.el.setXY(xy);
15132         this.el.show();
15133         this.hidden = false;
15134         this.focus();
15135         this.fireEvent("show", this);
15136     },
15137
15138     focus : function(){
15139         if(!this.hidden){
15140             this.doFocus.defer(50, this);
15141         }
15142     },
15143
15144     doFocus : function(){
15145         if(!this.hidden){
15146             this.focusEl.focus();
15147         }
15148     },
15149
15150     /**
15151      * Hides this menu and optionally all parent menus
15152      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15153      */
15154     hide : function(deep){
15155         if(this.el && this.isVisible()){
15156             this.fireEvent("beforehide", this);
15157             if(this.activeItem){
15158                 this.activeItem.deactivate();
15159                 this.activeItem = null;
15160             }
15161             this.el.hide();
15162             this.hidden = true;
15163             this.fireEvent("hide", this);
15164         }
15165         if(deep === true && this.parentMenu){
15166             this.parentMenu.hide(true);
15167         }
15168     },
15169
15170     /**
15171      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15172      * Any of the following are valid:
15173      * <ul>
15174      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15175      * <li>An HTMLElement object which will be converted to a menu item</li>
15176      * <li>A menu item config object that will be created as a new menu item</li>
15177      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15178      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15179      * </ul>
15180      * Usage:
15181      * <pre><code>
15182 // Create the menu
15183 var menu = new Roo.menu.Menu();
15184
15185 // Create a menu item to add by reference
15186 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15187
15188 // Add a bunch of items at once using different methods.
15189 // Only the last item added will be returned.
15190 var item = menu.add(
15191     menuItem,                // add existing item by ref
15192     'Dynamic Item',          // new TextItem
15193     '-',                     // new separator
15194     { text: 'Config Item' }  // new item by config
15195 );
15196 </code></pre>
15197      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15198      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15199      */
15200     add : function(){
15201         var a = arguments, l = a.length, item;
15202         for(var i = 0; i < l; i++){
15203             var el = a[i];
15204             if ((typeof(el) == "object") && el.xtype && el.xns) {
15205                 el = Roo.factory(el, Roo.menu);
15206             }
15207             
15208             if(el.render){ // some kind of Item
15209                 item = this.addItem(el);
15210             }else if(typeof el == "string"){ // string
15211                 if(el == "separator" || el == "-"){
15212                     item = this.addSeparator();
15213                 }else{
15214                     item = this.addText(el);
15215                 }
15216             }else if(el.tagName || el.el){ // element
15217                 item = this.addElement(el);
15218             }else if(typeof el == "object"){ // must be menu item config?
15219                 item = this.addMenuItem(el);
15220             }
15221         }
15222         return item;
15223     },
15224
15225     /**
15226      * Returns this menu's underlying {@link Roo.Element} object
15227      * @return {Roo.Element} The element
15228      */
15229     getEl : function(){
15230         if(!this.el){
15231             this.render();
15232         }
15233         return this.el;
15234     },
15235
15236     /**
15237      * Adds a separator bar to the menu
15238      * @return {Roo.menu.Item} The menu item that was added
15239      */
15240     addSeparator : function(){
15241         return this.addItem(new Roo.menu.Separator());
15242     },
15243
15244     /**
15245      * Adds an {@link Roo.Element} object to the menu
15246      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15247      * @return {Roo.menu.Item} The menu item that was added
15248      */
15249     addElement : function(el){
15250         return this.addItem(new Roo.menu.BaseItem(el));
15251     },
15252
15253     /**
15254      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15255      * @param {Roo.menu.Item} item The menu item to add
15256      * @return {Roo.menu.Item} The menu item that was added
15257      */
15258     addItem : function(item){
15259         this.items.add(item);
15260         if(this.ul){
15261             var li = document.createElement("li");
15262             li.className = "x-menu-list-item";
15263             this.ul.dom.appendChild(li);
15264             item.render(li, this);
15265             this.delayAutoWidth();
15266         }
15267         return item;
15268     },
15269
15270     /**
15271      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15272      * @param {Object} config A MenuItem config object
15273      * @return {Roo.menu.Item} The menu item that was added
15274      */
15275     addMenuItem : function(config){
15276         if(!(config instanceof Roo.menu.Item)){
15277             if(typeof config.checked == "boolean"){ // must be check menu item config?
15278                 config = new Roo.menu.CheckItem(config);
15279             }else{
15280                 config = new Roo.menu.Item(config);
15281             }
15282         }
15283         return this.addItem(config);
15284     },
15285
15286     /**
15287      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15288      * @param {String} text The text to display in the menu item
15289      * @return {Roo.menu.Item} The menu item that was added
15290      */
15291     addText : function(text){
15292         return this.addItem(new Roo.menu.TextItem({ text : text }));
15293     },
15294
15295     /**
15296      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15297      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15298      * @param {Roo.menu.Item} item The menu item to add
15299      * @return {Roo.menu.Item} The menu item that was added
15300      */
15301     insert : function(index, item){
15302         this.items.insert(index, item);
15303         if(this.ul){
15304             var li = document.createElement("li");
15305             li.className = "x-menu-list-item";
15306             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15307             item.render(li, this);
15308             this.delayAutoWidth();
15309         }
15310         return item;
15311     },
15312
15313     /**
15314      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15315      * @param {Roo.menu.Item} item The menu item to remove
15316      */
15317     remove : function(item){
15318         this.items.removeKey(item.id);
15319         item.destroy();
15320     },
15321
15322     /**
15323      * Removes and destroys all items in the menu
15324      */
15325     removeAll : function(){
15326         var f;
15327         while(f = this.items.first()){
15328             this.remove(f);
15329         }
15330     }
15331 });
15332
15333 // MenuNav is a private utility class used internally by the Menu
15334 Roo.menu.MenuNav = function(menu){
15335     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15336     this.scope = this.menu = menu;
15337 };
15338
15339 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15340     doRelay : function(e, h){
15341         var k = e.getKey();
15342         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15343             this.menu.tryActivate(0, 1);
15344             return false;
15345         }
15346         return h.call(this.scope || this, e, this.menu);
15347     },
15348
15349     up : function(e, m){
15350         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15351             m.tryActivate(m.items.length-1, -1);
15352         }
15353     },
15354
15355     down : function(e, m){
15356         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15357             m.tryActivate(0, 1);
15358         }
15359     },
15360
15361     right : function(e, m){
15362         if(m.activeItem){
15363             m.activeItem.expandMenu(true);
15364         }
15365     },
15366
15367     left : function(e, m){
15368         m.hide();
15369         if(m.parentMenu && m.parentMenu.activeItem){
15370             m.parentMenu.activeItem.activate();
15371         }
15372     },
15373
15374     enter : function(e, m){
15375         if(m.activeItem){
15376             e.stopPropagation();
15377             m.activeItem.onClick(e);
15378             m.fireEvent("click", this, m.activeItem);
15379             return true;
15380         }
15381     }
15382 });/*
15383  * Based on:
15384  * Ext JS Library 1.1.1
15385  * Copyright(c) 2006-2007, Ext JS, LLC.
15386  *
15387  * Originally Released Under LGPL - original licence link has changed is not relivant.
15388  *
15389  * Fork - LGPL
15390  * <script type="text/javascript">
15391  */
15392  
15393 /**
15394  * @class Roo.menu.MenuMgr
15395  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15396  * @singleton
15397  */
15398 Roo.menu.MenuMgr = function(){
15399    var menus, active, groups = {}, attached = false, lastShow = new Date();
15400
15401    // private - called when first menu is created
15402    function init(){
15403        menus = {};
15404        active = new Roo.util.MixedCollection();
15405        Roo.get(document).addKeyListener(27, function(){
15406            if(active.length > 0){
15407                hideAll();
15408            }
15409        });
15410    }
15411
15412    // private
15413    function hideAll(){
15414        if(active && active.length > 0){
15415            var c = active.clone();
15416            c.each(function(m){
15417                m.hide();
15418            });
15419        }
15420    }
15421
15422    // private
15423    function onHide(m){
15424        active.remove(m);
15425        if(active.length < 1){
15426            Roo.get(document).un("mousedown", onMouseDown);
15427            attached = false;
15428        }
15429    }
15430
15431    // private
15432    function onShow(m){
15433        var last = active.last();
15434        lastShow = new Date();
15435        active.add(m);
15436        if(!attached){
15437            Roo.get(document).on("mousedown", onMouseDown);
15438            attached = true;
15439        }
15440        if(m.parentMenu){
15441           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15442           m.parentMenu.activeChild = m;
15443        }else if(last && last.isVisible()){
15444           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15445        }
15446    }
15447
15448    // private
15449    function onBeforeHide(m){
15450        if(m.activeChild){
15451            m.activeChild.hide();
15452        }
15453        if(m.autoHideTimer){
15454            clearTimeout(m.autoHideTimer);
15455            delete m.autoHideTimer;
15456        }
15457    }
15458
15459    // private
15460    function onBeforeShow(m){
15461        var pm = m.parentMenu;
15462        if(!pm && !m.allowOtherMenus){
15463            hideAll();
15464        }else if(pm && pm.activeChild && active != m){
15465            pm.activeChild.hide();
15466        }
15467    }
15468
15469    // private
15470    function onMouseDown(e){
15471        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15472            hideAll();
15473        }
15474    }
15475
15476    // private
15477    function onBeforeCheck(mi, state){
15478        if(state){
15479            var g = groups[mi.group];
15480            for(var i = 0, l = g.length; i < l; i++){
15481                if(g[i] != mi){
15482                    g[i].setChecked(false);
15483                }
15484            }
15485        }
15486    }
15487
15488    return {
15489
15490        /**
15491         * Hides all menus that are currently visible
15492         */
15493        hideAll : function(){
15494             hideAll();  
15495        },
15496
15497        // private
15498        register : function(menu){
15499            if(!menus){
15500                init();
15501            }
15502            menus[menu.id] = menu;
15503            menu.on("beforehide", onBeforeHide);
15504            menu.on("hide", onHide);
15505            menu.on("beforeshow", onBeforeShow);
15506            menu.on("show", onShow);
15507            var g = menu.group;
15508            if(g && menu.events["checkchange"]){
15509                if(!groups[g]){
15510                    groups[g] = [];
15511                }
15512                groups[g].push(menu);
15513                menu.on("checkchange", onCheck);
15514            }
15515        },
15516
15517         /**
15518          * Returns a {@link Roo.menu.Menu} object
15519          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15520          * be used to generate and return a new Menu instance.
15521          */
15522        get : function(menu){
15523            if(typeof menu == "string"){ // menu id
15524                return menus[menu];
15525            }else if(menu.events){  // menu instance
15526                return menu;
15527            }else if(typeof menu.length == 'number'){ // array of menu items?
15528                return new Roo.menu.Menu({items:menu});
15529            }else{ // otherwise, must be a config
15530                return new Roo.menu.Menu(menu);
15531            }
15532        },
15533
15534        // private
15535        unregister : function(menu){
15536            delete menus[menu.id];
15537            menu.un("beforehide", onBeforeHide);
15538            menu.un("hide", onHide);
15539            menu.un("beforeshow", onBeforeShow);
15540            menu.un("show", onShow);
15541            var g = menu.group;
15542            if(g && menu.events["checkchange"]){
15543                groups[g].remove(menu);
15544                menu.un("checkchange", onCheck);
15545            }
15546        },
15547
15548        // private
15549        registerCheckable : function(menuItem){
15550            var g = menuItem.group;
15551            if(g){
15552                if(!groups[g]){
15553                    groups[g] = [];
15554                }
15555                groups[g].push(menuItem);
15556                menuItem.on("beforecheckchange", onBeforeCheck);
15557            }
15558        },
15559
15560        // private
15561        unregisterCheckable : function(menuItem){
15562            var g = menuItem.group;
15563            if(g){
15564                groups[g].remove(menuItem);
15565                menuItem.un("beforecheckchange", onBeforeCheck);
15566            }
15567        }
15568    };
15569 }();/*
15570  * Based on:
15571  * Ext JS Library 1.1.1
15572  * Copyright(c) 2006-2007, Ext JS, LLC.
15573  *
15574  * Originally Released Under LGPL - original licence link has changed is not relivant.
15575  *
15576  * Fork - LGPL
15577  * <script type="text/javascript">
15578  */
15579  
15580
15581 /**
15582  * @class Roo.menu.BaseItem
15583  * @extends Roo.Component
15584  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15585  * management and base configuration options shared by all menu components.
15586  * @constructor
15587  * Creates a new BaseItem
15588  * @param {Object} config Configuration options
15589  */
15590 Roo.menu.BaseItem = function(config){
15591     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15592
15593     this.addEvents({
15594         /**
15595          * @event click
15596          * Fires when this item is clicked
15597          * @param {Roo.menu.BaseItem} this
15598          * @param {Roo.EventObject} e
15599          */
15600         click: true,
15601         /**
15602          * @event activate
15603          * Fires when this item is activated
15604          * @param {Roo.menu.BaseItem} this
15605          */
15606         activate : true,
15607         /**
15608          * @event deactivate
15609          * Fires when this item is deactivated
15610          * @param {Roo.menu.BaseItem} this
15611          */
15612         deactivate : true
15613     });
15614
15615     if(this.handler){
15616         this.on("click", this.handler, this.scope, true);
15617     }
15618 };
15619
15620 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15621     /**
15622      * @cfg {Function} handler
15623      * A function that will handle the click event of this menu item (defaults to undefined)
15624      */
15625     /**
15626      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15627      */
15628     canActivate : false,
15629     
15630      /**
15631      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15632      */
15633     hidden: false,
15634     
15635     /**
15636      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15637      */
15638     activeClass : "x-menu-item-active",
15639     /**
15640      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15641      */
15642     hideOnClick : true,
15643     /**
15644      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15645      */
15646     hideDelay : 100,
15647
15648     // private
15649     ctype: "Roo.menu.BaseItem",
15650
15651     // private
15652     actionMode : "container",
15653
15654     // private
15655     render : function(container, parentMenu){
15656         this.parentMenu = parentMenu;
15657         Roo.menu.BaseItem.superclass.render.call(this, container);
15658         this.container.menuItemId = this.id;
15659     },
15660
15661     // private
15662     onRender : function(container, position){
15663         this.el = Roo.get(this.el);
15664         container.dom.appendChild(this.el.dom);
15665     },
15666
15667     // private
15668     onClick : function(e){
15669         if(!this.disabled && this.fireEvent("click", this, e) !== false
15670                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15671             this.handleClick(e);
15672         }else{
15673             e.stopEvent();
15674         }
15675     },
15676
15677     // private
15678     activate : function(){
15679         if(this.disabled){
15680             return false;
15681         }
15682         var li = this.container;
15683         li.addClass(this.activeClass);
15684         this.region = li.getRegion().adjust(2, 2, -2, -2);
15685         this.fireEvent("activate", this);
15686         return true;
15687     },
15688
15689     // private
15690     deactivate : function(){
15691         this.container.removeClass(this.activeClass);
15692         this.fireEvent("deactivate", this);
15693     },
15694
15695     // private
15696     shouldDeactivate : function(e){
15697         return !this.region || !this.region.contains(e.getPoint());
15698     },
15699
15700     // private
15701     handleClick : function(e){
15702         if(this.hideOnClick){
15703             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15704         }
15705     },
15706
15707     // private
15708     expandMenu : function(autoActivate){
15709         // do nothing
15710     },
15711
15712     // private
15713     hideMenu : function(){
15714         // do nothing
15715     }
15716 });/*
15717  * Based on:
15718  * Ext JS Library 1.1.1
15719  * Copyright(c) 2006-2007, Ext JS, LLC.
15720  *
15721  * Originally Released Under LGPL - original licence link has changed is not relivant.
15722  *
15723  * Fork - LGPL
15724  * <script type="text/javascript">
15725  */
15726  
15727 /**
15728  * @class Roo.menu.Adapter
15729  * @extends Roo.menu.BaseItem
15730  * 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.
15731  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15732  * @constructor
15733  * Creates a new Adapter
15734  * @param {Object} config Configuration options
15735  */
15736 Roo.menu.Adapter = function(component, config){
15737     Roo.menu.Adapter.superclass.constructor.call(this, config);
15738     this.component = component;
15739 };
15740 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15741     // private
15742     canActivate : true,
15743
15744     // private
15745     onRender : function(container, position){
15746         this.component.render(container);
15747         this.el = this.component.getEl();
15748     },
15749
15750     // private
15751     activate : function(){
15752         if(this.disabled){
15753             return false;
15754         }
15755         this.component.focus();
15756         this.fireEvent("activate", this);
15757         return true;
15758     },
15759
15760     // private
15761     deactivate : function(){
15762         this.fireEvent("deactivate", this);
15763     },
15764
15765     // private
15766     disable : function(){
15767         this.component.disable();
15768         Roo.menu.Adapter.superclass.disable.call(this);
15769     },
15770
15771     // private
15772     enable : function(){
15773         this.component.enable();
15774         Roo.menu.Adapter.superclass.enable.call(this);
15775     }
15776 });/*
15777  * Based on:
15778  * Ext JS Library 1.1.1
15779  * Copyright(c) 2006-2007, Ext JS, LLC.
15780  *
15781  * Originally Released Under LGPL - original licence link has changed is not relivant.
15782  *
15783  * Fork - LGPL
15784  * <script type="text/javascript">
15785  */
15786
15787 /**
15788  * @class Roo.menu.TextItem
15789  * @extends Roo.menu.BaseItem
15790  * Adds a static text string to a menu, usually used as either a heading or group separator.
15791  * Note: old style constructor with text is still supported.
15792  * 
15793  * @constructor
15794  * Creates a new TextItem
15795  * @param {Object} cfg Configuration
15796  */
15797 Roo.menu.TextItem = function(cfg){
15798     if (typeof(cfg) == 'string') {
15799         this.text = cfg;
15800     } else {
15801         Roo.apply(this,cfg);
15802     }
15803     
15804     Roo.menu.TextItem.superclass.constructor.call(this);
15805 };
15806
15807 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15808     /**
15809      * @cfg {Boolean} text Text to show on item.
15810      */
15811     text : '',
15812     
15813     /**
15814      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15815      */
15816     hideOnClick : false,
15817     /**
15818      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15819      */
15820     itemCls : "x-menu-text",
15821
15822     // private
15823     onRender : function(){
15824         var s = document.createElement("span");
15825         s.className = this.itemCls;
15826         s.innerHTML = this.text;
15827         this.el = s;
15828         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15829     }
15830 });/*
15831  * Based on:
15832  * Ext JS Library 1.1.1
15833  * Copyright(c) 2006-2007, Ext JS, LLC.
15834  *
15835  * Originally Released Under LGPL - original licence link has changed is not relivant.
15836  *
15837  * Fork - LGPL
15838  * <script type="text/javascript">
15839  */
15840
15841 /**
15842  * @class Roo.menu.Separator
15843  * @extends Roo.menu.BaseItem
15844  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15845  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15846  * @constructor
15847  * @param {Object} config Configuration options
15848  */
15849 Roo.menu.Separator = function(config){
15850     Roo.menu.Separator.superclass.constructor.call(this, config);
15851 };
15852
15853 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15854     /**
15855      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15856      */
15857     itemCls : "x-menu-sep",
15858     /**
15859      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15860      */
15861     hideOnClick : false,
15862
15863     // private
15864     onRender : function(li){
15865         var s = document.createElement("span");
15866         s.className = this.itemCls;
15867         s.innerHTML = "&#160;";
15868         this.el = s;
15869         li.addClass("x-menu-sep-li");
15870         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15871     }
15872 });/*
15873  * Based on:
15874  * Ext JS Library 1.1.1
15875  * Copyright(c) 2006-2007, Ext JS, LLC.
15876  *
15877  * Originally Released Under LGPL - original licence link has changed is not relivant.
15878  *
15879  * Fork - LGPL
15880  * <script type="text/javascript">
15881  */
15882 /**
15883  * @class Roo.menu.Item
15884  * @extends Roo.menu.BaseItem
15885  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15886  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15887  * activation and click handling.
15888  * @constructor
15889  * Creates a new Item
15890  * @param {Object} config Configuration options
15891  */
15892 Roo.menu.Item = function(config){
15893     Roo.menu.Item.superclass.constructor.call(this, config);
15894     if(this.menu){
15895         this.menu = Roo.menu.MenuMgr.get(this.menu);
15896     }
15897 };
15898 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15899     
15900     /**
15901      * @cfg {String} text
15902      * The text to show on the menu item.
15903      */
15904     text: '',
15905      /**
15906      * @cfg {String} HTML to render in menu
15907      * The text to show on the menu item (HTML version).
15908      */
15909     html: '',
15910     /**
15911      * @cfg {String} icon
15912      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15913      */
15914     icon: undefined,
15915     /**
15916      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15917      */
15918     itemCls : "x-menu-item",
15919     /**
15920      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15921      */
15922     canActivate : true,
15923     /**
15924      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15925      */
15926     showDelay: 200,
15927     // doc'd in BaseItem
15928     hideDelay: 200,
15929
15930     // private
15931     ctype: "Roo.menu.Item",
15932     
15933     // private
15934     onRender : function(container, position){
15935         var el = document.createElement("a");
15936         el.hideFocus = true;
15937         el.unselectable = "on";
15938         el.href = this.href || "#";
15939         if(this.hrefTarget){
15940             el.target = this.hrefTarget;
15941         }
15942         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15943         
15944         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15945         
15946         el.innerHTML = String.format(
15947                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15948                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15949         this.el = el;
15950         Roo.menu.Item.superclass.onRender.call(this, container, position);
15951     },
15952
15953     /**
15954      * Sets the text to display in this menu item
15955      * @param {String} text The text to display
15956      * @param {Boolean} isHTML true to indicate text is pure html.
15957      */
15958     setText : function(text, isHTML){
15959         if (isHTML) {
15960             this.html = text;
15961         } else {
15962             this.text = text;
15963             this.html = '';
15964         }
15965         if(this.rendered){
15966             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15967      
15968             this.el.update(String.format(
15969                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15970                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15971             this.parentMenu.autoWidth();
15972         }
15973     },
15974
15975     // private
15976     handleClick : function(e){
15977         if(!this.href){ // if no link defined, stop the event automatically
15978             e.stopEvent();
15979         }
15980         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15981     },
15982
15983     // private
15984     activate : function(autoExpand){
15985         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15986             this.focus();
15987             if(autoExpand){
15988                 this.expandMenu();
15989             }
15990         }
15991         return true;
15992     },
15993
15994     // private
15995     shouldDeactivate : function(e){
15996         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15997             if(this.menu && this.menu.isVisible()){
15998                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15999             }
16000             return true;
16001         }
16002         return false;
16003     },
16004
16005     // private
16006     deactivate : function(){
16007         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
16008         this.hideMenu();
16009     },
16010
16011     // private
16012     expandMenu : function(autoActivate){
16013         if(!this.disabled && this.menu){
16014             clearTimeout(this.hideTimer);
16015             delete this.hideTimer;
16016             if(!this.menu.isVisible() && !this.showTimer){
16017                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16018             }else if (this.menu.isVisible() && autoActivate){
16019                 this.menu.tryActivate(0, 1);
16020             }
16021         }
16022     },
16023
16024     // private
16025     deferExpand : function(autoActivate){
16026         delete this.showTimer;
16027         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16028         if(autoActivate){
16029             this.menu.tryActivate(0, 1);
16030         }
16031     },
16032
16033     // private
16034     hideMenu : function(){
16035         clearTimeout(this.showTimer);
16036         delete this.showTimer;
16037         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16038             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16039         }
16040     },
16041
16042     // private
16043     deferHide : function(){
16044         delete this.hideTimer;
16045         this.menu.hide();
16046     }
16047 });/*
16048  * Based on:
16049  * Ext JS Library 1.1.1
16050  * Copyright(c) 2006-2007, Ext JS, LLC.
16051  *
16052  * Originally Released Under LGPL - original licence link has changed is not relivant.
16053  *
16054  * Fork - LGPL
16055  * <script type="text/javascript">
16056  */
16057  
16058 /**
16059  * @class Roo.menu.CheckItem
16060  * @extends Roo.menu.Item
16061  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16062  * @constructor
16063  * Creates a new CheckItem
16064  * @param {Object} config Configuration options
16065  */
16066 Roo.menu.CheckItem = function(config){
16067     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16068     this.addEvents({
16069         /**
16070          * @event beforecheckchange
16071          * Fires before the checked value is set, providing an opportunity to cancel if needed
16072          * @param {Roo.menu.CheckItem} this
16073          * @param {Boolean} checked The new checked value that will be set
16074          */
16075         "beforecheckchange" : true,
16076         /**
16077          * @event checkchange
16078          * Fires after the checked value has been set
16079          * @param {Roo.menu.CheckItem} this
16080          * @param {Boolean} checked The checked value that was set
16081          */
16082         "checkchange" : true
16083     });
16084     if(this.checkHandler){
16085         this.on('checkchange', this.checkHandler, this.scope);
16086     }
16087 };
16088 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16089     /**
16090      * @cfg {String} group
16091      * All check items with the same group name will automatically be grouped into a single-select
16092      * radio button group (defaults to '')
16093      */
16094     /**
16095      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16096      */
16097     itemCls : "x-menu-item x-menu-check-item",
16098     /**
16099      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16100      */
16101     groupClass : "x-menu-group-item",
16102
16103     /**
16104      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16105      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16106      * initialized with checked = true will be rendered as checked.
16107      */
16108     checked: false,
16109
16110     // private
16111     ctype: "Roo.menu.CheckItem",
16112
16113     // private
16114     onRender : function(c){
16115         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16116         if(this.group){
16117             this.el.addClass(this.groupClass);
16118         }
16119         Roo.menu.MenuMgr.registerCheckable(this);
16120         if(this.checked){
16121             this.checked = false;
16122             this.setChecked(true, true);
16123         }
16124     },
16125
16126     // private
16127     destroy : function(){
16128         if(this.rendered){
16129             Roo.menu.MenuMgr.unregisterCheckable(this);
16130         }
16131         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16132     },
16133
16134     /**
16135      * Set the checked state of this item
16136      * @param {Boolean} checked The new checked value
16137      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16138      */
16139     setChecked : function(state, suppressEvent){
16140         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16141             if(this.container){
16142                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16143             }
16144             this.checked = state;
16145             if(suppressEvent !== true){
16146                 this.fireEvent("checkchange", this, state);
16147             }
16148         }
16149     },
16150
16151     // private
16152     handleClick : function(e){
16153        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16154            this.setChecked(!this.checked);
16155        }
16156        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16157     }
16158 });/*
16159  * Based on:
16160  * Ext JS Library 1.1.1
16161  * Copyright(c) 2006-2007, Ext JS, LLC.
16162  *
16163  * Originally Released Under LGPL - original licence link has changed is not relivant.
16164  *
16165  * Fork - LGPL
16166  * <script type="text/javascript">
16167  */
16168  
16169 /**
16170  * @class Roo.menu.DateItem
16171  * @extends Roo.menu.Adapter
16172  * A menu item that wraps the {@link Roo.DatPicker} component.
16173  * @constructor
16174  * Creates a new DateItem
16175  * @param {Object} config Configuration options
16176  */
16177 Roo.menu.DateItem = function(config){
16178     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16179     /** The Roo.DatePicker object @type Roo.DatePicker */
16180     this.picker = this.component;
16181     this.addEvents({select: true});
16182     
16183     this.picker.on("render", function(picker){
16184         picker.getEl().swallowEvent("click");
16185         picker.container.addClass("x-menu-date-item");
16186     });
16187
16188     this.picker.on("select", this.onSelect, this);
16189 };
16190
16191 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16192     // private
16193     onSelect : function(picker, date){
16194         this.fireEvent("select", this, date, picker);
16195         Roo.menu.DateItem.superclass.handleClick.call(this);
16196     }
16197 });/*
16198  * Based on:
16199  * Ext JS Library 1.1.1
16200  * Copyright(c) 2006-2007, Ext JS, LLC.
16201  *
16202  * Originally Released Under LGPL - original licence link has changed is not relivant.
16203  *
16204  * Fork - LGPL
16205  * <script type="text/javascript">
16206  */
16207  
16208 /**
16209  * @class Roo.menu.ColorItem
16210  * @extends Roo.menu.Adapter
16211  * A menu item that wraps the {@link Roo.ColorPalette} component.
16212  * @constructor
16213  * Creates a new ColorItem
16214  * @param {Object} config Configuration options
16215  */
16216 Roo.menu.ColorItem = function(config){
16217     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16218     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16219     this.palette = this.component;
16220     this.relayEvents(this.palette, ["select"]);
16221     if(this.selectHandler){
16222         this.on('select', this.selectHandler, this.scope);
16223     }
16224 };
16225 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16226  * Based on:
16227  * Ext JS Library 1.1.1
16228  * Copyright(c) 2006-2007, Ext JS, LLC.
16229  *
16230  * Originally Released Under LGPL - original licence link has changed is not relivant.
16231  *
16232  * Fork - LGPL
16233  * <script type="text/javascript">
16234  */
16235  
16236
16237 /**
16238  * @class Roo.menu.DateMenu
16239  * @extends Roo.menu.Menu
16240  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16241  * @constructor
16242  * Creates a new DateMenu
16243  * @param {Object} config Configuration options
16244  */
16245 Roo.menu.DateMenu = function(config){
16246     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16247     this.plain = true;
16248     var di = new Roo.menu.DateItem(config);
16249     this.add(di);
16250     /**
16251      * The {@link Roo.DatePicker} instance for this DateMenu
16252      * @type DatePicker
16253      */
16254     this.picker = di.picker;
16255     /**
16256      * @event select
16257      * @param {DatePicker} picker
16258      * @param {Date} date
16259      */
16260     this.relayEvents(di, ["select"]);
16261     this.on('beforeshow', function(){
16262         if(this.picker){
16263             this.picker.hideMonthPicker(false);
16264         }
16265     }, this);
16266 };
16267 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16268     cls:'x-date-menu'
16269 });/*
16270  * Based on:
16271  * Ext JS Library 1.1.1
16272  * Copyright(c) 2006-2007, Ext JS, LLC.
16273  *
16274  * Originally Released Under LGPL - original licence link has changed is not relivant.
16275  *
16276  * Fork - LGPL
16277  * <script type="text/javascript">
16278  */
16279  
16280
16281 /**
16282  * @class Roo.menu.ColorMenu
16283  * @extends Roo.menu.Menu
16284  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16285  * @constructor
16286  * Creates a new ColorMenu
16287  * @param {Object} config Configuration options
16288  */
16289 Roo.menu.ColorMenu = function(config){
16290     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16291     this.plain = true;
16292     var ci = new Roo.menu.ColorItem(config);
16293     this.add(ci);
16294     /**
16295      * The {@link Roo.ColorPalette} instance for this ColorMenu
16296      * @type ColorPalette
16297      */
16298     this.palette = ci.palette;
16299     /**
16300      * @event select
16301      * @param {ColorPalette} palette
16302      * @param {String} color
16303      */
16304     this.relayEvents(ci, ["select"]);
16305 };
16306 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16307  * Based on:
16308  * Ext JS Library 1.1.1
16309  * Copyright(c) 2006-2007, Ext JS, LLC.
16310  *
16311  * Originally Released Under LGPL - original licence link has changed is not relivant.
16312  *
16313  * Fork - LGPL
16314  * <script type="text/javascript">
16315  */
16316  
16317 /**
16318  * @class Roo.form.TextItem
16319  * @extends Roo.BoxComponent
16320  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16321  * @constructor
16322  * Creates a new TextItem
16323  * @param {Object} config Configuration options
16324  */
16325 Roo.form.TextItem = function(config){
16326     Roo.form.TextItem.superclass.constructor.call(this, config);
16327 };
16328
16329 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16330     
16331     /**
16332      * @cfg {String} tag the tag for this item (default div)
16333      */
16334     tag : 'div',
16335     /**
16336      * @cfg {String} html the content for this item
16337      */
16338     html : '',
16339     
16340     getAutoCreate : function()
16341     {
16342         var cfg = {
16343             id: this.id,
16344             tag: this.tag,
16345             html: this.html,
16346             cls: 'x-form-item'
16347         };
16348         
16349         return cfg;
16350         
16351     },
16352     
16353     onRender : function(ct, position)
16354     {
16355         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16356         
16357         if(!this.el){
16358             var cfg = this.getAutoCreate();
16359             if(!cfg.name){
16360                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16361             }
16362             if (!cfg.name.length) {
16363                 delete cfg.name;
16364             }
16365             this.el = ct.createChild(cfg, position);
16366         }
16367     }
16368     
16369 });/*
16370  * Based on:
16371  * Ext JS Library 1.1.1
16372  * Copyright(c) 2006-2007, Ext JS, LLC.
16373  *
16374  * Originally Released Under LGPL - original licence link has changed is not relivant.
16375  *
16376  * Fork - LGPL
16377  * <script type="text/javascript">
16378  */
16379  
16380 /**
16381  * @class Roo.form.Field
16382  * @extends Roo.BoxComponent
16383  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16384  * @constructor
16385  * Creates a new Field
16386  * @param {Object} config Configuration options
16387  */
16388 Roo.form.Field = function(config){
16389     Roo.form.Field.superclass.constructor.call(this, config);
16390 };
16391
16392 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16393     /**
16394      * @cfg {String} fieldLabel Label to use when rendering a form.
16395      */
16396        /**
16397      * @cfg {String} qtip Mouse over tip
16398      */
16399      
16400     /**
16401      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16402      */
16403     invalidClass : "x-form-invalid",
16404     /**
16405      * @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")
16406      */
16407     invalidText : "The value in this field is invalid",
16408     /**
16409      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16410      */
16411     focusClass : "x-form-focus",
16412     /**
16413      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16414       automatic validation (defaults to "keyup").
16415      */
16416     validationEvent : "keyup",
16417     /**
16418      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16419      */
16420     validateOnBlur : true,
16421     /**
16422      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16423      */
16424     validationDelay : 250,
16425     /**
16426      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16427      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16428      */
16429     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16430     /**
16431      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16432      */
16433     fieldClass : "x-form-field",
16434     /**
16435      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16436      *<pre>
16437 Value         Description
16438 -----------   ----------------------------------------------------------------------
16439 qtip          Display a quick tip when the user hovers over the field
16440 title         Display a default browser title attribute popup
16441 under         Add a block div beneath the field containing the error text
16442 side          Add an error icon to the right of the field with a popup on hover
16443 [element id]  Add the error text directly to the innerHTML of the specified element
16444 </pre>
16445      */
16446     msgTarget : 'qtip',
16447     /**
16448      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16449      */
16450     msgFx : 'normal',
16451
16452     /**
16453      * @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.
16454      */
16455     readOnly : false,
16456
16457     /**
16458      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16459      */
16460     disabled : false,
16461
16462     /**
16463      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16464      */
16465     inputType : undefined,
16466     
16467     /**
16468      * @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).
16469          */
16470         tabIndex : undefined,
16471         
16472     // private
16473     isFormField : true,
16474
16475     // private
16476     hasFocus : false,
16477     /**
16478      * @property {Roo.Element} fieldEl
16479      * Element Containing the rendered Field (with label etc.)
16480      */
16481     /**
16482      * @cfg {Mixed} value A value to initialize this field with.
16483      */
16484     value : undefined,
16485
16486     /**
16487      * @cfg {String} name The field's HTML name attribute.
16488      */
16489     /**
16490      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16491      */
16492     // private
16493     loadedValue : false,
16494      
16495      
16496         // private ??
16497         initComponent : function(){
16498         Roo.form.Field.superclass.initComponent.call(this);
16499         this.addEvents({
16500             /**
16501              * @event focus
16502              * Fires when this field receives input focus.
16503              * @param {Roo.form.Field} this
16504              */
16505             focus : true,
16506             /**
16507              * @event blur
16508              * Fires when this field loses input focus.
16509              * @param {Roo.form.Field} this
16510              */
16511             blur : true,
16512             /**
16513              * @event specialkey
16514              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16515              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16516              * @param {Roo.form.Field} this
16517              * @param {Roo.EventObject} e The event object
16518              */
16519             specialkey : true,
16520             /**
16521              * @event change
16522              * Fires just before the field blurs if the field value has changed.
16523              * @param {Roo.form.Field} this
16524              * @param {Mixed} newValue The new value
16525              * @param {Mixed} oldValue The original value
16526              */
16527             change : true,
16528             /**
16529              * @event invalid
16530              * Fires after the field has been marked as invalid.
16531              * @param {Roo.form.Field} this
16532              * @param {String} msg The validation message
16533              */
16534             invalid : true,
16535             /**
16536              * @event valid
16537              * Fires after the field has been validated with no errors.
16538              * @param {Roo.form.Field} this
16539              */
16540             valid : true,
16541              /**
16542              * @event keyup
16543              * Fires after the key up
16544              * @param {Roo.form.Field} this
16545              * @param {Roo.EventObject}  e The event Object
16546              */
16547             keyup : true
16548         });
16549     },
16550
16551     /**
16552      * Returns the name attribute of the field if available
16553      * @return {String} name The field name
16554      */
16555     getName: function(){
16556          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16557     },
16558
16559     // private
16560     onRender : function(ct, position){
16561         Roo.form.Field.superclass.onRender.call(this, ct, position);
16562         if(!this.el){
16563             var cfg = this.getAutoCreate();
16564             if(!cfg.name){
16565                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16566             }
16567             if (!cfg.name.length) {
16568                 delete cfg.name;
16569             }
16570             if(this.inputType){
16571                 cfg.type = this.inputType;
16572             }
16573             this.el = ct.createChild(cfg, position);
16574         }
16575         var type = this.el.dom.type;
16576         if(type){
16577             if(type == 'password'){
16578                 type = 'text';
16579             }
16580             this.el.addClass('x-form-'+type);
16581         }
16582         if(this.readOnly){
16583             this.el.dom.readOnly = true;
16584         }
16585         if(this.tabIndex !== undefined){
16586             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16587         }
16588
16589         this.el.addClass([this.fieldClass, this.cls]);
16590         this.initValue();
16591     },
16592
16593     /**
16594      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16595      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16596      * @return {Roo.form.Field} this
16597      */
16598     applyTo : function(target){
16599         this.allowDomMove = false;
16600         this.el = Roo.get(target);
16601         this.render(this.el.dom.parentNode);
16602         return this;
16603     },
16604
16605     // private
16606     initValue : function(){
16607         if(this.value !== undefined){
16608             this.setValue(this.value);
16609         }else if(this.el.dom.value.length > 0){
16610             this.setValue(this.el.dom.value);
16611         }
16612     },
16613
16614     /**
16615      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16616      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16617      */
16618     isDirty : function() {
16619         if(this.disabled) {
16620             return false;
16621         }
16622         return String(this.getValue()) !== String(this.originalValue);
16623     },
16624
16625     /**
16626      * stores the current value in loadedValue
16627      */
16628     resetHasChanged : function()
16629     {
16630         this.loadedValue = String(this.getValue());
16631     },
16632     /**
16633      * checks the current value against the 'loaded' value.
16634      * Note - will return false if 'resetHasChanged' has not been called first.
16635      */
16636     hasChanged : function()
16637     {
16638         if(this.disabled || this.readOnly) {
16639             return false;
16640         }
16641         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16642     },
16643     
16644     
16645     
16646     // private
16647     afterRender : function(){
16648         Roo.form.Field.superclass.afterRender.call(this);
16649         this.initEvents();
16650     },
16651
16652     // private
16653     fireKey : function(e){
16654         //Roo.log('field ' + e.getKey());
16655         if(e.isNavKeyPress()){
16656             this.fireEvent("specialkey", this, e);
16657         }
16658     },
16659
16660     /**
16661      * Resets the current field value to the originally loaded value and clears any validation messages
16662      */
16663     reset : function(){
16664         this.setValue(this.resetValue);
16665         this.originalValue = this.getValue();
16666         this.clearInvalid();
16667     },
16668
16669     // private
16670     initEvents : function(){
16671         // safari killled keypress - so keydown is now used..
16672         this.el.on("keydown" , this.fireKey,  this);
16673         this.el.on("focus", this.onFocus,  this);
16674         this.el.on("blur", this.onBlur,  this);
16675         this.el.relayEvent('keyup', this);
16676
16677         // reference to original value for reset
16678         this.originalValue = this.getValue();
16679         this.resetValue =  this.getValue();
16680     },
16681
16682     // private
16683     onFocus : function(){
16684         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16685             this.el.addClass(this.focusClass);
16686         }
16687         if(!this.hasFocus){
16688             this.hasFocus = true;
16689             this.startValue = this.getValue();
16690             this.fireEvent("focus", this);
16691         }
16692     },
16693
16694     beforeBlur : Roo.emptyFn,
16695
16696     // private
16697     onBlur : function(){
16698         this.beforeBlur();
16699         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16700             this.el.removeClass(this.focusClass);
16701         }
16702         this.hasFocus = false;
16703         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16704             this.validate();
16705         }
16706         var v = this.getValue();
16707         if(String(v) !== String(this.startValue)){
16708             this.fireEvent('change', this, v, this.startValue);
16709         }
16710         this.fireEvent("blur", this);
16711     },
16712
16713     /**
16714      * Returns whether or not the field value is currently valid
16715      * @param {Boolean} preventMark True to disable marking the field invalid
16716      * @return {Boolean} True if the value is valid, else false
16717      */
16718     isValid : function(preventMark){
16719         if(this.disabled){
16720             return true;
16721         }
16722         var restore = this.preventMark;
16723         this.preventMark = preventMark === true;
16724         var v = this.validateValue(this.processValue(this.getRawValue()));
16725         this.preventMark = restore;
16726         return v;
16727     },
16728
16729     /**
16730      * Validates the field value
16731      * @return {Boolean} True if the value is valid, else false
16732      */
16733     validate : function(){
16734         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16735             this.clearInvalid();
16736             return true;
16737         }
16738         return false;
16739     },
16740
16741     processValue : function(value){
16742         return value;
16743     },
16744
16745     // private
16746     // Subclasses should provide the validation implementation by overriding this
16747     validateValue : function(value){
16748         return true;
16749     },
16750
16751     /**
16752      * Mark this field as invalid
16753      * @param {String} msg The validation message
16754      */
16755     markInvalid : function(msg){
16756         if(!this.rendered || this.preventMark){ // not rendered
16757             return;
16758         }
16759         
16760         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16761         
16762         obj.el.addClass(this.invalidClass);
16763         msg = msg || this.invalidText;
16764         switch(this.msgTarget){
16765             case 'qtip':
16766                 obj.el.dom.qtip = msg;
16767                 obj.el.dom.qclass = 'x-form-invalid-tip';
16768                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16769                     Roo.QuickTips.enable();
16770                 }
16771                 break;
16772             case 'title':
16773                 this.el.dom.title = msg;
16774                 break;
16775             case 'under':
16776                 if(!this.errorEl){
16777                     var elp = this.el.findParent('.x-form-element', 5, true);
16778                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16779                     this.errorEl.setWidth(elp.getWidth(true)-20);
16780                 }
16781                 this.errorEl.update(msg);
16782                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16783                 break;
16784             case 'side':
16785                 if(!this.errorIcon){
16786                     var elp = this.el.findParent('.x-form-element', 5, true);
16787                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16788                 }
16789                 this.alignErrorIcon();
16790                 this.errorIcon.dom.qtip = msg;
16791                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16792                 this.errorIcon.show();
16793                 this.on('resize', this.alignErrorIcon, this);
16794                 break;
16795             default:
16796                 var t = Roo.getDom(this.msgTarget);
16797                 t.innerHTML = msg;
16798                 t.style.display = this.msgDisplay;
16799                 break;
16800         }
16801         this.fireEvent('invalid', this, msg);
16802     },
16803
16804     // private
16805     alignErrorIcon : function(){
16806         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16807     },
16808
16809     /**
16810      * Clear any invalid styles/messages for this field
16811      */
16812     clearInvalid : function(){
16813         if(!this.rendered || this.preventMark){ // not rendered
16814             return;
16815         }
16816         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16817         
16818         obj.el.removeClass(this.invalidClass);
16819         switch(this.msgTarget){
16820             case 'qtip':
16821                 obj.el.dom.qtip = '';
16822                 break;
16823             case 'title':
16824                 this.el.dom.title = '';
16825                 break;
16826             case 'under':
16827                 if(this.errorEl){
16828                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16829                 }
16830                 break;
16831             case 'side':
16832                 if(this.errorIcon){
16833                     this.errorIcon.dom.qtip = '';
16834                     this.errorIcon.hide();
16835                     this.un('resize', this.alignErrorIcon, this);
16836                 }
16837                 break;
16838             default:
16839                 var t = Roo.getDom(this.msgTarget);
16840                 t.innerHTML = '';
16841                 t.style.display = 'none';
16842                 break;
16843         }
16844         this.fireEvent('valid', this);
16845     },
16846
16847     /**
16848      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16849      * @return {Mixed} value The field value
16850      */
16851     getRawValue : function(){
16852         var v = this.el.getValue();
16853         
16854         return v;
16855     },
16856
16857     /**
16858      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16859      * @return {Mixed} value The field value
16860      */
16861     getValue : function(){
16862         var v = this.el.getValue();
16863          
16864         return v;
16865     },
16866
16867     /**
16868      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16869      * @param {Mixed} value The value to set
16870      */
16871     setRawValue : function(v){
16872         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16873     },
16874
16875     /**
16876      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16877      * @param {Mixed} value The value to set
16878      */
16879     setValue : function(v){
16880         this.value = v;
16881         if(this.rendered){
16882             this.el.dom.value = (v === null || v === undefined ? '' : v);
16883              this.validate();
16884         }
16885     },
16886
16887     adjustSize : function(w, h){
16888         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16889         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16890         return s;
16891     },
16892
16893     adjustWidth : function(tag, w){
16894         tag = tag.toLowerCase();
16895         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16896             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16897                 if(tag == 'input'){
16898                     return w + 2;
16899                 }
16900                 if(tag == 'textarea'){
16901                     return w-2;
16902                 }
16903             }else if(Roo.isOpera){
16904                 if(tag == 'input'){
16905                     return w + 2;
16906                 }
16907                 if(tag == 'textarea'){
16908                     return w-2;
16909                 }
16910             }
16911         }
16912         return w;
16913     }
16914 });
16915
16916
16917 // anything other than normal should be considered experimental
16918 Roo.form.Field.msgFx = {
16919     normal : {
16920         show: function(msgEl, f){
16921             msgEl.setDisplayed('block');
16922         },
16923
16924         hide : function(msgEl, f){
16925             msgEl.setDisplayed(false).update('');
16926         }
16927     },
16928
16929     slide : {
16930         show: function(msgEl, f){
16931             msgEl.slideIn('t', {stopFx:true});
16932         },
16933
16934         hide : function(msgEl, f){
16935             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16936         }
16937     },
16938
16939     slideRight : {
16940         show: function(msgEl, f){
16941             msgEl.fixDisplay();
16942             msgEl.alignTo(f.el, 'tl-tr');
16943             msgEl.slideIn('l', {stopFx:true});
16944         },
16945
16946         hide : function(msgEl, f){
16947             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16948         }
16949     }
16950 };/*
16951  * Based on:
16952  * Ext JS Library 1.1.1
16953  * Copyright(c) 2006-2007, Ext JS, LLC.
16954  *
16955  * Originally Released Under LGPL - original licence link has changed is not relivant.
16956  *
16957  * Fork - LGPL
16958  * <script type="text/javascript">
16959  */
16960  
16961
16962 /**
16963  * @class Roo.form.TextField
16964  * @extends Roo.form.Field
16965  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16966  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16967  * @constructor
16968  * Creates a new TextField
16969  * @param {Object} config Configuration options
16970  */
16971 Roo.form.TextField = function(config){
16972     Roo.form.TextField.superclass.constructor.call(this, config);
16973     this.addEvents({
16974         /**
16975          * @event autosize
16976          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16977          * according to the default logic, but this event provides a hook for the developer to apply additional
16978          * logic at runtime to resize the field if needed.
16979              * @param {Roo.form.Field} this This text field
16980              * @param {Number} width The new field width
16981              */
16982         autosize : true
16983     });
16984 };
16985
16986 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16987     /**
16988      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16989      */
16990     grow : false,
16991     /**
16992      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16993      */
16994     growMin : 30,
16995     /**
16996      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16997      */
16998     growMax : 800,
16999     /**
17000      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
17001      */
17002     vtype : null,
17003     /**
17004      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
17005      */
17006     maskRe : null,
17007     /**
17008      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
17009      */
17010     disableKeyFilter : false,
17011     /**
17012      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
17013      */
17014     allowBlank : true,
17015     /**
17016      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
17017      */
17018     minLength : 0,
17019     /**
17020      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17021      */
17022     maxLength : Number.MAX_VALUE,
17023     /**
17024      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17025      */
17026     minLengthText : "The minimum length for this field is {0}",
17027     /**
17028      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17029      */
17030     maxLengthText : "The maximum length for this field is {0}",
17031     /**
17032      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17033      */
17034     selectOnFocus : false,
17035     /**
17036      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17037      */    
17038     allowLeadingSpace : false,
17039     /**
17040      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17041      */
17042     blankText : "This field is required",
17043     /**
17044      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17045      * If available, this function will be called only after the basic validators all return true, and will be passed the
17046      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17047      */
17048     validator : null,
17049     /**
17050      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17051      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17052      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17053      */
17054     regex : null,
17055     /**
17056      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17057      */
17058     regexText : "",
17059     /**
17060      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17061      */
17062     emptyText : null,
17063    
17064
17065     // private
17066     initEvents : function()
17067     {
17068         if (this.emptyText) {
17069             this.el.attr('placeholder', this.emptyText);
17070         }
17071         
17072         Roo.form.TextField.superclass.initEvents.call(this);
17073         if(this.validationEvent == 'keyup'){
17074             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17075             this.el.on('keyup', this.filterValidation, this);
17076         }
17077         else if(this.validationEvent !== false){
17078             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17079         }
17080         
17081         if(this.selectOnFocus){
17082             this.on("focus", this.preFocus, this);
17083         }
17084         if (!this.allowLeadingSpace) {
17085             this.on('blur', this.cleanLeadingSpace, this);
17086         }
17087         
17088         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17089             this.el.on("keypress", this.filterKeys, this);
17090         }
17091         if(this.grow){
17092             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17093             this.el.on("click", this.autoSize,  this);
17094         }
17095         if(this.el.is('input[type=password]') && Roo.isSafari){
17096             this.el.on('keydown', this.SafariOnKeyDown, this);
17097         }
17098     },
17099
17100     processValue : function(value){
17101         if(this.stripCharsRe){
17102             var newValue = value.replace(this.stripCharsRe, '');
17103             if(newValue !== value){
17104                 this.setRawValue(newValue);
17105                 return newValue;
17106             }
17107         }
17108         return value;
17109     },
17110
17111     filterValidation : function(e){
17112         if(!e.isNavKeyPress()){
17113             this.validationTask.delay(this.validationDelay);
17114         }
17115     },
17116
17117     // private
17118     onKeyUp : function(e){
17119         if(!e.isNavKeyPress()){
17120             this.autoSize();
17121         }
17122     },
17123     // private - clean the leading white space
17124     cleanLeadingSpace : function(e)
17125     {
17126         if ( this.inputType == 'file') {
17127             return;
17128         }
17129         
17130         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17131     },
17132     /**
17133      * Resets the current field value to the originally-loaded value and clears any validation messages.
17134      *  
17135      */
17136     reset : function(){
17137         Roo.form.TextField.superclass.reset.call(this);
17138        
17139     }, 
17140     // private
17141     preFocus : function(){
17142         
17143         if(this.selectOnFocus){
17144             this.el.dom.select();
17145         }
17146     },
17147
17148     
17149     // private
17150     filterKeys : function(e){
17151         var k = e.getKey();
17152         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17153             return;
17154         }
17155         var c = e.getCharCode(), cc = String.fromCharCode(c);
17156         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17157             return;
17158         }
17159         if(!this.maskRe.test(cc)){
17160             e.stopEvent();
17161         }
17162     },
17163
17164     setValue : function(v){
17165         
17166         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17167         
17168         this.autoSize();
17169     },
17170
17171     /**
17172      * Validates a value according to the field's validation rules and marks the field as invalid
17173      * if the validation fails
17174      * @param {Mixed} value The value to validate
17175      * @return {Boolean} True if the value is valid, else false
17176      */
17177     validateValue : function(value){
17178         if(value.length < 1)  { // if it's blank
17179              if(this.allowBlank){
17180                 this.clearInvalid();
17181                 return true;
17182              }else{
17183                 this.markInvalid(this.blankText);
17184                 return false;
17185              }
17186         }
17187         if(value.length < this.minLength){
17188             this.markInvalid(String.format(this.minLengthText, this.minLength));
17189             return false;
17190         }
17191         if(value.length > this.maxLength){
17192             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17193             return false;
17194         }
17195         if(this.vtype){
17196             var vt = Roo.form.VTypes;
17197             if(!vt[this.vtype](value, this)){
17198                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17199                 return false;
17200             }
17201         }
17202         if(typeof this.validator == "function"){
17203             var msg = this.validator(value);
17204             if(msg !== true){
17205                 this.markInvalid(msg);
17206                 return false;
17207             }
17208         }
17209         if(this.regex && !this.regex.test(value)){
17210             this.markInvalid(this.regexText);
17211             return false;
17212         }
17213         return true;
17214     },
17215
17216     /**
17217      * Selects text in this field
17218      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17219      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17220      */
17221     selectText : function(start, end){
17222         var v = this.getRawValue();
17223         if(v.length > 0){
17224             start = start === undefined ? 0 : start;
17225             end = end === undefined ? v.length : end;
17226             var d = this.el.dom;
17227             if(d.setSelectionRange){
17228                 d.setSelectionRange(start, end);
17229             }else if(d.createTextRange){
17230                 var range = d.createTextRange();
17231                 range.moveStart("character", start);
17232                 range.moveEnd("character", v.length-end);
17233                 range.select();
17234             }
17235         }
17236     },
17237
17238     /**
17239      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17240      * This only takes effect if grow = true, and fires the autosize event.
17241      */
17242     autoSize : function(){
17243         if(!this.grow || !this.rendered){
17244             return;
17245         }
17246         if(!this.metrics){
17247             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17248         }
17249         var el = this.el;
17250         var v = el.dom.value;
17251         var d = document.createElement('div');
17252         d.appendChild(document.createTextNode(v));
17253         v = d.innerHTML;
17254         d = null;
17255         v += "&#160;";
17256         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17257         this.el.setWidth(w);
17258         this.fireEvent("autosize", this, w);
17259     },
17260     
17261     // private
17262     SafariOnKeyDown : function(event)
17263     {
17264         // this is a workaround for a password hang bug on chrome/ webkit.
17265         
17266         var isSelectAll = false;
17267         
17268         if(this.el.dom.selectionEnd > 0){
17269             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17270         }
17271         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17272             event.preventDefault();
17273             this.setValue('');
17274             return;
17275         }
17276         
17277         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17278             
17279             event.preventDefault();
17280             // this is very hacky as keydown always get's upper case.
17281             
17282             var cc = String.fromCharCode(event.getCharCode());
17283             
17284             
17285             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17286             
17287         }
17288         
17289         
17290     }
17291 });/*
17292  * Based on:
17293  * Ext JS Library 1.1.1
17294  * Copyright(c) 2006-2007, Ext JS, LLC.
17295  *
17296  * Originally Released Under LGPL - original licence link has changed is not relivant.
17297  *
17298  * Fork - LGPL
17299  * <script type="text/javascript">
17300  */
17301  
17302 /**
17303  * @class Roo.form.Hidden
17304  * @extends Roo.form.TextField
17305  * Simple Hidden element used on forms 
17306  * 
17307  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17308  * 
17309  * @constructor
17310  * Creates a new Hidden form element.
17311  * @param {Object} config Configuration options
17312  */
17313
17314
17315
17316 // easy hidden field...
17317 Roo.form.Hidden = function(config){
17318     Roo.form.Hidden.superclass.constructor.call(this, config);
17319 };
17320   
17321 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17322     fieldLabel:      '',
17323     inputType:      'hidden',
17324     width:          50,
17325     allowBlank:     true,
17326     labelSeparator: '',
17327     hidden:         true,
17328     itemCls :       'x-form-item-display-none'
17329
17330
17331 });
17332
17333
17334 /*
17335  * Based on:
17336  * Ext JS Library 1.1.1
17337  * Copyright(c) 2006-2007, Ext JS, LLC.
17338  *
17339  * Originally Released Under LGPL - original licence link has changed is not relivant.
17340  *
17341  * Fork - LGPL
17342  * <script type="text/javascript">
17343  */
17344  
17345 /**
17346  * @class Roo.form.TriggerField
17347  * @extends Roo.form.TextField
17348  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17349  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17350  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17351  * for which you can provide a custom implementation.  For example:
17352  * <pre><code>
17353 var trigger = new Roo.form.TriggerField();
17354 trigger.onTriggerClick = myTriggerFn;
17355 trigger.applyTo('my-field');
17356 </code></pre>
17357  *
17358  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17359  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17360  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17361  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17362  * @constructor
17363  * Create a new TriggerField.
17364  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17365  * to the base TextField)
17366  */
17367 Roo.form.TriggerField = function(config){
17368     this.mimicing = false;
17369     Roo.form.TriggerField.superclass.constructor.call(this, config);
17370 };
17371
17372 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17373     /**
17374      * @cfg {String} triggerClass A CSS class to apply to the trigger
17375      */
17376     /**
17377      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17378      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17379      */
17380     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17381     /**
17382      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17383      */
17384     hideTrigger:false,
17385
17386     /** @cfg {Boolean} grow @hide */
17387     /** @cfg {Number} growMin @hide */
17388     /** @cfg {Number} growMax @hide */
17389
17390     /**
17391      * @hide 
17392      * @method
17393      */
17394     autoSize: Roo.emptyFn,
17395     // private
17396     monitorTab : true,
17397     // private
17398     deferHeight : true,
17399
17400     
17401     actionMode : 'wrap',
17402     // private
17403     onResize : function(w, h){
17404         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17405         if(typeof w == 'number'){
17406             var x = w - this.trigger.getWidth();
17407             this.el.setWidth(this.adjustWidth('input', x));
17408             this.trigger.setStyle('left', x+'px');
17409         }
17410     },
17411
17412     // private
17413     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17414
17415     // private
17416     getResizeEl : function(){
17417         return this.wrap;
17418     },
17419
17420     // private
17421     getPositionEl : function(){
17422         return this.wrap;
17423     },
17424
17425     // private
17426     alignErrorIcon : function(){
17427         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17428     },
17429
17430     // private
17431     onRender : function(ct, position){
17432         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17433         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17434         this.trigger = this.wrap.createChild(this.triggerConfig ||
17435                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17436         if(this.hideTrigger){
17437             this.trigger.setDisplayed(false);
17438         }
17439         this.initTrigger();
17440         if(!this.width){
17441             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17442         }
17443     },
17444
17445     // private
17446     initTrigger : function(){
17447         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17448         this.trigger.addClassOnOver('x-form-trigger-over');
17449         this.trigger.addClassOnClick('x-form-trigger-click');
17450     },
17451
17452     // private
17453     onDestroy : function(){
17454         if(this.trigger){
17455             this.trigger.removeAllListeners();
17456             this.trigger.remove();
17457         }
17458         if(this.wrap){
17459             this.wrap.remove();
17460         }
17461         Roo.form.TriggerField.superclass.onDestroy.call(this);
17462     },
17463
17464     // private
17465     onFocus : function(){
17466         Roo.form.TriggerField.superclass.onFocus.call(this);
17467         if(!this.mimicing){
17468             this.wrap.addClass('x-trigger-wrap-focus');
17469             this.mimicing = true;
17470             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17471             if(this.monitorTab){
17472                 this.el.on("keydown", this.checkTab, this);
17473             }
17474         }
17475     },
17476
17477     // private
17478     checkTab : function(e){
17479         if(e.getKey() == e.TAB){
17480             this.triggerBlur();
17481         }
17482     },
17483
17484     // private
17485     onBlur : function(){
17486         // do nothing
17487     },
17488
17489     // private
17490     mimicBlur : function(e, t){
17491         if(!this.wrap.contains(t) && this.validateBlur()){
17492             this.triggerBlur();
17493         }
17494     },
17495
17496     // private
17497     triggerBlur : function(){
17498         this.mimicing = false;
17499         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17500         if(this.monitorTab){
17501             this.el.un("keydown", this.checkTab, this);
17502         }
17503         this.wrap.removeClass('x-trigger-wrap-focus');
17504         Roo.form.TriggerField.superclass.onBlur.call(this);
17505     },
17506
17507     // private
17508     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17509     validateBlur : function(e, t){
17510         return true;
17511     },
17512
17513     // private
17514     onDisable : function(){
17515         Roo.form.TriggerField.superclass.onDisable.call(this);
17516         if(this.wrap){
17517             this.wrap.addClass('x-item-disabled');
17518         }
17519     },
17520
17521     // private
17522     onEnable : function(){
17523         Roo.form.TriggerField.superclass.onEnable.call(this);
17524         if(this.wrap){
17525             this.wrap.removeClass('x-item-disabled');
17526         }
17527     },
17528
17529     // private
17530     onShow : function(){
17531         var ae = this.getActionEl();
17532         
17533         if(ae){
17534             ae.dom.style.display = '';
17535             ae.dom.style.visibility = 'visible';
17536         }
17537     },
17538
17539     // private
17540     
17541     onHide : function(){
17542         var ae = this.getActionEl();
17543         ae.dom.style.display = 'none';
17544     },
17545
17546     /**
17547      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17548      * by an implementing function.
17549      * @method
17550      * @param {EventObject} e
17551      */
17552     onTriggerClick : Roo.emptyFn
17553 });
17554
17555 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17556 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17557 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17558 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17559     initComponent : function(){
17560         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17561
17562         this.triggerConfig = {
17563             tag:'span', cls:'x-form-twin-triggers', cn:[
17564             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17565             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17566         ]};
17567     },
17568
17569     getTrigger : function(index){
17570         return this.triggers[index];
17571     },
17572
17573     initTrigger : function(){
17574         var ts = this.trigger.select('.x-form-trigger', true);
17575         this.wrap.setStyle('overflow', 'hidden');
17576         var triggerField = this;
17577         ts.each(function(t, all, index){
17578             t.hide = function(){
17579                 var w = triggerField.wrap.getWidth();
17580                 this.dom.style.display = 'none';
17581                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17582             };
17583             t.show = function(){
17584                 var w = triggerField.wrap.getWidth();
17585                 this.dom.style.display = '';
17586                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17587             };
17588             var triggerIndex = 'Trigger'+(index+1);
17589
17590             if(this['hide'+triggerIndex]){
17591                 t.dom.style.display = 'none';
17592             }
17593             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17594             t.addClassOnOver('x-form-trigger-over');
17595             t.addClassOnClick('x-form-trigger-click');
17596         }, this);
17597         this.triggers = ts.elements;
17598     },
17599
17600     onTrigger1Click : Roo.emptyFn,
17601     onTrigger2Click : Roo.emptyFn
17602 });/*
17603  * Based on:
17604  * Ext JS Library 1.1.1
17605  * Copyright(c) 2006-2007, Ext JS, LLC.
17606  *
17607  * Originally Released Under LGPL - original licence link has changed is not relivant.
17608  *
17609  * Fork - LGPL
17610  * <script type="text/javascript">
17611  */
17612  
17613 /**
17614  * @class Roo.form.TextArea
17615  * @extends Roo.form.TextField
17616  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17617  * support for auto-sizing.
17618  * @constructor
17619  * Creates a new TextArea
17620  * @param {Object} config Configuration options
17621  */
17622 Roo.form.TextArea = function(config){
17623     Roo.form.TextArea.superclass.constructor.call(this, config);
17624     // these are provided exchanges for backwards compat
17625     // minHeight/maxHeight were replaced by growMin/growMax to be
17626     // compatible with TextField growing config values
17627     if(this.minHeight !== undefined){
17628         this.growMin = this.minHeight;
17629     }
17630     if(this.maxHeight !== undefined){
17631         this.growMax = this.maxHeight;
17632     }
17633 };
17634
17635 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17636     /**
17637      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17638      */
17639     growMin : 60,
17640     /**
17641      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17642      */
17643     growMax: 1000,
17644     /**
17645      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17646      * in the field (equivalent to setting overflow: hidden, defaults to false)
17647      */
17648     preventScrollbars: false,
17649     /**
17650      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17651      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17652      */
17653
17654     // private
17655     onRender : function(ct, position){
17656         if(!this.el){
17657             this.defaultAutoCreate = {
17658                 tag: "textarea",
17659                 style:"width:300px;height:60px;",
17660                 autocomplete: "new-password"
17661             };
17662         }
17663         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17664         if(this.grow){
17665             this.textSizeEl = Roo.DomHelper.append(document.body, {
17666                 tag: "pre", cls: "x-form-grow-sizer"
17667             });
17668             if(this.preventScrollbars){
17669                 this.el.setStyle("overflow", "hidden");
17670             }
17671             this.el.setHeight(this.growMin);
17672         }
17673     },
17674
17675     onDestroy : function(){
17676         if(this.textSizeEl){
17677             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17678         }
17679         Roo.form.TextArea.superclass.onDestroy.call(this);
17680     },
17681
17682     // private
17683     onKeyUp : function(e){
17684         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17685             this.autoSize();
17686         }
17687     },
17688
17689     /**
17690      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17691      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17692      */
17693     autoSize : function(){
17694         if(!this.grow || !this.textSizeEl){
17695             return;
17696         }
17697         var el = this.el;
17698         var v = el.dom.value;
17699         var ts = this.textSizeEl;
17700
17701         ts.innerHTML = '';
17702         ts.appendChild(document.createTextNode(v));
17703         v = ts.innerHTML;
17704
17705         Roo.fly(ts).setWidth(this.el.getWidth());
17706         if(v.length < 1){
17707             v = "&#160;&#160;";
17708         }else{
17709             if(Roo.isIE){
17710                 v = v.replace(/\n/g, '<p>&#160;</p>');
17711             }
17712             v += "&#160;\n&#160;";
17713         }
17714         ts.innerHTML = v;
17715         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17716         if(h != this.lastHeight){
17717             this.lastHeight = h;
17718             this.el.setHeight(h);
17719             this.fireEvent("autosize", this, h);
17720         }
17721     }
17722 });/*
17723  * Based on:
17724  * Ext JS Library 1.1.1
17725  * Copyright(c) 2006-2007, Ext JS, LLC.
17726  *
17727  * Originally Released Under LGPL - original licence link has changed is not relivant.
17728  *
17729  * Fork - LGPL
17730  * <script type="text/javascript">
17731  */
17732  
17733
17734 /**
17735  * @class Roo.form.NumberField
17736  * @extends Roo.form.TextField
17737  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17738  * @constructor
17739  * Creates a new NumberField
17740  * @param {Object} config Configuration options
17741  */
17742 Roo.form.NumberField = function(config){
17743     Roo.form.NumberField.superclass.constructor.call(this, config);
17744 };
17745
17746 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17747     /**
17748      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17749      */
17750     fieldClass: "x-form-field x-form-num-field",
17751     /**
17752      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17753      */
17754     allowDecimals : true,
17755     /**
17756      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17757      */
17758     decimalSeparator : ".",
17759     /**
17760      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17761      */
17762     decimalPrecision : 2,
17763     /**
17764      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17765      */
17766     allowNegative : true,
17767     /**
17768      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17769      */
17770     minValue : Number.NEGATIVE_INFINITY,
17771     /**
17772      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17773      */
17774     maxValue : Number.MAX_VALUE,
17775     /**
17776      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17777      */
17778     minText : "The minimum value for this field is {0}",
17779     /**
17780      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17781      */
17782     maxText : "The maximum value for this field is {0}",
17783     /**
17784      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17785      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17786      */
17787     nanText : "{0} is not a valid number",
17788
17789     // private
17790     initEvents : function(){
17791         Roo.form.NumberField.superclass.initEvents.call(this);
17792         var allowed = "0123456789";
17793         if(this.allowDecimals){
17794             allowed += this.decimalSeparator;
17795         }
17796         if(this.allowNegative){
17797             allowed += "-";
17798         }
17799         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17800         var keyPress = function(e){
17801             var k = e.getKey();
17802             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17803                 return;
17804             }
17805             var c = e.getCharCode();
17806             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17807                 e.stopEvent();
17808             }
17809         };
17810         this.el.on("keypress", keyPress, this);
17811     },
17812
17813     // private
17814     validateValue : function(value){
17815         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17816             return false;
17817         }
17818         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17819              return true;
17820         }
17821         var num = this.parseValue(value);
17822         if(isNaN(num)){
17823             this.markInvalid(String.format(this.nanText, value));
17824             return false;
17825         }
17826         if(num < this.minValue){
17827             this.markInvalid(String.format(this.minText, this.minValue));
17828             return false;
17829         }
17830         if(num > this.maxValue){
17831             this.markInvalid(String.format(this.maxText, this.maxValue));
17832             return false;
17833         }
17834         return true;
17835     },
17836
17837     getValue : function(){
17838         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17839     },
17840
17841     // private
17842     parseValue : function(value){
17843         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17844         return isNaN(value) ? '' : value;
17845     },
17846
17847     // private
17848     fixPrecision : function(value){
17849         var nan = isNaN(value);
17850         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17851             return nan ? '' : value;
17852         }
17853         return parseFloat(value).toFixed(this.decimalPrecision);
17854     },
17855
17856     setValue : function(v){
17857         v = this.fixPrecision(v);
17858         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17859     },
17860
17861     // private
17862     decimalPrecisionFcn : function(v){
17863         return Math.floor(v);
17864     },
17865
17866     beforeBlur : function(){
17867         var v = this.parseValue(this.getRawValue());
17868         if(v){
17869             this.setValue(v);
17870         }
17871     }
17872 });/*
17873  * Based on:
17874  * Ext JS Library 1.1.1
17875  * Copyright(c) 2006-2007, Ext JS, LLC.
17876  *
17877  * Originally Released Under LGPL - original licence link has changed is not relivant.
17878  *
17879  * Fork - LGPL
17880  * <script type="text/javascript">
17881  */
17882  
17883 /**
17884  * @class Roo.form.DateField
17885  * @extends Roo.form.TriggerField
17886  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17887 * @constructor
17888 * Create a new DateField
17889 * @param {Object} config
17890  */
17891 Roo.form.DateField = function(config)
17892 {
17893     Roo.form.DateField.superclass.constructor.call(this, config);
17894     
17895       this.addEvents({
17896          
17897         /**
17898          * @event select
17899          * Fires when a date is selected
17900              * @param {Roo.form.DateField} combo This combo box
17901              * @param {Date} date The date selected
17902              */
17903         'select' : true
17904          
17905     });
17906     
17907     
17908     if(typeof this.minValue == "string") {
17909         this.minValue = this.parseDate(this.minValue);
17910     }
17911     if(typeof this.maxValue == "string") {
17912         this.maxValue = this.parseDate(this.maxValue);
17913     }
17914     this.ddMatch = null;
17915     if(this.disabledDates){
17916         var dd = this.disabledDates;
17917         var re = "(?:";
17918         for(var i = 0; i < dd.length; i++){
17919             re += dd[i];
17920             if(i != dd.length-1) {
17921                 re += "|";
17922             }
17923         }
17924         this.ddMatch = new RegExp(re + ")");
17925     }
17926 };
17927
17928 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17929     /**
17930      * @cfg {String} format
17931      * The default date format string which can be overriden for localization support.  The format must be
17932      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17933      */
17934     format : "m/d/y",
17935     /**
17936      * @cfg {String} altFormats
17937      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17938      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17939      */
17940     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17941     /**
17942      * @cfg {Array} disabledDays
17943      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17944      */
17945     disabledDays : null,
17946     /**
17947      * @cfg {String} disabledDaysText
17948      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17949      */
17950     disabledDaysText : "Disabled",
17951     /**
17952      * @cfg {Array} disabledDates
17953      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17954      * expression so they are very powerful. Some examples:
17955      * <ul>
17956      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17957      * <li>["03/08", "09/16"] would disable those days for every year</li>
17958      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17959      * <li>["03/../2006"] would disable every day in March 2006</li>
17960      * <li>["^03"] would disable every day in every March</li>
17961      * </ul>
17962      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17963      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17964      */
17965     disabledDates : null,
17966     /**
17967      * @cfg {String} disabledDatesText
17968      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17969      */
17970     disabledDatesText : "Disabled",
17971     /**
17972      * @cfg {Date/String} minValue
17973      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17974      * valid format (defaults to null).
17975      */
17976     minValue : null,
17977     /**
17978      * @cfg {Date/String} maxValue
17979      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17980      * valid format (defaults to null).
17981      */
17982     maxValue : null,
17983     /**
17984      * @cfg {String} minText
17985      * The error text to display when the date in the cell is before minValue (defaults to
17986      * 'The date in this field must be after {minValue}').
17987      */
17988     minText : "The date in this field must be equal to or after {0}",
17989     /**
17990      * @cfg {String} maxText
17991      * The error text to display when the date in the cell is after maxValue (defaults to
17992      * 'The date in this field must be before {maxValue}').
17993      */
17994     maxText : "The date in this field must be equal to or before {0}",
17995     /**
17996      * @cfg {String} invalidText
17997      * The error text to display when the date in the field is invalid (defaults to
17998      * '{value} is not a valid date - it must be in the format {format}').
17999      */
18000     invalidText : "{0} is not a valid date - it must be in the format {1}",
18001     /**
18002      * @cfg {String} triggerClass
18003      * An additional CSS class used to style the trigger button.  The trigger will always get the
18004      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18005      * which displays a calendar icon).
18006      */
18007     triggerClass : 'x-form-date-trigger',
18008     
18009
18010     /**
18011      * @cfg {Boolean} useIso
18012      * if enabled, then the date field will use a hidden field to store the 
18013      * real value as iso formated date. default (false)
18014      */ 
18015     useIso : false,
18016     /**
18017      * @cfg {String/Object} autoCreate
18018      * A DomHelper element spec, or true for a default element spec (defaults to
18019      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18020      */ 
18021     // private
18022     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18023     
18024     // private
18025     hiddenField: false,
18026     
18027     onRender : function(ct, position)
18028     {
18029         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18030         if (this.useIso) {
18031             //this.el.dom.removeAttribute('name'); 
18032             Roo.log("Changing name?");
18033             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18034             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18035                     'before', true);
18036             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18037             // prevent input submission
18038             this.hiddenName = this.name;
18039         }
18040             
18041             
18042     },
18043     
18044     // private
18045     validateValue : function(value)
18046     {
18047         value = this.formatDate(value);
18048         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18049             Roo.log('super failed');
18050             return false;
18051         }
18052         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18053              return true;
18054         }
18055         var svalue = value;
18056         value = this.parseDate(value);
18057         if(!value){
18058             Roo.log('parse date failed' + svalue);
18059             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18060             return false;
18061         }
18062         var time = value.getTime();
18063         if(this.minValue && time < this.minValue.getTime()){
18064             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18065             return false;
18066         }
18067         if(this.maxValue && time > this.maxValue.getTime()){
18068             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18069             return false;
18070         }
18071         if(this.disabledDays){
18072             var day = value.getDay();
18073             for(var i = 0; i < this.disabledDays.length; i++) {
18074                 if(day === this.disabledDays[i]){
18075                     this.markInvalid(this.disabledDaysText);
18076                     return false;
18077                 }
18078             }
18079         }
18080         var fvalue = this.formatDate(value);
18081         if(this.ddMatch && this.ddMatch.test(fvalue)){
18082             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18083             return false;
18084         }
18085         return true;
18086     },
18087
18088     // private
18089     // Provides logic to override the default TriggerField.validateBlur which just returns true
18090     validateBlur : function(){
18091         return !this.menu || !this.menu.isVisible();
18092     },
18093     
18094     getName: function()
18095     {
18096         // returns hidden if it's set..
18097         if (!this.rendered) {return ''};
18098         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18099         
18100     },
18101
18102     /**
18103      * Returns the current date value of the date field.
18104      * @return {Date} The date value
18105      */
18106     getValue : function(){
18107         
18108         return  this.hiddenField ?
18109                 this.hiddenField.value :
18110                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18111     },
18112
18113     /**
18114      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18115      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18116      * (the default format used is "m/d/y").
18117      * <br />Usage:
18118      * <pre><code>
18119 //All of these calls set the same date value (May 4, 2006)
18120
18121 //Pass a date object:
18122 var dt = new Date('5/4/06');
18123 dateField.setValue(dt);
18124
18125 //Pass a date string (default format):
18126 dateField.setValue('5/4/06');
18127
18128 //Pass a date string (custom format):
18129 dateField.format = 'Y-m-d';
18130 dateField.setValue('2006-5-4');
18131 </code></pre>
18132      * @param {String/Date} date The date or valid date string
18133      */
18134     setValue : function(date){
18135         if (this.hiddenField) {
18136             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18137         }
18138         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18139         // make sure the value field is always stored as a date..
18140         this.value = this.parseDate(date);
18141         
18142         
18143     },
18144
18145     // private
18146     parseDate : function(value){
18147         if(!value || value instanceof Date){
18148             return value;
18149         }
18150         var v = Date.parseDate(value, this.format);
18151          if (!v && this.useIso) {
18152             v = Date.parseDate(value, 'Y-m-d');
18153         }
18154         if(!v && this.altFormats){
18155             if(!this.altFormatsArray){
18156                 this.altFormatsArray = this.altFormats.split("|");
18157             }
18158             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18159                 v = Date.parseDate(value, this.altFormatsArray[i]);
18160             }
18161         }
18162         return v;
18163     },
18164
18165     // private
18166     formatDate : function(date, fmt){
18167         return (!date || !(date instanceof Date)) ?
18168                date : date.dateFormat(fmt || this.format);
18169     },
18170
18171     // private
18172     menuListeners : {
18173         select: function(m, d){
18174             
18175             this.setValue(d);
18176             this.fireEvent('select', this, d);
18177         },
18178         show : function(){ // retain focus styling
18179             this.onFocus();
18180         },
18181         hide : function(){
18182             this.focus.defer(10, this);
18183             var ml = this.menuListeners;
18184             this.menu.un("select", ml.select,  this);
18185             this.menu.un("show", ml.show,  this);
18186             this.menu.un("hide", ml.hide,  this);
18187         }
18188     },
18189
18190     // private
18191     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18192     onTriggerClick : function(){
18193         if(this.disabled){
18194             return;
18195         }
18196         if(this.menu == null){
18197             this.menu = new Roo.menu.DateMenu();
18198         }
18199         Roo.apply(this.menu.picker,  {
18200             showClear: this.allowBlank,
18201             minDate : this.minValue,
18202             maxDate : this.maxValue,
18203             disabledDatesRE : this.ddMatch,
18204             disabledDatesText : this.disabledDatesText,
18205             disabledDays : this.disabledDays,
18206             disabledDaysText : this.disabledDaysText,
18207             format : this.useIso ? 'Y-m-d' : this.format,
18208             minText : String.format(this.minText, this.formatDate(this.minValue)),
18209             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18210         });
18211         this.menu.on(Roo.apply({}, this.menuListeners, {
18212             scope:this
18213         }));
18214         this.menu.picker.setValue(this.getValue() || new Date());
18215         this.menu.show(this.el, "tl-bl?");
18216     },
18217
18218     beforeBlur : function(){
18219         var v = this.parseDate(this.getRawValue());
18220         if(v){
18221             this.setValue(v);
18222         }
18223     },
18224
18225     /*@
18226      * overide
18227      * 
18228      */
18229     isDirty : function() {
18230         if(this.disabled) {
18231             return false;
18232         }
18233         
18234         if(typeof(this.startValue) === 'undefined'){
18235             return false;
18236         }
18237         
18238         return String(this.getValue()) !== String(this.startValue);
18239         
18240     },
18241     // @overide
18242     cleanLeadingSpace : function(e)
18243     {
18244        return;
18245     }
18246     
18247 });/*
18248  * Based on:
18249  * Ext JS Library 1.1.1
18250  * Copyright(c) 2006-2007, Ext JS, LLC.
18251  *
18252  * Originally Released Under LGPL - original licence link has changed is not relivant.
18253  *
18254  * Fork - LGPL
18255  * <script type="text/javascript">
18256  */
18257  
18258 /**
18259  * @class Roo.form.MonthField
18260  * @extends Roo.form.TriggerField
18261  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18262 * @constructor
18263 * Create a new MonthField
18264 * @param {Object} config
18265  */
18266 Roo.form.MonthField = function(config){
18267     
18268     Roo.form.MonthField.superclass.constructor.call(this, config);
18269     
18270       this.addEvents({
18271          
18272         /**
18273          * @event select
18274          * Fires when a date is selected
18275              * @param {Roo.form.MonthFieeld} combo This combo box
18276              * @param {Date} date The date selected
18277              */
18278         'select' : true
18279          
18280     });
18281     
18282     
18283     if(typeof this.minValue == "string") {
18284         this.minValue = this.parseDate(this.minValue);
18285     }
18286     if(typeof this.maxValue == "string") {
18287         this.maxValue = this.parseDate(this.maxValue);
18288     }
18289     this.ddMatch = null;
18290     if(this.disabledDates){
18291         var dd = this.disabledDates;
18292         var re = "(?:";
18293         for(var i = 0; i < dd.length; i++){
18294             re += dd[i];
18295             if(i != dd.length-1) {
18296                 re += "|";
18297             }
18298         }
18299         this.ddMatch = new RegExp(re + ")");
18300     }
18301 };
18302
18303 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18304     /**
18305      * @cfg {String} format
18306      * The default date format string which can be overriden for localization support.  The format must be
18307      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18308      */
18309     format : "M Y",
18310     /**
18311      * @cfg {String} altFormats
18312      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18313      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18314      */
18315     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18316     /**
18317      * @cfg {Array} disabledDays
18318      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18319      */
18320     disabledDays : [0,1,2,3,4,5,6],
18321     /**
18322      * @cfg {String} disabledDaysText
18323      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18324      */
18325     disabledDaysText : "Disabled",
18326     /**
18327      * @cfg {Array} disabledDates
18328      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18329      * expression so they are very powerful. Some examples:
18330      * <ul>
18331      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18332      * <li>["03/08", "09/16"] would disable those days for every year</li>
18333      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18334      * <li>["03/../2006"] would disable every day in March 2006</li>
18335      * <li>["^03"] would disable every day in every March</li>
18336      * </ul>
18337      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18338      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18339      */
18340     disabledDates : null,
18341     /**
18342      * @cfg {String} disabledDatesText
18343      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18344      */
18345     disabledDatesText : "Disabled",
18346     /**
18347      * @cfg {Date/String} minValue
18348      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18349      * valid format (defaults to null).
18350      */
18351     minValue : null,
18352     /**
18353      * @cfg {Date/String} maxValue
18354      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18355      * valid format (defaults to null).
18356      */
18357     maxValue : null,
18358     /**
18359      * @cfg {String} minText
18360      * The error text to display when the date in the cell is before minValue (defaults to
18361      * 'The date in this field must be after {minValue}').
18362      */
18363     minText : "The date in this field must be equal to or after {0}",
18364     /**
18365      * @cfg {String} maxTextf
18366      * The error text to display when the date in the cell is after maxValue (defaults to
18367      * 'The date in this field must be before {maxValue}').
18368      */
18369     maxText : "The date in this field must be equal to or before {0}",
18370     /**
18371      * @cfg {String} invalidText
18372      * The error text to display when the date in the field is invalid (defaults to
18373      * '{value} is not a valid date - it must be in the format {format}').
18374      */
18375     invalidText : "{0} is not a valid date - it must be in the format {1}",
18376     /**
18377      * @cfg {String} triggerClass
18378      * An additional CSS class used to style the trigger button.  The trigger will always get the
18379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18380      * which displays a calendar icon).
18381      */
18382     triggerClass : 'x-form-date-trigger',
18383     
18384
18385     /**
18386      * @cfg {Boolean} useIso
18387      * if enabled, then the date field will use a hidden field to store the 
18388      * real value as iso formated date. default (true)
18389      */ 
18390     useIso : true,
18391     /**
18392      * @cfg {String/Object} autoCreate
18393      * A DomHelper element spec, or true for a default element spec (defaults to
18394      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18395      */ 
18396     // private
18397     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18398     
18399     // private
18400     hiddenField: false,
18401     
18402     hideMonthPicker : false,
18403     
18404     onRender : function(ct, position)
18405     {
18406         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18407         if (this.useIso) {
18408             this.el.dom.removeAttribute('name'); 
18409             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18410                     'before', true);
18411             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18412             // prevent input submission
18413             this.hiddenName = this.name;
18414         }
18415             
18416             
18417     },
18418     
18419     // private
18420     validateValue : function(value)
18421     {
18422         value = this.formatDate(value);
18423         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18424             return false;
18425         }
18426         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18427              return true;
18428         }
18429         var svalue = value;
18430         value = this.parseDate(value);
18431         if(!value){
18432             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18433             return false;
18434         }
18435         var time = value.getTime();
18436         if(this.minValue && time < this.minValue.getTime()){
18437             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18438             return false;
18439         }
18440         if(this.maxValue && time > this.maxValue.getTime()){
18441             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18442             return false;
18443         }
18444         /*if(this.disabledDays){
18445             var day = value.getDay();
18446             for(var i = 0; i < this.disabledDays.length; i++) {
18447                 if(day === this.disabledDays[i]){
18448                     this.markInvalid(this.disabledDaysText);
18449                     return false;
18450                 }
18451             }
18452         }
18453         */
18454         var fvalue = this.formatDate(value);
18455         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18456             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18457             return false;
18458         }
18459         */
18460         return true;
18461     },
18462
18463     // private
18464     // Provides logic to override the default TriggerField.validateBlur which just returns true
18465     validateBlur : function(){
18466         return !this.menu || !this.menu.isVisible();
18467     },
18468
18469     /**
18470      * Returns the current date value of the date field.
18471      * @return {Date} The date value
18472      */
18473     getValue : function(){
18474         
18475         
18476         
18477         return  this.hiddenField ?
18478                 this.hiddenField.value :
18479                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18480     },
18481
18482     /**
18483      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18484      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18485      * (the default format used is "m/d/y").
18486      * <br />Usage:
18487      * <pre><code>
18488 //All of these calls set the same date value (May 4, 2006)
18489
18490 //Pass a date object:
18491 var dt = new Date('5/4/06');
18492 monthField.setValue(dt);
18493
18494 //Pass a date string (default format):
18495 monthField.setValue('5/4/06');
18496
18497 //Pass a date string (custom format):
18498 monthField.format = 'Y-m-d';
18499 monthField.setValue('2006-5-4');
18500 </code></pre>
18501      * @param {String/Date} date The date or valid date string
18502      */
18503     setValue : function(date){
18504         Roo.log('month setValue' + date);
18505         // can only be first of month..
18506         
18507         var val = this.parseDate(date);
18508         
18509         if (this.hiddenField) {
18510             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18511         }
18512         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18513         this.value = this.parseDate(date);
18514     },
18515
18516     // private
18517     parseDate : function(value){
18518         if(!value || value instanceof Date){
18519             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18520             return value;
18521         }
18522         var v = Date.parseDate(value, this.format);
18523         if (!v && this.useIso) {
18524             v = Date.parseDate(value, 'Y-m-d');
18525         }
18526         if (v) {
18527             // 
18528             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18529         }
18530         
18531         
18532         if(!v && this.altFormats){
18533             if(!this.altFormatsArray){
18534                 this.altFormatsArray = this.altFormats.split("|");
18535             }
18536             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18537                 v = Date.parseDate(value, this.altFormatsArray[i]);
18538             }
18539         }
18540         return v;
18541     },
18542
18543     // private
18544     formatDate : function(date, fmt){
18545         return (!date || !(date instanceof Date)) ?
18546                date : date.dateFormat(fmt || this.format);
18547     },
18548
18549     // private
18550     menuListeners : {
18551         select: function(m, d){
18552             this.setValue(d);
18553             this.fireEvent('select', this, d);
18554         },
18555         show : function(){ // retain focus styling
18556             this.onFocus();
18557         },
18558         hide : function(){
18559             this.focus.defer(10, this);
18560             var ml = this.menuListeners;
18561             this.menu.un("select", ml.select,  this);
18562             this.menu.un("show", ml.show,  this);
18563             this.menu.un("hide", ml.hide,  this);
18564         }
18565     },
18566     // private
18567     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18568     onTriggerClick : function(){
18569         if(this.disabled){
18570             return;
18571         }
18572         if(this.menu == null){
18573             this.menu = new Roo.menu.DateMenu();
18574            
18575         }
18576         
18577         Roo.apply(this.menu.picker,  {
18578             
18579             showClear: this.allowBlank,
18580             minDate : this.minValue,
18581             maxDate : this.maxValue,
18582             disabledDatesRE : this.ddMatch,
18583             disabledDatesText : this.disabledDatesText,
18584             
18585             format : this.useIso ? 'Y-m-d' : this.format,
18586             minText : String.format(this.minText, this.formatDate(this.minValue)),
18587             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18588             
18589         });
18590          this.menu.on(Roo.apply({}, this.menuListeners, {
18591             scope:this
18592         }));
18593        
18594         
18595         var m = this.menu;
18596         var p = m.picker;
18597         
18598         // hide month picker get's called when we called by 'before hide';
18599         
18600         var ignorehide = true;
18601         p.hideMonthPicker  = function(disableAnim){
18602             if (ignorehide) {
18603                 return;
18604             }
18605              if(this.monthPicker){
18606                 Roo.log("hideMonthPicker called");
18607                 if(disableAnim === true){
18608                     this.monthPicker.hide();
18609                 }else{
18610                     this.monthPicker.slideOut('t', {duration:.2});
18611                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18612                     p.fireEvent("select", this, this.value);
18613                     m.hide();
18614                 }
18615             }
18616         }
18617         
18618         Roo.log('picker set value');
18619         Roo.log(this.getValue());
18620         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18621         m.show(this.el, 'tl-bl?');
18622         ignorehide  = false;
18623         // this will trigger hideMonthPicker..
18624         
18625         
18626         // hidden the day picker
18627         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18628         
18629         
18630         
18631       
18632         
18633         p.showMonthPicker.defer(100, p);
18634     
18635         
18636        
18637     },
18638
18639     beforeBlur : function(){
18640         var v = this.parseDate(this.getRawValue());
18641         if(v){
18642             this.setValue(v);
18643         }
18644     }
18645
18646     /** @cfg {Boolean} grow @hide */
18647     /** @cfg {Number} growMin @hide */
18648     /** @cfg {Number} growMax @hide */
18649     /**
18650      * @hide
18651      * @method autoSize
18652      */
18653 });/*
18654  * Based on:
18655  * Ext JS Library 1.1.1
18656  * Copyright(c) 2006-2007, Ext JS, LLC.
18657  *
18658  * Originally Released Under LGPL - original licence link has changed is not relivant.
18659  *
18660  * Fork - LGPL
18661  * <script type="text/javascript">
18662  */
18663  
18664
18665 /**
18666  * @class Roo.form.ComboBox
18667  * @extends Roo.form.TriggerField
18668  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18669  * @constructor
18670  * Create a new ComboBox.
18671  * @param {Object} config Configuration options
18672  */
18673 Roo.form.ComboBox = function(config){
18674     Roo.form.ComboBox.superclass.constructor.call(this, config);
18675     this.addEvents({
18676         /**
18677          * @event expand
18678          * Fires when the dropdown list is expanded
18679              * @param {Roo.form.ComboBox} combo This combo box
18680              */
18681         'expand' : true,
18682         /**
18683          * @event collapse
18684          * Fires when the dropdown list is collapsed
18685              * @param {Roo.form.ComboBox} combo This combo box
18686              */
18687         'collapse' : true,
18688         /**
18689          * @event beforeselect
18690          * Fires before a list item is selected. Return false to cancel the selection.
18691              * @param {Roo.form.ComboBox} combo This combo box
18692              * @param {Roo.data.Record} record The data record returned from the underlying store
18693              * @param {Number} index The index of the selected item in the dropdown list
18694              */
18695         'beforeselect' : true,
18696         /**
18697          * @event select
18698          * Fires when a list item is selected
18699              * @param {Roo.form.ComboBox} combo This combo box
18700              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18701              * @param {Number} index The index of the selected item in the dropdown list
18702              */
18703         'select' : true,
18704         /**
18705          * @event beforequery
18706          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18707          * The event object passed has these properties:
18708              * @param {Roo.form.ComboBox} combo This combo box
18709              * @param {String} query The query
18710              * @param {Boolean} forceAll true to force "all" query
18711              * @param {Boolean} cancel true to cancel the query
18712              * @param {Object} e The query event object
18713              */
18714         'beforequery': true,
18715          /**
18716          * @event add
18717          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18718              * @param {Roo.form.ComboBox} combo This combo box
18719              */
18720         'add' : true,
18721         /**
18722          * @event edit
18723          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18724              * @param {Roo.form.ComboBox} combo This combo box
18725              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18726              */
18727         'edit' : true
18728         
18729         
18730     });
18731     if(this.transform){
18732         this.allowDomMove = false;
18733         var s = Roo.getDom(this.transform);
18734         if(!this.hiddenName){
18735             this.hiddenName = s.name;
18736         }
18737         if(!this.store){
18738             this.mode = 'local';
18739             var d = [], opts = s.options;
18740             for(var i = 0, len = opts.length;i < len; i++){
18741                 var o = opts[i];
18742                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18743                 if(o.selected) {
18744                     this.value = value;
18745                 }
18746                 d.push([value, o.text]);
18747             }
18748             this.store = new Roo.data.SimpleStore({
18749                 'id': 0,
18750                 fields: ['value', 'text'],
18751                 data : d
18752             });
18753             this.valueField = 'value';
18754             this.displayField = 'text';
18755         }
18756         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18757         if(!this.lazyRender){
18758             this.target = true;
18759             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18760             s.parentNode.removeChild(s); // remove it
18761             this.render(this.el.parentNode);
18762         }else{
18763             s.parentNode.removeChild(s); // remove it
18764         }
18765
18766     }
18767     if (this.store) {
18768         this.store = Roo.factory(this.store, Roo.data);
18769     }
18770     
18771     this.selectedIndex = -1;
18772     if(this.mode == 'local'){
18773         if(config.queryDelay === undefined){
18774             this.queryDelay = 10;
18775         }
18776         if(config.minChars === undefined){
18777             this.minChars = 0;
18778         }
18779     }
18780 };
18781
18782 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18783     /**
18784      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18785      */
18786     /**
18787      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18788      * rendering into an Roo.Editor, defaults to false)
18789      */
18790     /**
18791      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18792      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18793      */
18794     /**
18795      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18796      */
18797     /**
18798      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18799      * the dropdown list (defaults to undefined, with no header element)
18800      */
18801
18802      /**
18803      * @cfg {String/Roo.Template} tpl The template to use to render the output
18804      */
18805      
18806     // private
18807     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18808     /**
18809      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18810      */
18811     listWidth: undefined,
18812     /**
18813      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18814      * mode = 'remote' or 'text' if mode = 'local')
18815      */
18816     displayField: undefined,
18817     /**
18818      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18819      * mode = 'remote' or 'value' if mode = 'local'). 
18820      * Note: use of a valueField requires the user make a selection
18821      * in order for a value to be mapped.
18822      */
18823     valueField: undefined,
18824     
18825     
18826     /**
18827      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18828      * field's data value (defaults to the underlying DOM element's name)
18829      */
18830     hiddenName: undefined,
18831     /**
18832      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18833      */
18834     listClass: '',
18835     /**
18836      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18837      */
18838     selectedClass: 'x-combo-selected',
18839     /**
18840      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18841      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18842      * which displays a downward arrow icon).
18843      */
18844     triggerClass : 'x-form-arrow-trigger',
18845     /**
18846      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18847      */
18848     shadow:'sides',
18849     /**
18850      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18851      * anchor positions (defaults to 'tl-bl')
18852      */
18853     listAlign: 'tl-bl?',
18854     /**
18855      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18856      */
18857     maxHeight: 300,
18858     /**
18859      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18860      * query specified by the allQuery config option (defaults to 'query')
18861      */
18862     triggerAction: 'query',
18863     /**
18864      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18865      * (defaults to 4, does not apply if editable = false)
18866      */
18867     minChars : 4,
18868     /**
18869      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18870      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18871      */
18872     typeAhead: false,
18873     /**
18874      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18875      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18876      */
18877     queryDelay: 500,
18878     /**
18879      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18880      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18881      */
18882     pageSize: 0,
18883     /**
18884      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18885      * when editable = true (defaults to false)
18886      */
18887     selectOnFocus:false,
18888     /**
18889      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18890      */
18891     queryParam: 'query',
18892     /**
18893      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18894      * when mode = 'remote' (defaults to 'Loading...')
18895      */
18896     loadingText: 'Loading...',
18897     /**
18898      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18899      */
18900     resizable: false,
18901     /**
18902      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18903      */
18904     handleHeight : 8,
18905     /**
18906      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18907      * traditional select (defaults to true)
18908      */
18909     editable: true,
18910     /**
18911      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18912      */
18913     allQuery: '',
18914     /**
18915      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18916      */
18917     mode: 'remote',
18918     /**
18919      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18920      * listWidth has a higher value)
18921      */
18922     minListWidth : 70,
18923     /**
18924      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18925      * allow the user to set arbitrary text into the field (defaults to false)
18926      */
18927     forceSelection:false,
18928     /**
18929      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18930      * if typeAhead = true (defaults to 250)
18931      */
18932     typeAheadDelay : 250,
18933     /**
18934      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18935      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18936      */
18937     valueNotFoundText : undefined,
18938     /**
18939      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18940      */
18941     blockFocus : false,
18942     
18943     /**
18944      * @cfg {Boolean} disableClear Disable showing of clear button.
18945      */
18946     disableClear : false,
18947     /**
18948      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18949      */
18950     alwaysQuery : false,
18951     
18952     //private
18953     addicon : false,
18954     editicon: false,
18955     
18956     // element that contains real text value.. (when hidden is used..)
18957      
18958     // private
18959     onRender : function(ct, position)
18960     {
18961         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18962         
18963         if(this.hiddenName){
18964             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18965                     'before', true);
18966             this.hiddenField.value =
18967                 this.hiddenValue !== undefined ? this.hiddenValue :
18968                 this.value !== undefined ? this.value : '';
18969
18970             // prevent input submission
18971             this.el.dom.removeAttribute('name');
18972              
18973              
18974         }
18975         
18976         if(Roo.isGecko){
18977             this.el.dom.setAttribute('autocomplete', 'off');
18978         }
18979
18980         var cls = 'x-combo-list';
18981
18982         this.list = new Roo.Layer({
18983             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18984         });
18985
18986         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18987         this.list.setWidth(lw);
18988         this.list.swallowEvent('mousewheel');
18989         this.assetHeight = 0;
18990
18991         if(this.title){
18992             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18993             this.assetHeight += this.header.getHeight();
18994         }
18995
18996         this.innerList = this.list.createChild({cls:cls+'-inner'});
18997         this.innerList.on('mouseover', this.onViewOver, this);
18998         this.innerList.on('mousemove', this.onViewMove, this);
18999         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19000         
19001         if(this.allowBlank && !this.pageSize && !this.disableClear){
19002             this.footer = this.list.createChild({cls:cls+'-ft'});
19003             this.pageTb = new Roo.Toolbar(this.footer);
19004            
19005         }
19006         if(this.pageSize){
19007             this.footer = this.list.createChild({cls:cls+'-ft'});
19008             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
19009                     {pageSize: this.pageSize});
19010             
19011         }
19012         
19013         if (this.pageTb && this.allowBlank && !this.disableClear) {
19014             var _this = this;
19015             this.pageTb.add(new Roo.Toolbar.Fill(), {
19016                 cls: 'x-btn-icon x-btn-clear',
19017                 text: '&#160;',
19018                 handler: function()
19019                 {
19020                     _this.collapse();
19021                     _this.clearValue();
19022                     _this.onSelect(false, -1);
19023                 }
19024             });
19025         }
19026         if (this.footer) {
19027             this.assetHeight += this.footer.getHeight();
19028         }
19029         
19030
19031         if(!this.tpl){
19032             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19033         }
19034
19035         this.view = new Roo.View(this.innerList, this.tpl, {
19036             singleSelect:true,
19037             store: this.store,
19038             selectedClass: this.selectedClass
19039         });
19040
19041         this.view.on('click', this.onViewClick, this);
19042
19043         this.store.on('beforeload', this.onBeforeLoad, this);
19044         this.store.on('load', this.onLoad, this);
19045         this.store.on('loadexception', this.onLoadException, this);
19046
19047         if(this.resizable){
19048             this.resizer = new Roo.Resizable(this.list,  {
19049                pinned:true, handles:'se'
19050             });
19051             this.resizer.on('resize', function(r, w, h){
19052                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19053                 this.listWidth = w;
19054                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19055                 this.restrictHeight();
19056             }, this);
19057             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19058         }
19059         if(!this.editable){
19060             this.editable = true;
19061             this.setEditable(false);
19062         }  
19063         
19064         
19065         if (typeof(this.events.add.listeners) != 'undefined') {
19066             
19067             this.addicon = this.wrap.createChild(
19068                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19069        
19070             this.addicon.on('click', function(e) {
19071                 this.fireEvent('add', this);
19072             }, this);
19073         }
19074         if (typeof(this.events.edit.listeners) != 'undefined') {
19075             
19076             this.editicon = this.wrap.createChild(
19077                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19078             if (this.addicon) {
19079                 this.editicon.setStyle('margin-left', '40px');
19080             }
19081             this.editicon.on('click', function(e) {
19082                 
19083                 // we fire even  if inothing is selected..
19084                 this.fireEvent('edit', this, this.lastData );
19085                 
19086             }, this);
19087         }
19088         
19089         
19090         
19091     },
19092
19093     // private
19094     initEvents : function(){
19095         Roo.form.ComboBox.superclass.initEvents.call(this);
19096
19097         this.keyNav = new Roo.KeyNav(this.el, {
19098             "up" : function(e){
19099                 this.inKeyMode = true;
19100                 this.selectPrev();
19101             },
19102
19103             "down" : function(e){
19104                 if(!this.isExpanded()){
19105                     this.onTriggerClick();
19106                 }else{
19107                     this.inKeyMode = true;
19108                     this.selectNext();
19109                 }
19110             },
19111
19112             "enter" : function(e){
19113                 this.onViewClick();
19114                 //return true;
19115             },
19116
19117             "esc" : function(e){
19118                 this.collapse();
19119             },
19120
19121             "tab" : function(e){
19122                 this.onViewClick(false);
19123                 this.fireEvent("specialkey", this, e);
19124                 return true;
19125             },
19126
19127             scope : this,
19128
19129             doRelay : function(foo, bar, hname){
19130                 if(hname == 'down' || this.scope.isExpanded()){
19131                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19132                 }
19133                 return true;
19134             },
19135
19136             forceKeyDown: true
19137         });
19138         this.queryDelay = Math.max(this.queryDelay || 10,
19139                 this.mode == 'local' ? 10 : 250);
19140         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19141         if(this.typeAhead){
19142             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19143         }
19144         if(this.editable !== false){
19145             this.el.on("keyup", this.onKeyUp, this);
19146         }
19147         if(this.forceSelection){
19148             this.on('blur', this.doForce, this);
19149         }
19150     },
19151
19152     onDestroy : function(){
19153         if(this.view){
19154             this.view.setStore(null);
19155             this.view.el.removeAllListeners();
19156             this.view.el.remove();
19157             this.view.purgeListeners();
19158         }
19159         if(this.list){
19160             this.list.destroy();
19161         }
19162         if(this.store){
19163             this.store.un('beforeload', this.onBeforeLoad, this);
19164             this.store.un('load', this.onLoad, this);
19165             this.store.un('loadexception', this.onLoadException, this);
19166         }
19167         Roo.form.ComboBox.superclass.onDestroy.call(this);
19168     },
19169
19170     // private
19171     fireKey : function(e){
19172         if(e.isNavKeyPress() && !this.list.isVisible()){
19173             this.fireEvent("specialkey", this, e);
19174         }
19175     },
19176
19177     // private
19178     onResize: function(w, h){
19179         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19180         
19181         if(typeof w != 'number'){
19182             // we do not handle it!?!?
19183             return;
19184         }
19185         var tw = this.trigger.getWidth();
19186         tw += this.addicon ? this.addicon.getWidth() : 0;
19187         tw += this.editicon ? this.editicon.getWidth() : 0;
19188         var x = w - tw;
19189         this.el.setWidth( this.adjustWidth('input', x));
19190             
19191         this.trigger.setStyle('left', x+'px');
19192         
19193         if(this.list && this.listWidth === undefined){
19194             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19195             this.list.setWidth(lw);
19196             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19197         }
19198         
19199     
19200         
19201     },
19202
19203     /**
19204      * Allow or prevent the user from directly editing the field text.  If false is passed,
19205      * the user will only be able to select from the items defined in the dropdown list.  This method
19206      * is the runtime equivalent of setting the 'editable' config option at config time.
19207      * @param {Boolean} value True to allow the user to directly edit the field text
19208      */
19209     setEditable : function(value){
19210         if(value == this.editable){
19211             return;
19212         }
19213         this.editable = value;
19214         if(!value){
19215             this.el.dom.setAttribute('readOnly', true);
19216             this.el.on('mousedown', this.onTriggerClick,  this);
19217             this.el.addClass('x-combo-noedit');
19218         }else{
19219             this.el.dom.setAttribute('readOnly', false);
19220             this.el.un('mousedown', this.onTriggerClick,  this);
19221             this.el.removeClass('x-combo-noedit');
19222         }
19223     },
19224
19225     // private
19226     onBeforeLoad : function(){
19227         if(!this.hasFocus){
19228             return;
19229         }
19230         this.innerList.update(this.loadingText ?
19231                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19232         this.restrictHeight();
19233         this.selectedIndex = -1;
19234     },
19235
19236     // private
19237     onLoad : function(){
19238         if(!this.hasFocus){
19239             return;
19240         }
19241         if(this.store.getCount() > 0){
19242             this.expand();
19243             this.restrictHeight();
19244             if(this.lastQuery == this.allQuery){
19245                 if(this.editable){
19246                     this.el.dom.select();
19247                 }
19248                 if(!this.selectByValue(this.value, true)){
19249                     this.select(0, true);
19250                 }
19251             }else{
19252                 this.selectNext();
19253                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19254                     this.taTask.delay(this.typeAheadDelay);
19255                 }
19256             }
19257         }else{
19258             this.onEmptyResults();
19259         }
19260         //this.el.focus();
19261     },
19262     // private
19263     onLoadException : function()
19264     {
19265         this.collapse();
19266         Roo.log(this.store.reader.jsonData);
19267         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19268             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19269         }
19270         
19271         
19272     },
19273     // private
19274     onTypeAhead : function(){
19275         if(this.store.getCount() > 0){
19276             var r = this.store.getAt(0);
19277             var newValue = r.data[this.displayField];
19278             var len = newValue.length;
19279             var selStart = this.getRawValue().length;
19280             if(selStart != len){
19281                 this.setRawValue(newValue);
19282                 this.selectText(selStart, newValue.length);
19283             }
19284         }
19285     },
19286
19287     // private
19288     onSelect : function(record, index){
19289         if(this.fireEvent('beforeselect', this, record, index) !== false){
19290             this.setFromData(index > -1 ? record.data : false);
19291             this.collapse();
19292             this.fireEvent('select', this, record, index);
19293         }
19294     },
19295
19296     /**
19297      * Returns the currently selected field value or empty string if no value is set.
19298      * @return {String} value The selected value
19299      */
19300     getValue : function(){
19301         if(this.valueField){
19302             return typeof this.value != 'undefined' ? this.value : '';
19303         }
19304         return Roo.form.ComboBox.superclass.getValue.call(this);
19305     },
19306
19307     /**
19308      * Clears any text/value currently set in the field
19309      */
19310     clearValue : function(){
19311         if(this.hiddenField){
19312             this.hiddenField.value = '';
19313         }
19314         this.value = '';
19315         this.setRawValue('');
19316         this.lastSelectionText = '';
19317         
19318     },
19319
19320     /**
19321      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19322      * will be displayed in the field.  If the value does not match the data value of an existing item,
19323      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19324      * Otherwise the field will be blank (although the value will still be set).
19325      * @param {String} value The value to match
19326      */
19327     setValue : function(v){
19328         var text = v;
19329         if(this.valueField){
19330             var r = this.findRecord(this.valueField, v);
19331             if(r){
19332                 text = r.data[this.displayField];
19333             }else if(this.valueNotFoundText !== undefined){
19334                 text = this.valueNotFoundText;
19335             }
19336         }
19337         this.lastSelectionText = text;
19338         if(this.hiddenField){
19339             this.hiddenField.value = v;
19340         }
19341         Roo.form.ComboBox.superclass.setValue.call(this, text);
19342         this.value = v;
19343     },
19344     /**
19345      * @property {Object} the last set data for the element
19346      */
19347     
19348     lastData : false,
19349     /**
19350      * Sets the value of the field based on a object which is related to the record format for the store.
19351      * @param {Object} value the value to set as. or false on reset?
19352      */
19353     setFromData : function(o){
19354         var dv = ''; // display value
19355         var vv = ''; // value value..
19356         this.lastData = o;
19357         if (this.displayField) {
19358             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19359         } else {
19360             // this is an error condition!!!
19361             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19362         }
19363         
19364         if(this.valueField){
19365             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19366         }
19367         if(this.hiddenField){
19368             this.hiddenField.value = vv;
19369             
19370             this.lastSelectionText = dv;
19371             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19372             this.value = vv;
19373             return;
19374         }
19375         // no hidden field.. - we store the value in 'value', but still display
19376         // display field!!!!
19377         this.lastSelectionText = dv;
19378         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19379         this.value = vv;
19380         
19381         
19382     },
19383     // private
19384     reset : function(){
19385         // overridden so that last data is reset..
19386         this.setValue(this.resetValue);
19387         this.originalValue = this.getValue();
19388         this.clearInvalid();
19389         this.lastData = false;
19390         if (this.view) {
19391             this.view.clearSelections();
19392         }
19393     },
19394     // private
19395     findRecord : function(prop, value){
19396         var record;
19397         if(this.store.getCount() > 0){
19398             this.store.each(function(r){
19399                 if(r.data[prop] == value){
19400                     record = r;
19401                     return false;
19402                 }
19403                 return true;
19404             });
19405         }
19406         return record;
19407     },
19408     
19409     getName: function()
19410     {
19411         // returns hidden if it's set..
19412         if (!this.rendered) {return ''};
19413         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19414         
19415     },
19416     // private
19417     onViewMove : function(e, t){
19418         this.inKeyMode = false;
19419     },
19420
19421     // private
19422     onViewOver : function(e, t){
19423         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19424             return;
19425         }
19426         var item = this.view.findItemFromChild(t);
19427         if(item){
19428             var index = this.view.indexOf(item);
19429             this.select(index, false);
19430         }
19431     },
19432
19433     // private
19434     onViewClick : function(doFocus)
19435     {
19436         var index = this.view.getSelectedIndexes()[0];
19437         var r = this.store.getAt(index);
19438         if(r){
19439             this.onSelect(r, index);
19440         }
19441         if(doFocus !== false && !this.blockFocus){
19442             this.el.focus();
19443         }
19444     },
19445
19446     // private
19447     restrictHeight : function(){
19448         this.innerList.dom.style.height = '';
19449         var inner = this.innerList.dom;
19450         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19451         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19452         this.list.beginUpdate();
19453         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19454         this.list.alignTo(this.el, this.listAlign);
19455         this.list.endUpdate();
19456     },
19457
19458     // private
19459     onEmptyResults : function(){
19460         this.collapse();
19461     },
19462
19463     /**
19464      * Returns true if the dropdown list is expanded, else false.
19465      */
19466     isExpanded : function(){
19467         return this.list.isVisible();
19468     },
19469
19470     /**
19471      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19472      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19473      * @param {String} value The data value of the item to select
19474      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19475      * selected item if it is not currently in view (defaults to true)
19476      * @return {Boolean} True if the value matched an item in the list, else false
19477      */
19478     selectByValue : function(v, scrollIntoView){
19479         if(v !== undefined && v !== null){
19480             var r = this.findRecord(this.valueField || this.displayField, v);
19481             if(r){
19482                 this.select(this.store.indexOf(r), scrollIntoView);
19483                 return true;
19484             }
19485         }
19486         return false;
19487     },
19488
19489     /**
19490      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19491      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19492      * @param {Number} index The zero-based index of the list item to select
19493      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19494      * selected item if it is not currently in view (defaults to true)
19495      */
19496     select : function(index, scrollIntoView){
19497         this.selectedIndex = index;
19498         this.view.select(index);
19499         if(scrollIntoView !== false){
19500             var el = this.view.getNode(index);
19501             if(el){
19502                 this.innerList.scrollChildIntoView(el, false);
19503             }
19504         }
19505     },
19506
19507     // private
19508     selectNext : function(){
19509         var ct = this.store.getCount();
19510         if(ct > 0){
19511             if(this.selectedIndex == -1){
19512                 this.select(0);
19513             }else if(this.selectedIndex < ct-1){
19514                 this.select(this.selectedIndex+1);
19515             }
19516         }
19517     },
19518
19519     // private
19520     selectPrev : function(){
19521         var ct = this.store.getCount();
19522         if(ct > 0){
19523             if(this.selectedIndex == -1){
19524                 this.select(0);
19525             }else if(this.selectedIndex != 0){
19526                 this.select(this.selectedIndex-1);
19527             }
19528         }
19529     },
19530
19531     // private
19532     onKeyUp : function(e){
19533         if(this.editable !== false && !e.isSpecialKey()){
19534             this.lastKey = e.getKey();
19535             this.dqTask.delay(this.queryDelay);
19536         }
19537     },
19538
19539     // private
19540     validateBlur : function(){
19541         return !this.list || !this.list.isVisible();   
19542     },
19543
19544     // private
19545     initQuery : function(){
19546         this.doQuery(this.getRawValue());
19547     },
19548
19549     // private
19550     doForce : function(){
19551         if(this.el.dom.value.length > 0){
19552             this.el.dom.value =
19553                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19554              
19555         }
19556     },
19557
19558     /**
19559      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19560      * query allowing the query action to be canceled if needed.
19561      * @param {String} query The SQL query to execute
19562      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19563      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19564      * saved in the current store (defaults to false)
19565      */
19566     doQuery : function(q, forceAll){
19567         if(q === undefined || q === null){
19568             q = '';
19569         }
19570         var qe = {
19571             query: q,
19572             forceAll: forceAll,
19573             combo: this,
19574             cancel:false
19575         };
19576         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19577             return false;
19578         }
19579         q = qe.query;
19580         forceAll = qe.forceAll;
19581         if(forceAll === true || (q.length >= this.minChars)){
19582             if(this.lastQuery != q || this.alwaysQuery){
19583                 this.lastQuery = q;
19584                 if(this.mode == 'local'){
19585                     this.selectedIndex = -1;
19586                     if(forceAll){
19587                         this.store.clearFilter();
19588                     }else{
19589                         this.store.filter(this.displayField, q);
19590                     }
19591                     this.onLoad();
19592                 }else{
19593                     this.store.baseParams[this.queryParam] = q;
19594                     this.store.load({
19595                         params: this.getParams(q)
19596                     });
19597                     this.expand();
19598                 }
19599             }else{
19600                 this.selectedIndex = -1;
19601                 this.onLoad();   
19602             }
19603         }
19604     },
19605
19606     // private
19607     getParams : function(q){
19608         var p = {};
19609         //p[this.queryParam] = q;
19610         if(this.pageSize){
19611             p.start = 0;
19612             p.limit = this.pageSize;
19613         }
19614         return p;
19615     },
19616
19617     /**
19618      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19619      */
19620     collapse : function(){
19621         if(!this.isExpanded()){
19622             return;
19623         }
19624         this.list.hide();
19625         Roo.get(document).un('mousedown', this.collapseIf, this);
19626         Roo.get(document).un('mousewheel', this.collapseIf, this);
19627         if (!this.editable) {
19628             Roo.get(document).un('keydown', this.listKeyPress, this);
19629         }
19630         this.fireEvent('collapse', this);
19631     },
19632
19633     // private
19634     collapseIf : function(e){
19635         if(!e.within(this.wrap) && !e.within(this.list)){
19636             this.collapse();
19637         }
19638     },
19639
19640     /**
19641      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19642      */
19643     expand : function(){
19644         if(this.isExpanded() || !this.hasFocus){
19645             return;
19646         }
19647         this.list.alignTo(this.el, this.listAlign);
19648         this.list.show();
19649         Roo.get(document).on('mousedown', this.collapseIf, this);
19650         Roo.get(document).on('mousewheel', this.collapseIf, this);
19651         if (!this.editable) {
19652             Roo.get(document).on('keydown', this.listKeyPress, this);
19653         }
19654         
19655         this.fireEvent('expand', this);
19656     },
19657
19658     // private
19659     // Implements the default empty TriggerField.onTriggerClick function
19660     onTriggerClick : function(){
19661         if(this.disabled){
19662             return;
19663         }
19664         if(this.isExpanded()){
19665             this.collapse();
19666             if (!this.blockFocus) {
19667                 this.el.focus();
19668             }
19669             
19670         }else {
19671             this.hasFocus = true;
19672             if(this.triggerAction == 'all') {
19673                 this.doQuery(this.allQuery, true);
19674             } else {
19675                 this.doQuery(this.getRawValue());
19676             }
19677             if (!this.blockFocus) {
19678                 this.el.focus();
19679             }
19680         }
19681     },
19682     listKeyPress : function(e)
19683     {
19684         //Roo.log('listkeypress');
19685         // scroll to first matching element based on key pres..
19686         if (e.isSpecialKey()) {
19687             return false;
19688         }
19689         var k = String.fromCharCode(e.getKey()).toUpperCase();
19690         //Roo.log(k);
19691         var match  = false;
19692         var csel = this.view.getSelectedNodes();
19693         var cselitem = false;
19694         if (csel.length) {
19695             var ix = this.view.indexOf(csel[0]);
19696             cselitem  = this.store.getAt(ix);
19697             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19698                 cselitem = false;
19699             }
19700             
19701         }
19702         
19703         this.store.each(function(v) { 
19704             if (cselitem) {
19705                 // start at existing selection.
19706                 if (cselitem.id == v.id) {
19707                     cselitem = false;
19708                 }
19709                 return;
19710             }
19711                 
19712             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19713                 match = this.store.indexOf(v);
19714                 return false;
19715             }
19716         }, this);
19717         
19718         if (match === false) {
19719             return true; // no more action?
19720         }
19721         // scroll to?
19722         this.view.select(match);
19723         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19724         sn.scrollIntoView(sn.dom.parentNode, false);
19725     } 
19726
19727     /** 
19728     * @cfg {Boolean} grow 
19729     * @hide 
19730     */
19731     /** 
19732     * @cfg {Number} growMin 
19733     * @hide 
19734     */
19735     /** 
19736     * @cfg {Number} growMax 
19737     * @hide 
19738     */
19739     /**
19740      * @hide
19741      * @method autoSize
19742      */
19743 });/*
19744  * Copyright(c) 2010-2012, Roo J Solutions Limited
19745  *
19746  * Licence LGPL
19747  *
19748  */
19749
19750 /**
19751  * @class Roo.form.ComboBoxArray
19752  * @extends Roo.form.TextField
19753  * A facebook style adder... for lists of email / people / countries  etc...
19754  * pick multiple items from a combo box, and shows each one.
19755  *
19756  *  Fred [x]  Brian [x]  [Pick another |v]
19757  *
19758  *
19759  *  For this to work: it needs various extra information
19760  *    - normal combo problay has
19761  *      name, hiddenName
19762  *    + displayField, valueField
19763  *
19764  *    For our purpose...
19765  *
19766  *
19767  *   If we change from 'extends' to wrapping...
19768  *   
19769  *  
19770  *
19771  
19772  
19773  * @constructor
19774  * Create a new ComboBoxArray.
19775  * @param {Object} config Configuration options
19776  */
19777  
19778
19779 Roo.form.ComboBoxArray = function(config)
19780 {
19781     this.addEvents({
19782         /**
19783          * @event beforeremove
19784          * Fires before remove the value from the list
19785              * @param {Roo.form.ComboBoxArray} _self This combo box array
19786              * @param {Roo.form.ComboBoxArray.Item} item removed item
19787              */
19788         'beforeremove' : true,
19789         /**
19790          * @event remove
19791          * Fires when remove the value from the list
19792              * @param {Roo.form.ComboBoxArray} _self This combo box array
19793              * @param {Roo.form.ComboBoxArray.Item} item removed item
19794              */
19795         'remove' : true
19796         
19797         
19798     });
19799     
19800     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19801     
19802     this.items = new Roo.util.MixedCollection(false);
19803     
19804     // construct the child combo...
19805     
19806     
19807     
19808     
19809    
19810     
19811 }
19812
19813  
19814 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19815
19816     /**
19817      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19818      */
19819     
19820     lastData : false,
19821     
19822     // behavies liek a hiddne field
19823     inputType:      'hidden',
19824     /**
19825      * @cfg {Number} width The width of the box that displays the selected element
19826      */ 
19827     width:          300,
19828
19829     
19830     
19831     /**
19832      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19833      */
19834     name : false,
19835     /**
19836      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19837      */
19838     hiddenName : false,
19839     
19840     
19841     // private the array of items that are displayed..
19842     items  : false,
19843     // private - the hidden field el.
19844     hiddenEl : false,
19845     // private - the filed el..
19846     el : false,
19847     
19848     //validateValue : function() { return true; }, // all values are ok!
19849     //onAddClick: function() { },
19850     
19851     onRender : function(ct, position) 
19852     {
19853         
19854         // create the standard hidden element
19855         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19856         
19857         
19858         // give fake names to child combo;
19859         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19860         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19861         
19862         this.combo = Roo.factory(this.combo, Roo.form);
19863         this.combo.onRender(ct, position);
19864         if (typeof(this.combo.width) != 'undefined') {
19865             this.combo.onResize(this.combo.width,0);
19866         }
19867         
19868         this.combo.initEvents();
19869         
19870         // assigned so form know we need to do this..
19871         this.store          = this.combo.store;
19872         this.valueField     = this.combo.valueField;
19873         this.displayField   = this.combo.displayField ;
19874         
19875         
19876         this.combo.wrap.addClass('x-cbarray-grp');
19877         
19878         var cbwrap = this.combo.wrap.createChild(
19879             {tag: 'div', cls: 'x-cbarray-cb'},
19880             this.combo.el.dom
19881         );
19882         
19883              
19884         this.hiddenEl = this.combo.wrap.createChild({
19885             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19886         });
19887         this.el = this.combo.wrap.createChild({
19888             tag: 'input',  type:'hidden' , name: this.name, value : ''
19889         });
19890          //   this.el.dom.removeAttribute("name");
19891         
19892         
19893         this.outerWrap = this.combo.wrap;
19894         this.wrap = cbwrap;
19895         
19896         this.outerWrap.setWidth(this.width);
19897         this.outerWrap.dom.removeChild(this.el.dom);
19898         
19899         this.wrap.dom.appendChild(this.el.dom);
19900         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19901         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19902         
19903         this.combo.trigger.setStyle('position','relative');
19904         this.combo.trigger.setStyle('left', '0px');
19905         this.combo.trigger.setStyle('top', '2px');
19906         
19907         this.combo.el.setStyle('vertical-align', 'text-bottom');
19908         
19909         //this.trigger.setStyle('vertical-align', 'top');
19910         
19911         // this should use the code from combo really... on('add' ....)
19912         if (this.adder) {
19913             
19914         
19915             this.adder = this.outerWrap.createChild(
19916                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19917             var _t = this;
19918             this.adder.on('click', function(e) {
19919                 _t.fireEvent('adderclick', this, e);
19920             }, _t);
19921         }
19922         //var _t = this;
19923         //this.adder.on('click', this.onAddClick, _t);
19924         
19925         
19926         this.combo.on('select', function(cb, rec, ix) {
19927             this.addItem(rec.data);
19928             
19929             cb.setValue('');
19930             cb.el.dom.value = '';
19931             //cb.lastData = rec.data;
19932             // add to list
19933             
19934         }, this);
19935         
19936         
19937     },
19938     
19939     
19940     getName: function()
19941     {
19942         // returns hidden if it's set..
19943         if (!this.rendered) {return ''};
19944         return  this.hiddenName ? this.hiddenName : this.name;
19945         
19946     },
19947     
19948     
19949     onResize: function(w, h){
19950         
19951         return;
19952         // not sure if this is needed..
19953         //this.combo.onResize(w,h);
19954         
19955         if(typeof w != 'number'){
19956             // we do not handle it!?!?
19957             return;
19958         }
19959         var tw = this.combo.trigger.getWidth();
19960         tw += this.addicon ? this.addicon.getWidth() : 0;
19961         tw += this.editicon ? this.editicon.getWidth() : 0;
19962         var x = w - tw;
19963         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19964             
19965         this.combo.trigger.setStyle('left', '0px');
19966         
19967         if(this.list && this.listWidth === undefined){
19968             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19969             this.list.setWidth(lw);
19970             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19971         }
19972         
19973     
19974         
19975     },
19976     
19977     addItem: function(rec)
19978     {
19979         var valueField = this.combo.valueField;
19980         var displayField = this.combo.displayField;
19981         
19982         if (this.items.indexOfKey(rec[valueField]) > -1) {
19983             //console.log("GOT " + rec.data.id);
19984             return;
19985         }
19986         
19987         var x = new Roo.form.ComboBoxArray.Item({
19988             //id : rec[this.idField],
19989             data : rec,
19990             displayField : displayField ,
19991             tipField : displayField ,
19992             cb : this
19993         });
19994         // use the 
19995         this.items.add(rec[valueField],x);
19996         // add it before the element..
19997         this.updateHiddenEl();
19998         x.render(this.outerWrap, this.wrap.dom);
19999         // add the image handler..
20000     },
20001     
20002     updateHiddenEl : function()
20003     {
20004         this.validate();
20005         if (!this.hiddenEl) {
20006             return;
20007         }
20008         var ar = [];
20009         var idField = this.combo.valueField;
20010         
20011         this.items.each(function(f) {
20012             ar.push(f.data[idField]);
20013         });
20014         this.hiddenEl.dom.value = ar.join(',');
20015         this.validate();
20016     },
20017     
20018     reset : function()
20019     {
20020         this.items.clear();
20021         
20022         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20023            el.remove();
20024         });
20025         
20026         this.el.dom.value = '';
20027         if (this.hiddenEl) {
20028             this.hiddenEl.dom.value = '';
20029         }
20030         
20031     },
20032     getValue: function()
20033     {
20034         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20035     },
20036     setValue: function(v) // not a valid action - must use addItems..
20037     {
20038         
20039         this.reset();
20040          
20041         if (this.store.isLocal && (typeof(v) == 'string')) {
20042             // then we can use the store to find the values..
20043             // comma seperated at present.. this needs to allow JSON based encoding..
20044             this.hiddenEl.value  = v;
20045             var v_ar = [];
20046             Roo.each(v.split(','), function(k) {
20047                 Roo.log("CHECK " + this.valueField + ',' + k);
20048                 var li = this.store.query(this.valueField, k);
20049                 if (!li.length) {
20050                     return;
20051                 }
20052                 var add = {};
20053                 add[this.valueField] = k;
20054                 add[this.displayField] = li.item(0).data[this.displayField];
20055                 
20056                 this.addItem(add);
20057             }, this) 
20058              
20059         }
20060         if (typeof(v) == 'object' ) {
20061             // then let's assume it's an array of objects..
20062             Roo.each(v, function(l) {
20063                 this.addItem(l);
20064             }, this);
20065              
20066         }
20067         
20068         
20069     },
20070     setFromData: function(v)
20071     {
20072         // this recieves an object, if setValues is called.
20073         this.reset();
20074         this.el.dom.value = v[this.displayField];
20075         this.hiddenEl.dom.value = v[this.valueField];
20076         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20077             return;
20078         }
20079         var kv = v[this.valueField];
20080         var dv = v[this.displayField];
20081         kv = typeof(kv) != 'string' ? '' : kv;
20082         dv = typeof(dv) != 'string' ? '' : dv;
20083         
20084         
20085         var keys = kv.split(',');
20086         var display = dv.split(',');
20087         for (var i = 0 ; i < keys.length; i++) {
20088             
20089             add = {};
20090             add[this.valueField] = keys[i];
20091             add[this.displayField] = display[i];
20092             this.addItem(add);
20093         }
20094       
20095         
20096     },
20097     
20098     /**
20099      * Validates the combox array value
20100      * @return {Boolean} True if the value is valid, else false
20101      */
20102     validate : function(){
20103         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20104             this.clearInvalid();
20105             return true;
20106         }
20107         return false;
20108     },
20109     
20110     validateValue : function(value){
20111         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20112         
20113     },
20114     
20115     /*@
20116      * overide
20117      * 
20118      */
20119     isDirty : function() {
20120         if(this.disabled) {
20121             return false;
20122         }
20123         
20124         try {
20125             var d = Roo.decode(String(this.originalValue));
20126         } catch (e) {
20127             return String(this.getValue()) !== String(this.originalValue);
20128         }
20129         
20130         var originalValue = [];
20131         
20132         for (var i = 0; i < d.length; i++){
20133             originalValue.push(d[i][this.valueField]);
20134         }
20135         
20136         return String(this.getValue()) !== String(originalValue.join(','));
20137         
20138     }
20139     
20140 });
20141
20142
20143
20144 /**
20145  * @class Roo.form.ComboBoxArray.Item
20146  * @extends Roo.BoxComponent
20147  * A selected item in the list
20148  *  Fred [x]  Brian [x]  [Pick another |v]
20149  * 
20150  * @constructor
20151  * Create a new item.
20152  * @param {Object} config Configuration options
20153  */
20154  
20155 Roo.form.ComboBoxArray.Item = function(config) {
20156     config.id = Roo.id();
20157     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20158 }
20159
20160 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20161     data : {},
20162     cb: false,
20163     displayField : false,
20164     tipField : false,
20165     
20166     
20167     defaultAutoCreate : {
20168         tag: 'div',
20169         cls: 'x-cbarray-item',
20170         cn : [ 
20171             { tag: 'div' },
20172             {
20173                 tag: 'img',
20174                 width:16,
20175                 height : 16,
20176                 src : Roo.BLANK_IMAGE_URL ,
20177                 align: 'center'
20178             }
20179         ]
20180         
20181     },
20182     
20183  
20184     onRender : function(ct, position)
20185     {
20186         Roo.form.Field.superclass.onRender.call(this, ct, position);
20187         
20188         if(!this.el){
20189             var cfg = this.getAutoCreate();
20190             this.el = ct.createChild(cfg, position);
20191         }
20192         
20193         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20194         
20195         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20196             this.cb.renderer(this.data) :
20197             String.format('{0}',this.data[this.displayField]);
20198         
20199             
20200         this.el.child('div').dom.setAttribute('qtip',
20201                         String.format('{0}',this.data[this.tipField])
20202         );
20203         
20204         this.el.child('img').on('click', this.remove, this);
20205         
20206     },
20207    
20208     remove : function()
20209     {
20210         if(this.cb.disabled){
20211             return;
20212         }
20213         
20214         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20215             this.cb.items.remove(this);
20216             this.el.child('img').un('click', this.remove, this);
20217             this.el.remove();
20218             this.cb.updateHiddenEl();
20219
20220             this.cb.fireEvent('remove', this.cb, this);
20221         }
20222         
20223     }
20224 });/*
20225  * RooJS Library 1.1.1
20226  * Copyright(c) 2008-2011  Alan Knowles
20227  *
20228  * License - LGPL
20229  */
20230  
20231
20232 /**
20233  * @class Roo.form.ComboNested
20234  * @extends Roo.form.ComboBox
20235  * A combobox for that allows selection of nested items in a list,
20236  * eg.
20237  *
20238  *  Book
20239  *    -> red
20240  *    -> green
20241  *  Table
20242  *    -> square
20243  *      ->red
20244  *      ->green
20245  *    -> rectangle
20246  *      ->green
20247  *      
20248  * 
20249  * @constructor
20250  * Create a new ComboNested
20251  * @param {Object} config Configuration options
20252  */
20253 Roo.form.ComboNested = function(config){
20254     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20255     // should verify some data...
20256     // like
20257     // hiddenName = required..
20258     // displayField = required
20259     // valudField == required
20260     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20261     var _t = this;
20262     Roo.each(req, function(e) {
20263         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20264             throw "Roo.form.ComboNested : missing value for: " + e;
20265         }
20266     });
20267      
20268     
20269 };
20270
20271 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20272    
20273     /*
20274      * @config {Number} max Number of columns to show
20275      */
20276     
20277     maxColumns : 3,
20278    
20279     list : null, // the outermost div..
20280     innerLists : null, // the
20281     views : null,
20282     stores : null,
20283     // private
20284     onRender : function(ct, position)
20285     {
20286         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20287         
20288         if(this.hiddenName){
20289             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20290                     'before', true);
20291             this.hiddenField.value =
20292                 this.hiddenValue !== undefined ? this.hiddenValue :
20293                 this.value !== undefined ? this.value : '';
20294
20295             // prevent input submission
20296             this.el.dom.removeAttribute('name');
20297              
20298              
20299         }
20300         
20301         if(Roo.isGecko){
20302             this.el.dom.setAttribute('autocomplete', 'off');
20303         }
20304
20305         var cls = 'x-combo-list';
20306
20307         this.list = new Roo.Layer({
20308             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20309         });
20310
20311         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20312         this.list.setWidth(lw);
20313         this.list.swallowEvent('mousewheel');
20314         this.assetHeight = 0;
20315
20316         if(this.title){
20317             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20318             this.assetHeight += this.header.getHeight();
20319         }
20320         this.innerLists = [];
20321         this.views = [];
20322         this.stores = [];
20323         for (var i =0 ; i < this.maxColumns; i++) {
20324             this.onRenderList( cls, i);
20325         }
20326         
20327         // always needs footer, as we are going to have an 'OK' button.
20328         this.footer = this.list.createChild({cls:cls+'-ft'});
20329         this.pageTb = new Roo.Toolbar(this.footer);  
20330         var _this = this;
20331         this.pageTb.add(  {
20332             
20333             text: 'Done',
20334             handler: function()
20335             {
20336                 _this.collapse();
20337             }
20338         });
20339         
20340         if ( this.allowBlank && !this.disableClear) {
20341             
20342             this.pageTb.add(new Roo.Toolbar.Fill(), {
20343                 cls: 'x-btn-icon x-btn-clear',
20344                 text: '&#160;',
20345                 handler: function()
20346                 {
20347                     _this.collapse();
20348                     _this.clearValue();
20349                     _this.onSelect(false, -1);
20350                 }
20351             });
20352         }
20353         if (this.footer) {
20354             this.assetHeight += this.footer.getHeight();
20355         }
20356         
20357     },
20358     onRenderList : function (  cls, i)
20359     {
20360         
20361         var lw = Math.floor(
20362                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20363         );
20364         
20365         this.list.setWidth(lw); // default to '1'
20366
20367         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20368         //il.on('mouseover', this.onViewOver, this, { list:  i });
20369         //il.on('mousemove', this.onViewMove, this, { list:  i });
20370         il.setWidth(lw);
20371         il.setStyle({ 'overflow-x' : 'hidden'});
20372
20373         if(!this.tpl){
20374             this.tpl = new Roo.Template({
20375                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20376                 isEmpty: function (value, allValues) {
20377                     //Roo.log(value);
20378                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20379                     return dl ? 'has-children' : 'no-children'
20380                 }
20381             });
20382         }
20383         
20384         var store  = this.store;
20385         if (i > 0) {
20386             store  = new Roo.data.SimpleStore({
20387                 //fields : this.store.reader.meta.fields,
20388                 reader : this.store.reader,
20389                 data : [ ]
20390             });
20391         }
20392         this.stores[i]  = store;
20393                 
20394         
20395         
20396         var view = this.views[i] = new Roo.View(
20397             il,
20398             this.tpl,
20399             {
20400                 singleSelect:true,
20401                 store: store,
20402                 selectedClass: this.selectedClass
20403             }
20404         );
20405         view.getEl().setWidth(lw);
20406         view.getEl().setStyle({
20407             position: i < 1 ? 'relative' : 'absolute',
20408             top: 0,
20409             left: (i * lw ) + 'px',
20410             display : i > 0 ? 'none' : 'block'
20411         });
20412         view.on('selectionchange', this.onSelectChange, this, {list : i });
20413         view.on('dblclick', this.onDoubleClick, this, {list : i });
20414         //view.on('click', this.onViewClick, this, { list : i });
20415
20416         store.on('beforeload', this.onBeforeLoad, this);
20417         store.on('load',  this.onLoad, this, { list  : i});
20418         store.on('loadexception', this.onLoadException, this);
20419
20420         // hide the other vies..
20421         
20422         
20423         
20424     },
20425     onResize : function()  {},
20426     
20427     restrictHeight : function()
20428     {
20429         var mh = 0;
20430         Roo.each(this.innerLists, function(il,i) {
20431             var el = this.views[i].getEl();
20432             el.dom.style.height = '';
20433             var inner = el.dom;
20434             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20435             // only adjust heights on other ones..
20436             if (i < 1) {
20437                 
20438                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20439                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20440                 mh = Math.max(el.getHeight(), mh);
20441             }
20442             
20443             
20444         }, this);
20445         
20446         this.list.beginUpdate();
20447         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20448         this.list.alignTo(this.el, this.listAlign);
20449         this.list.endUpdate();
20450         
20451     },
20452      
20453     
20454     // -- store handlers..
20455     // private
20456     onBeforeLoad : function()
20457     {
20458         if(!this.hasFocus){
20459             return;
20460         }
20461         this.innerLists[0].update(this.loadingText ?
20462                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20463         this.restrictHeight();
20464         this.selectedIndex = -1;
20465     },
20466     // private
20467     onLoad : function(a,b,c,d)
20468     {
20469         
20470         if(!this.hasFocus){
20471             return;
20472         }
20473         
20474         if(this.store.getCount() > 0) {
20475             this.expand();
20476             this.restrictHeight();   
20477         } else {
20478             this.onEmptyResults();
20479         }
20480         /*
20481         this.stores[1].loadData([]);
20482         this.stores[2].loadData([]);
20483         this.views
20484         */    
20485     
20486         //this.el.focus();
20487     },
20488     
20489     
20490     // private
20491     onLoadException : function()
20492     {
20493         this.collapse();
20494         Roo.log(this.store.reader.jsonData);
20495         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20496             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20497         }
20498         
20499         
20500     } ,
20501      
20502      
20503
20504     onSelectChange : function (view, sels, opts )
20505     {
20506         var ix = view.getSelectedIndexes();
20507         
20508         
20509         if (opts.list > this.maxColumns - 2) {
20510              
20511             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20512             return;
20513         }
20514         
20515         if (!ix.length) {
20516             this.setFromData({});
20517             var str = this.stores[opts.list+1];
20518             str.removeAll();
20519             return;
20520         }
20521         
20522         var rec = view.store.getAt(ix[0]);
20523         this.setFromData(rec.data);
20524         
20525         var lw = Math.floor(
20526                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20527         );
20528         
20529         this.stores[opts.list+1].loadDataFromChildren( rec );
20530         var dl = this.stores[opts.list+1]. getTotalCount();
20531         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20532         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20533         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20534         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20535     },
20536     onDoubleClick : function()
20537     {
20538         this.collapse(); //??
20539     },
20540     
20541      
20542     
20543     findRecord : function (prop,value)
20544     {
20545         return this.findRecordInStore(this.store, prop,value);
20546     },
20547     
20548     // private
20549     findRecordInStore : function(store, prop, value)
20550     {
20551         var cstore = new Roo.data.SimpleStore({
20552             //fields : this.store.reader.meta.fields, // we need array reader.. for
20553             reader : this.store.reader,
20554             data : [ ]
20555         });
20556         var _this = this;
20557         var record  = false;
20558         if(store.getCount() > 0){
20559            store.each(function(r){
20560                 if(r.data[prop] == value){
20561                     record = r;
20562                     return false;
20563                 }
20564                 if (r.data.cn && r.data.cn.length) {
20565                     cstore.loadDataFromChildren( r);
20566                     var cret = _this.findRecordInStore(cstore, prop, value);
20567                     if (cret !== false) {
20568                         record = cret;
20569                         return false;
20570                     }
20571                 }
20572                 
20573                 return true;
20574             });
20575         }
20576         return record;
20577     }
20578     
20579     
20580     
20581     
20582 });/*
20583  * Based on:
20584  * Ext JS Library 1.1.1
20585  * Copyright(c) 2006-2007, Ext JS, LLC.
20586  *
20587  * Originally Released Under LGPL - original licence link has changed is not relivant.
20588  *
20589  * Fork - LGPL
20590  * <script type="text/javascript">
20591  */
20592 /**
20593  * @class Roo.form.Checkbox
20594  * @extends Roo.form.Field
20595  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20596  * @constructor
20597  * Creates a new Checkbox
20598  * @param {Object} config Configuration options
20599  */
20600 Roo.form.Checkbox = function(config){
20601     Roo.form.Checkbox.superclass.constructor.call(this, config);
20602     this.addEvents({
20603         /**
20604          * @event check
20605          * Fires when the checkbox is checked or unchecked.
20606              * @param {Roo.form.Checkbox} this This checkbox
20607              * @param {Boolean} checked The new checked value
20608              */
20609         check : true
20610     });
20611 };
20612
20613 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20614     /**
20615      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20616      */
20617     focusClass : undefined,
20618     /**
20619      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20620      */
20621     fieldClass: "x-form-field",
20622     /**
20623      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20624      */
20625     checked: false,
20626     /**
20627      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20628      * {tag: "input", type: "checkbox", autocomplete: "off"})
20629      */
20630     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20631     /**
20632      * @cfg {String} boxLabel The text that appears beside the checkbox
20633      */
20634     boxLabel : "",
20635     /**
20636      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20637      */  
20638     inputValue : '1',
20639     /**
20640      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20641      */
20642      valueOff: '0', // value when not checked..
20643
20644     actionMode : 'viewEl', 
20645     //
20646     // private
20647     itemCls : 'x-menu-check-item x-form-item',
20648     groupClass : 'x-menu-group-item',
20649     inputType : 'hidden',
20650     
20651     
20652     inSetChecked: false, // check that we are not calling self...
20653     
20654     inputElement: false, // real input element?
20655     basedOn: false, // ????
20656     
20657     isFormField: true, // not sure where this is needed!!!!
20658
20659     onResize : function(){
20660         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20661         if(!this.boxLabel){
20662             this.el.alignTo(this.wrap, 'c-c');
20663         }
20664     },
20665
20666     initEvents : function(){
20667         Roo.form.Checkbox.superclass.initEvents.call(this);
20668         this.el.on("click", this.onClick,  this);
20669         this.el.on("change", this.onClick,  this);
20670     },
20671
20672
20673     getResizeEl : function(){
20674         return this.wrap;
20675     },
20676
20677     getPositionEl : function(){
20678         return this.wrap;
20679     },
20680
20681     // private
20682     onRender : function(ct, position){
20683         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20684         /*
20685         if(this.inputValue !== undefined){
20686             this.el.dom.value = this.inputValue;
20687         }
20688         */
20689         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20690         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20691         var viewEl = this.wrap.createChild({ 
20692             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20693         this.viewEl = viewEl;   
20694         this.wrap.on('click', this.onClick,  this); 
20695         
20696         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20697         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20698         
20699         
20700         
20701         if(this.boxLabel){
20702             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20703         //    viewEl.on('click', this.onClick,  this); 
20704         }
20705         //if(this.checked){
20706             this.setChecked(this.checked);
20707         //}else{
20708             //this.checked = this.el.dom;
20709         //}
20710
20711     },
20712
20713     // private
20714     initValue : Roo.emptyFn,
20715
20716     /**
20717      * Returns the checked state of the checkbox.
20718      * @return {Boolean} True if checked, else false
20719      */
20720     getValue : function(){
20721         if(this.el){
20722             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20723         }
20724         return this.valueOff;
20725         
20726     },
20727
20728         // private
20729     onClick : function(){ 
20730         if (this.disabled) {
20731             return;
20732         }
20733         this.setChecked(!this.checked);
20734
20735         //if(this.el.dom.checked != this.checked){
20736         //    this.setValue(this.el.dom.checked);
20737        // }
20738     },
20739
20740     /**
20741      * Sets the checked state of the checkbox.
20742      * On is always based on a string comparison between inputValue and the param.
20743      * @param {Boolean/String} value - the value to set 
20744      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20745      */
20746     setValue : function(v,suppressEvent){
20747         
20748         
20749         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20750         //if(this.el && this.el.dom){
20751         //    this.el.dom.checked = this.checked;
20752         //    this.el.dom.defaultChecked = this.checked;
20753         //}
20754         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20755         //this.fireEvent("check", this, this.checked);
20756     },
20757     // private..
20758     setChecked : function(state,suppressEvent)
20759     {
20760         if (this.inSetChecked) {
20761             this.checked = state;
20762             return;
20763         }
20764         
20765     
20766         if(this.wrap){
20767             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20768         }
20769         this.checked = state;
20770         if(suppressEvent !== true){
20771             this.fireEvent('check', this, state);
20772         }
20773         this.inSetChecked = true;
20774         this.el.dom.value = state ? this.inputValue : this.valueOff;
20775         this.inSetChecked = false;
20776         
20777     },
20778     // handle setting of hidden value by some other method!!?!?
20779     setFromHidden: function()
20780     {
20781         if(!this.el){
20782             return;
20783         }
20784         //console.log("SET FROM HIDDEN");
20785         //alert('setFrom hidden');
20786         this.setValue(this.el.dom.value);
20787     },
20788     
20789     onDestroy : function()
20790     {
20791         if(this.viewEl){
20792             Roo.get(this.viewEl).remove();
20793         }
20794          
20795         Roo.form.Checkbox.superclass.onDestroy.call(this);
20796     },
20797     
20798     setBoxLabel : function(str)
20799     {
20800         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20801     }
20802
20803 });/*
20804  * Based on:
20805  * Ext JS Library 1.1.1
20806  * Copyright(c) 2006-2007, Ext JS, LLC.
20807  *
20808  * Originally Released Under LGPL - original licence link has changed is not relivant.
20809  *
20810  * Fork - LGPL
20811  * <script type="text/javascript">
20812  */
20813  
20814 /**
20815  * @class Roo.form.Radio
20816  * @extends Roo.form.Checkbox
20817  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20818  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20819  * @constructor
20820  * Creates a new Radio
20821  * @param {Object} config Configuration options
20822  */
20823 Roo.form.Radio = function(){
20824     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20825 };
20826 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20827     inputType: 'radio',
20828
20829     /**
20830      * If this radio is part of a group, it will return the selected value
20831      * @return {String}
20832      */
20833     getGroupValue : function(){
20834         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20835     },
20836     
20837     
20838     onRender : function(ct, position){
20839         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20840         
20841         if(this.inputValue !== undefined){
20842             this.el.dom.value = this.inputValue;
20843         }
20844          
20845         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20846         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20847         //var viewEl = this.wrap.createChild({ 
20848         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20849         //this.viewEl = viewEl;   
20850         //this.wrap.on('click', this.onClick,  this); 
20851         
20852         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20853         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20854         
20855         
20856         
20857         if(this.boxLabel){
20858             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20859         //    viewEl.on('click', this.onClick,  this); 
20860         }
20861          if(this.checked){
20862             this.el.dom.checked =   'checked' ;
20863         }
20864          
20865     } 
20866     
20867     
20868 });//<script type="text/javascript">
20869
20870 /*
20871  * Based  Ext JS Library 1.1.1
20872  * Copyright(c) 2006-2007, Ext JS, LLC.
20873  * LGPL
20874  *
20875  */
20876  
20877 /**
20878  * @class Roo.HtmlEditorCore
20879  * @extends Roo.Component
20880  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20881  *
20882  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20883  */
20884
20885 Roo.HtmlEditorCore = function(config){
20886     
20887     
20888     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20889     
20890     
20891     this.addEvents({
20892         /**
20893          * @event initialize
20894          * Fires when the editor is fully initialized (including the iframe)
20895          * @param {Roo.HtmlEditorCore} this
20896          */
20897         initialize: true,
20898         /**
20899          * @event activate
20900          * Fires when the editor is first receives the focus. Any insertion must wait
20901          * until after this event.
20902          * @param {Roo.HtmlEditorCore} this
20903          */
20904         activate: true,
20905          /**
20906          * @event beforesync
20907          * Fires before the textarea is updated with content from the editor iframe. Return false
20908          * to cancel the sync.
20909          * @param {Roo.HtmlEditorCore} this
20910          * @param {String} html
20911          */
20912         beforesync: true,
20913          /**
20914          * @event beforepush
20915          * Fires before the iframe editor is updated with content from the textarea. Return false
20916          * to cancel the push.
20917          * @param {Roo.HtmlEditorCore} this
20918          * @param {String} html
20919          */
20920         beforepush: true,
20921          /**
20922          * @event sync
20923          * Fires when the textarea is updated with content from the editor iframe.
20924          * @param {Roo.HtmlEditorCore} this
20925          * @param {String} html
20926          */
20927         sync: true,
20928          /**
20929          * @event push
20930          * Fires when the iframe editor is updated with content from the textarea.
20931          * @param {Roo.HtmlEditorCore} this
20932          * @param {String} html
20933          */
20934         push: true,
20935         
20936         /**
20937          * @event editorevent
20938          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20939          * @param {Roo.HtmlEditorCore} this
20940          */
20941         editorevent: true
20942         
20943     });
20944     
20945     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20946     
20947     // defaults : white / black...
20948     this.applyBlacklists();
20949     
20950     
20951     
20952 };
20953
20954
20955 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20956
20957
20958      /**
20959      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20960      */
20961     
20962     owner : false,
20963     
20964      /**
20965      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20966      *                        Roo.resizable.
20967      */
20968     resizable : false,
20969      /**
20970      * @cfg {Number} height (in pixels)
20971      */   
20972     height: 300,
20973    /**
20974      * @cfg {Number} width (in pixels)
20975      */   
20976     width: 500,
20977     
20978     /**
20979      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20980      * 
20981      */
20982     stylesheets: false,
20983     
20984     // id of frame..
20985     frameId: false,
20986     
20987     // private properties
20988     validationEvent : false,
20989     deferHeight: true,
20990     initialized : false,
20991     activated : false,
20992     sourceEditMode : false,
20993     onFocus : Roo.emptyFn,
20994     iframePad:3,
20995     hideMode:'offsets',
20996     
20997     clearUp: true,
20998     
20999     // blacklist + whitelisted elements..
21000     black: false,
21001     white: false,
21002      
21003     bodyCls : '',
21004
21005     /**
21006      * Protected method that will not generally be called directly. It
21007      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21008      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21009      */
21010     getDocMarkup : function(){
21011         // body styles..
21012         var st = '';
21013         
21014         // inherit styels from page...?? 
21015         if (this.stylesheets === false) {
21016             
21017             Roo.get(document.head).select('style').each(function(node) {
21018                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21019             });
21020             
21021             Roo.get(document.head).select('link').each(function(node) { 
21022                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21023             });
21024             
21025         } else if (!this.stylesheets.length) {
21026                 // simple..
21027                 st = '<style type="text/css">' +
21028                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21029                    '</style>';
21030         } else { 
21031             st = '<style type="text/css">' +
21032                     this.stylesheets +
21033                 '</style>';
21034         }
21035         
21036         st +=  '<style type="text/css">' +
21037             'IMG { cursor: pointer } ' +
21038         '</style>';
21039
21040         var cls = 'roo-htmleditor-body';
21041         
21042         if(this.bodyCls.length){
21043             cls += ' ' + this.bodyCls;
21044         }
21045         
21046         return '<html><head>' + st  +
21047             //<style type="text/css">' +
21048             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21049             //'</style>' +
21050             ' </head><body class="' +  cls + '"></body></html>';
21051     },
21052
21053     // private
21054     onRender : function(ct, position)
21055     {
21056         var _t = this;
21057         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21058         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21059         
21060         
21061         this.el.dom.style.border = '0 none';
21062         this.el.dom.setAttribute('tabIndex', -1);
21063         this.el.addClass('x-hidden hide');
21064         
21065         
21066         
21067         if(Roo.isIE){ // fix IE 1px bogus margin
21068             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21069         }
21070        
21071         
21072         this.frameId = Roo.id();
21073         
21074          
21075         
21076         var iframe = this.owner.wrap.createChild({
21077             tag: 'iframe',
21078             cls: 'form-control', // bootstrap..
21079             id: this.frameId,
21080             name: this.frameId,
21081             frameBorder : 'no',
21082             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21083         }, this.el
21084         );
21085         
21086         
21087         this.iframe = iframe.dom;
21088
21089          this.assignDocWin();
21090         
21091         this.doc.designMode = 'on';
21092        
21093         this.doc.open();
21094         this.doc.write(this.getDocMarkup());
21095         this.doc.close();
21096
21097         
21098         var task = { // must defer to wait for browser to be ready
21099             run : function(){
21100                 //console.log("run task?" + this.doc.readyState);
21101                 this.assignDocWin();
21102                 if(this.doc.body || this.doc.readyState == 'complete'){
21103                     try {
21104                         this.doc.designMode="on";
21105                     } catch (e) {
21106                         return;
21107                     }
21108                     Roo.TaskMgr.stop(task);
21109                     this.initEditor.defer(10, this);
21110                 }
21111             },
21112             interval : 10,
21113             duration: 10000,
21114             scope: this
21115         };
21116         Roo.TaskMgr.start(task);
21117
21118     },
21119
21120     // private
21121     onResize : function(w, h)
21122     {
21123          Roo.log('resize: ' +w + ',' + h );
21124         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21125         if(!this.iframe){
21126             return;
21127         }
21128         if(typeof w == 'number'){
21129             
21130             this.iframe.style.width = w + 'px';
21131         }
21132         if(typeof h == 'number'){
21133             
21134             this.iframe.style.height = h + 'px';
21135             if(this.doc){
21136                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21137             }
21138         }
21139         
21140     },
21141
21142     /**
21143      * Toggles the editor between standard and source edit mode.
21144      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21145      */
21146     toggleSourceEdit : function(sourceEditMode){
21147         
21148         this.sourceEditMode = sourceEditMode === true;
21149         
21150         if(this.sourceEditMode){
21151  
21152             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21153             
21154         }else{
21155             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21156             //this.iframe.className = '';
21157             this.deferFocus();
21158         }
21159         //this.setSize(this.owner.wrap.getSize());
21160         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21161     },
21162
21163     
21164   
21165
21166     /**
21167      * Protected method that will not generally be called directly. If you need/want
21168      * custom HTML cleanup, this is the method you should override.
21169      * @param {String} html The HTML to be cleaned
21170      * return {String} The cleaned HTML
21171      */
21172     cleanHtml : function(html){
21173         html = String(html);
21174         if(html.length > 5){
21175             if(Roo.isSafari){ // strip safari nonsense
21176                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21177             }
21178         }
21179         if(html == '&nbsp;'){
21180             html = '';
21181         }
21182         return html;
21183     },
21184
21185     /**
21186      * HTML Editor -> Textarea
21187      * Protected method that will not generally be called directly. Syncs the contents
21188      * of the editor iframe with the textarea.
21189      */
21190     syncValue : function(){
21191         if(this.initialized){
21192             var bd = (this.doc.body || this.doc.documentElement);
21193             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21194             var html = bd.innerHTML;
21195             if(Roo.isSafari){
21196                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21197                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21198                 if(m && m[1]){
21199                     html = '<div style="'+m[0]+'">' + html + '</div>';
21200                 }
21201             }
21202             html = this.cleanHtml(html);
21203             // fix up the special chars.. normaly like back quotes in word...
21204             // however we do not want to do this with chinese..
21205             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21206                 
21207                 var cc = match.charCodeAt();
21208
21209                 // Get the character value, handling surrogate pairs
21210                 if (match.length == 2) {
21211                     // It's a surrogate pair, calculate the Unicode code point
21212                     var high = match.charCodeAt(0) - 0xD800;
21213                     var low  = match.charCodeAt(1) - 0xDC00;
21214                     cc = (high * 0x400) + low + 0x10000;
21215                 }  else if (
21216                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21217                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21218                     (cc >= 0xf900 && cc < 0xfb00 )
21219                 ) {
21220                         return match;
21221                 }  
21222          
21223                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21224                 return "&#" + cc + ";";
21225                 
21226                 
21227             });
21228             
21229             
21230              
21231             if(this.owner.fireEvent('beforesync', this, html) !== false){
21232                 this.el.dom.value = html;
21233                 this.owner.fireEvent('sync', this, html);
21234             }
21235         }
21236     },
21237
21238     /**
21239      * Protected method that will not generally be called directly. Pushes the value of the textarea
21240      * into the iframe editor.
21241      */
21242     pushValue : function(){
21243         if(this.initialized){
21244             var v = this.el.dom.value.trim();
21245             
21246 //            if(v.length < 1){
21247 //                v = '&#160;';
21248 //            }
21249             
21250             if(this.owner.fireEvent('beforepush', this, v) !== false){
21251                 var d = (this.doc.body || this.doc.documentElement);
21252                 d.innerHTML = v;
21253                 this.cleanUpPaste();
21254                 this.el.dom.value = d.innerHTML;
21255                 this.owner.fireEvent('push', this, v);
21256             }
21257         }
21258     },
21259
21260     // private
21261     deferFocus : function(){
21262         this.focus.defer(10, this);
21263     },
21264
21265     // doc'ed in Field
21266     focus : function(){
21267         if(this.win && !this.sourceEditMode){
21268             this.win.focus();
21269         }else{
21270             this.el.focus();
21271         }
21272     },
21273     
21274     assignDocWin: function()
21275     {
21276         var iframe = this.iframe;
21277         
21278          if(Roo.isIE){
21279             this.doc = iframe.contentWindow.document;
21280             this.win = iframe.contentWindow;
21281         } else {
21282 //            if (!Roo.get(this.frameId)) {
21283 //                return;
21284 //            }
21285 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21286 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21287             
21288             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21289                 return;
21290             }
21291             
21292             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21293             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21294         }
21295     },
21296     
21297     // private
21298     initEditor : function(){
21299         //console.log("INIT EDITOR");
21300         this.assignDocWin();
21301         
21302         
21303         
21304         this.doc.designMode="on";
21305         this.doc.open();
21306         this.doc.write(this.getDocMarkup());
21307         this.doc.close();
21308         
21309         var dbody = (this.doc.body || this.doc.documentElement);
21310         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21311         // this copies styles from the containing element into thsi one..
21312         // not sure why we need all of this..
21313         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21314         
21315         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21316         //ss['background-attachment'] = 'fixed'; // w3c
21317         dbody.bgProperties = 'fixed'; // ie
21318         //Roo.DomHelper.applyStyles(dbody, ss);
21319         Roo.EventManager.on(this.doc, {
21320             //'mousedown': this.onEditorEvent,
21321             'mouseup': this.onEditorEvent,
21322             'dblclick': this.onEditorEvent,
21323             'click': this.onEditorEvent,
21324             'keyup': this.onEditorEvent,
21325             buffer:100,
21326             scope: this
21327         });
21328         if(Roo.isGecko){
21329             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21330         }
21331         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21332             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21333         }
21334         this.initialized = true;
21335
21336         this.owner.fireEvent('initialize', this);
21337         this.pushValue();
21338     },
21339
21340     // private
21341     onDestroy : function(){
21342         
21343         
21344         
21345         if(this.rendered){
21346             
21347             //for (var i =0; i < this.toolbars.length;i++) {
21348             //    // fixme - ask toolbars for heights?
21349             //    this.toolbars[i].onDestroy();
21350            // }
21351             
21352             //this.wrap.dom.innerHTML = '';
21353             //this.wrap.remove();
21354         }
21355     },
21356
21357     // private
21358     onFirstFocus : function(){
21359         
21360         this.assignDocWin();
21361         
21362         
21363         this.activated = true;
21364          
21365     
21366         if(Roo.isGecko){ // prevent silly gecko errors
21367             this.win.focus();
21368             var s = this.win.getSelection();
21369             if(!s.focusNode || s.focusNode.nodeType != 3){
21370                 var r = s.getRangeAt(0);
21371                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21372                 r.collapse(true);
21373                 this.deferFocus();
21374             }
21375             try{
21376                 this.execCmd('useCSS', true);
21377                 this.execCmd('styleWithCSS', false);
21378             }catch(e){}
21379         }
21380         this.owner.fireEvent('activate', this);
21381     },
21382
21383     // private
21384     adjustFont: function(btn){
21385         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21386         //if(Roo.isSafari){ // safari
21387         //    adjust *= 2;
21388        // }
21389         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21390         if(Roo.isSafari){ // safari
21391             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21392             v =  (v < 10) ? 10 : v;
21393             v =  (v > 48) ? 48 : v;
21394             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21395             
21396         }
21397         
21398         
21399         v = Math.max(1, v+adjust);
21400         
21401         this.execCmd('FontSize', v  );
21402     },
21403
21404     onEditorEvent : function(e)
21405     {
21406         this.owner.fireEvent('editorevent', this, e);
21407       //  this.updateToolbar();
21408         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21409     },
21410
21411     insertTag : function(tg)
21412     {
21413         // could be a bit smarter... -> wrap the current selected tRoo..
21414         if (tg.toLowerCase() == 'span' ||
21415             tg.toLowerCase() == 'code' ||
21416             tg.toLowerCase() == 'sup' ||
21417             tg.toLowerCase() == 'sub' 
21418             ) {
21419             
21420             range = this.createRange(this.getSelection());
21421             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21422             wrappingNode.appendChild(range.extractContents());
21423             range.insertNode(wrappingNode);
21424
21425             return;
21426             
21427             
21428             
21429         }
21430         this.execCmd("formatblock",   tg);
21431         
21432     },
21433     
21434     insertText : function(txt)
21435     {
21436         
21437         
21438         var range = this.createRange();
21439         range.deleteContents();
21440                //alert(Sender.getAttribute('label'));
21441                
21442         range.insertNode(this.doc.createTextNode(txt));
21443     } ,
21444     
21445      
21446
21447     /**
21448      * Executes a Midas editor command on the editor document and performs necessary focus and
21449      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21450      * @param {String} cmd The Midas command
21451      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21452      */
21453     relayCmd : function(cmd, value){
21454         this.win.focus();
21455         this.execCmd(cmd, value);
21456         this.owner.fireEvent('editorevent', this);
21457         //this.updateToolbar();
21458         this.owner.deferFocus();
21459     },
21460
21461     /**
21462      * Executes a Midas editor command directly on the editor document.
21463      * For visual commands, you should use {@link #relayCmd} instead.
21464      * <b>This should only be called after the editor is initialized.</b>
21465      * @param {String} cmd The Midas command
21466      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21467      */
21468     execCmd : function(cmd, value){
21469         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21470         this.syncValue();
21471     },
21472  
21473  
21474    
21475     /**
21476      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21477      * to insert tRoo.
21478      * @param {String} text | dom node.. 
21479      */
21480     insertAtCursor : function(text)
21481     {
21482         
21483         if(!this.activated){
21484             return;
21485         }
21486         /*
21487         if(Roo.isIE){
21488             this.win.focus();
21489             var r = this.doc.selection.createRange();
21490             if(r){
21491                 r.collapse(true);
21492                 r.pasteHTML(text);
21493                 this.syncValue();
21494                 this.deferFocus();
21495             
21496             }
21497             return;
21498         }
21499         */
21500         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21501             this.win.focus();
21502             
21503             
21504             // from jquery ui (MIT licenced)
21505             var range, node;
21506             var win = this.win;
21507             
21508             if (win.getSelection && win.getSelection().getRangeAt) {
21509                 range = win.getSelection().getRangeAt(0);
21510                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21511                 range.insertNode(node);
21512             } else if (win.document.selection && win.document.selection.createRange) {
21513                 // no firefox support
21514                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21515                 win.document.selection.createRange().pasteHTML(txt);
21516             } else {
21517                 // no firefox support
21518                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21519                 this.execCmd('InsertHTML', txt);
21520             } 
21521             
21522             this.syncValue();
21523             
21524             this.deferFocus();
21525         }
21526     },
21527  // private
21528     mozKeyPress : function(e){
21529         if(e.ctrlKey){
21530             var c = e.getCharCode(), cmd;
21531           
21532             if(c > 0){
21533                 c = String.fromCharCode(c).toLowerCase();
21534                 switch(c){
21535                     case 'b':
21536                         cmd = 'bold';
21537                         break;
21538                     case 'i':
21539                         cmd = 'italic';
21540                         break;
21541                     
21542                     case 'u':
21543                         cmd = 'underline';
21544                         break;
21545                     
21546                     case 'v':
21547                         this.cleanUpPaste.defer(100, this);
21548                         return;
21549                         
21550                 }
21551                 if(cmd){
21552                     this.win.focus();
21553                     this.execCmd(cmd);
21554                     this.deferFocus();
21555                     e.preventDefault();
21556                 }
21557                 
21558             }
21559         }
21560     },
21561
21562     // private
21563     fixKeys : function(){ // load time branching for fastest keydown performance
21564         if(Roo.isIE){
21565             return function(e){
21566                 var k = e.getKey(), r;
21567                 if(k == e.TAB){
21568                     e.stopEvent();
21569                     r = this.doc.selection.createRange();
21570                     if(r){
21571                         r.collapse(true);
21572                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21573                         this.deferFocus();
21574                     }
21575                     return;
21576                 }
21577                 
21578                 if(k == e.ENTER){
21579                     r = this.doc.selection.createRange();
21580                     if(r){
21581                         var target = r.parentElement();
21582                         if(!target || target.tagName.toLowerCase() != 'li'){
21583                             e.stopEvent();
21584                             r.pasteHTML('<br />');
21585                             r.collapse(false);
21586                             r.select();
21587                         }
21588                     }
21589                 }
21590                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21591                     this.cleanUpPaste.defer(100, this);
21592                     return;
21593                 }
21594                 
21595                 
21596             };
21597         }else if(Roo.isOpera){
21598             return function(e){
21599                 var k = e.getKey();
21600                 if(k == e.TAB){
21601                     e.stopEvent();
21602                     this.win.focus();
21603                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21604                     this.deferFocus();
21605                 }
21606                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21607                     this.cleanUpPaste.defer(100, this);
21608                     return;
21609                 }
21610                 
21611             };
21612         }else if(Roo.isSafari){
21613             return function(e){
21614                 var k = e.getKey();
21615                 
21616                 if(k == e.TAB){
21617                     e.stopEvent();
21618                     this.execCmd('InsertText','\t');
21619                     this.deferFocus();
21620                     return;
21621                 }
21622                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21623                     this.cleanUpPaste.defer(100, this);
21624                     return;
21625                 }
21626                 
21627              };
21628         }
21629     }(),
21630     
21631     getAllAncestors: function()
21632     {
21633         var p = this.getSelectedNode();
21634         var a = [];
21635         if (!p) {
21636             a.push(p); // push blank onto stack..
21637             p = this.getParentElement();
21638         }
21639         
21640         
21641         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21642             a.push(p);
21643             p = p.parentNode;
21644         }
21645         a.push(this.doc.body);
21646         return a;
21647     },
21648     lastSel : false,
21649     lastSelNode : false,
21650     
21651     
21652     getSelection : function() 
21653     {
21654         this.assignDocWin();
21655         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21656     },
21657     
21658     getSelectedNode: function() 
21659     {
21660         // this may only work on Gecko!!!
21661         
21662         // should we cache this!!!!
21663         
21664         
21665         
21666          
21667         var range = this.createRange(this.getSelection()).cloneRange();
21668         
21669         if (Roo.isIE) {
21670             var parent = range.parentElement();
21671             while (true) {
21672                 var testRange = range.duplicate();
21673                 testRange.moveToElementText(parent);
21674                 if (testRange.inRange(range)) {
21675                     break;
21676                 }
21677                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21678                     break;
21679                 }
21680                 parent = parent.parentElement;
21681             }
21682             return parent;
21683         }
21684         
21685         // is ancestor a text element.
21686         var ac =  range.commonAncestorContainer;
21687         if (ac.nodeType == 3) {
21688             ac = ac.parentNode;
21689         }
21690         
21691         var ar = ac.childNodes;
21692          
21693         var nodes = [];
21694         var other_nodes = [];
21695         var has_other_nodes = false;
21696         for (var i=0;i<ar.length;i++) {
21697             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21698                 continue;
21699             }
21700             // fullly contained node.
21701             
21702             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21703                 nodes.push(ar[i]);
21704                 continue;
21705             }
21706             
21707             // probably selected..
21708             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21709                 other_nodes.push(ar[i]);
21710                 continue;
21711             }
21712             // outer..
21713             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21714                 continue;
21715             }
21716             
21717             
21718             has_other_nodes = true;
21719         }
21720         if (!nodes.length && other_nodes.length) {
21721             nodes= other_nodes;
21722         }
21723         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21724             return false;
21725         }
21726         
21727         return nodes[0];
21728     },
21729     createRange: function(sel)
21730     {
21731         // this has strange effects when using with 
21732         // top toolbar - not sure if it's a great idea.
21733         //this.editor.contentWindow.focus();
21734         if (typeof sel != "undefined") {
21735             try {
21736                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21737             } catch(e) {
21738                 return this.doc.createRange();
21739             }
21740         } else {
21741             return this.doc.createRange();
21742         }
21743     },
21744     getParentElement: function()
21745     {
21746         
21747         this.assignDocWin();
21748         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21749         
21750         var range = this.createRange(sel);
21751          
21752         try {
21753             var p = range.commonAncestorContainer;
21754             while (p.nodeType == 3) { // text node
21755                 p = p.parentNode;
21756             }
21757             return p;
21758         } catch (e) {
21759             return null;
21760         }
21761     
21762     },
21763     /***
21764      *
21765      * Range intersection.. the hard stuff...
21766      *  '-1' = before
21767      *  '0' = hits..
21768      *  '1' = after.
21769      *         [ -- selected range --- ]
21770      *   [fail]                        [fail]
21771      *
21772      *    basically..
21773      *      if end is before start or  hits it. fail.
21774      *      if start is after end or hits it fail.
21775      *
21776      *   if either hits (but other is outside. - then it's not 
21777      *   
21778      *    
21779      **/
21780     
21781     
21782     // @see http://www.thismuchiknow.co.uk/?p=64.
21783     rangeIntersectsNode : function(range, node)
21784     {
21785         var nodeRange = node.ownerDocument.createRange();
21786         try {
21787             nodeRange.selectNode(node);
21788         } catch (e) {
21789             nodeRange.selectNodeContents(node);
21790         }
21791     
21792         var rangeStartRange = range.cloneRange();
21793         rangeStartRange.collapse(true);
21794     
21795         var rangeEndRange = range.cloneRange();
21796         rangeEndRange.collapse(false);
21797     
21798         var nodeStartRange = nodeRange.cloneRange();
21799         nodeStartRange.collapse(true);
21800     
21801         var nodeEndRange = nodeRange.cloneRange();
21802         nodeEndRange.collapse(false);
21803     
21804         return rangeStartRange.compareBoundaryPoints(
21805                  Range.START_TO_START, nodeEndRange) == -1 &&
21806                rangeEndRange.compareBoundaryPoints(
21807                  Range.START_TO_START, nodeStartRange) == 1;
21808         
21809          
21810     },
21811     rangeCompareNode : function(range, node)
21812     {
21813         var nodeRange = node.ownerDocument.createRange();
21814         try {
21815             nodeRange.selectNode(node);
21816         } catch (e) {
21817             nodeRange.selectNodeContents(node);
21818         }
21819         
21820         
21821         range.collapse(true);
21822     
21823         nodeRange.collapse(true);
21824      
21825         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21826         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21827          
21828         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21829         
21830         var nodeIsBefore   =  ss == 1;
21831         var nodeIsAfter    = ee == -1;
21832         
21833         if (nodeIsBefore && nodeIsAfter) {
21834             return 0; // outer
21835         }
21836         if (!nodeIsBefore && nodeIsAfter) {
21837             return 1; //right trailed.
21838         }
21839         
21840         if (nodeIsBefore && !nodeIsAfter) {
21841             return 2;  // left trailed.
21842         }
21843         // fully contined.
21844         return 3;
21845     },
21846
21847     // private? - in a new class?
21848     cleanUpPaste :  function()
21849     {
21850         // cleans up the whole document..
21851         Roo.log('cleanuppaste');
21852         
21853         this.cleanUpChildren(this.doc.body);
21854         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21855         if (clean != this.doc.body.innerHTML) {
21856             this.doc.body.innerHTML = clean;
21857         }
21858         
21859     },
21860     
21861     cleanWordChars : function(input) {// change the chars to hex code
21862         var he = Roo.HtmlEditorCore;
21863         
21864         var output = input;
21865         Roo.each(he.swapCodes, function(sw) { 
21866             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21867             
21868             output = output.replace(swapper, sw[1]);
21869         });
21870         
21871         return output;
21872     },
21873     
21874     
21875     cleanUpChildren : function (n)
21876     {
21877         if (!n.childNodes.length) {
21878             return;
21879         }
21880         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21881            this.cleanUpChild(n.childNodes[i]);
21882         }
21883     },
21884     
21885     
21886         
21887     
21888     cleanUpChild : function (node)
21889     {
21890         var ed = this;
21891         //console.log(node);
21892         if (node.nodeName == "#text") {
21893             // clean up silly Windows -- stuff?
21894             return; 
21895         }
21896         if (node.nodeName == "#comment") {
21897             node.parentNode.removeChild(node);
21898             // clean up silly Windows -- stuff?
21899             return; 
21900         }
21901         var lcname = node.tagName.toLowerCase();
21902         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21903         // whitelist of tags..
21904         
21905         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21906             // remove node.
21907             node.parentNode.removeChild(node);
21908             return;
21909             
21910         }
21911         
21912         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21913         
21914         // spans with no attributes - just remove them..
21915         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21916             remove_keep_children = true;
21917         }
21918         
21919         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21920         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21921         
21922         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21923         //    remove_keep_children = true;
21924         //}
21925         
21926         if (remove_keep_children) {
21927             this.cleanUpChildren(node);
21928             // inserts everything just before this node...
21929             while (node.childNodes.length) {
21930                 var cn = node.childNodes[0];
21931                 node.removeChild(cn);
21932                 node.parentNode.insertBefore(cn, node);
21933             }
21934             node.parentNode.removeChild(node);
21935             return;
21936         }
21937         
21938         if (!node.attributes || !node.attributes.length) {
21939             
21940           
21941             
21942             
21943             this.cleanUpChildren(node);
21944             return;
21945         }
21946         
21947         function cleanAttr(n,v)
21948         {
21949             
21950             if (v.match(/^\./) || v.match(/^\//)) {
21951                 return;
21952             }
21953             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21954                 return;
21955             }
21956             if (v.match(/^#/)) {
21957                 return;
21958             }
21959 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21960             node.removeAttribute(n);
21961             
21962         }
21963         
21964         var cwhite = this.cwhite;
21965         var cblack = this.cblack;
21966             
21967         function cleanStyle(n,v)
21968         {
21969             if (v.match(/expression/)) { //XSS?? should we even bother..
21970                 node.removeAttribute(n);
21971                 return;
21972             }
21973             
21974             var parts = v.split(/;/);
21975             var clean = [];
21976             
21977             Roo.each(parts, function(p) {
21978                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21979                 if (!p.length) {
21980                     return true;
21981                 }
21982                 var l = p.split(':').shift().replace(/\s+/g,'');
21983                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21984                 
21985                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21986 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21987                     //node.removeAttribute(n);
21988                     return true;
21989                 }
21990                 //Roo.log()
21991                 // only allow 'c whitelisted system attributes'
21992                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21993 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21994                     //node.removeAttribute(n);
21995                     return true;
21996                 }
21997                 
21998                 
21999                  
22000                 
22001                 clean.push(p);
22002                 return true;
22003             });
22004             if (clean.length) { 
22005                 node.setAttribute(n, clean.join(';'));
22006             } else {
22007                 node.removeAttribute(n);
22008             }
22009             
22010         }
22011         
22012         
22013         for (var i = node.attributes.length-1; i > -1 ; i--) {
22014             var a = node.attributes[i];
22015             //console.log(a);
22016             
22017             if (a.name.toLowerCase().substr(0,2)=='on')  {
22018                 node.removeAttribute(a.name);
22019                 continue;
22020             }
22021             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22022                 node.removeAttribute(a.name);
22023                 continue;
22024             }
22025             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22026                 cleanAttr(a.name,a.value); // fixme..
22027                 continue;
22028             }
22029             if (a.name == 'style') {
22030                 cleanStyle(a.name,a.value);
22031                 continue;
22032             }
22033             /// clean up MS crap..
22034             // tecnically this should be a list of valid class'es..
22035             
22036             
22037             if (a.name == 'class') {
22038                 if (a.value.match(/^Mso/)) {
22039                     node.removeAttribute('class');
22040                 }
22041                 
22042                 if (a.value.match(/^body$/)) {
22043                     node.removeAttribute('class');
22044                 }
22045                 continue;
22046             }
22047             
22048             // style cleanup!?
22049             // class cleanup?
22050             
22051         }
22052         
22053         
22054         this.cleanUpChildren(node);
22055         
22056         
22057     },
22058     
22059     /**
22060      * Clean up MS wordisms...
22061      */
22062     cleanWord : function(node)
22063     {
22064         if (!node) {
22065             this.cleanWord(this.doc.body);
22066             return;
22067         }
22068         
22069         if(
22070                 node.nodeName == 'SPAN' &&
22071                 !node.hasAttributes() &&
22072                 node.childNodes.length == 1 &&
22073                 node.firstChild.nodeName == "#text"  
22074         ) {
22075             var textNode = node.firstChild;
22076             node.removeChild(textNode);
22077             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22078                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22079             }
22080             node.parentNode.insertBefore(textNode, node);
22081             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22082                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22083             }
22084             node.parentNode.removeChild(node);
22085         }
22086         
22087         if (node.nodeName == "#text") {
22088             // clean up silly Windows -- stuff?
22089             return; 
22090         }
22091         if (node.nodeName == "#comment") {
22092             node.parentNode.removeChild(node);
22093             // clean up silly Windows -- stuff?
22094             return; 
22095         }
22096         
22097         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22098             node.parentNode.removeChild(node);
22099             return;
22100         }
22101         //Roo.log(node.tagName);
22102         // remove - but keep children..
22103         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22104             //Roo.log('-- removed');
22105             while (node.childNodes.length) {
22106                 var cn = node.childNodes[0];
22107                 node.removeChild(cn);
22108                 node.parentNode.insertBefore(cn, node);
22109                 // move node to parent - and clean it..
22110                 this.cleanWord(cn);
22111             }
22112             node.parentNode.removeChild(node);
22113             /// no need to iterate chidlren = it's got none..
22114             //this.iterateChildren(node, this.cleanWord);
22115             return;
22116         }
22117         // clean styles
22118         if (node.className.length) {
22119             
22120             var cn = node.className.split(/\W+/);
22121             var cna = [];
22122             Roo.each(cn, function(cls) {
22123                 if (cls.match(/Mso[a-zA-Z]+/)) {
22124                     return;
22125                 }
22126                 cna.push(cls);
22127             });
22128             node.className = cna.length ? cna.join(' ') : '';
22129             if (!cna.length) {
22130                 node.removeAttribute("class");
22131             }
22132         }
22133         
22134         if (node.hasAttribute("lang")) {
22135             node.removeAttribute("lang");
22136         }
22137         
22138         if (node.hasAttribute("style")) {
22139             
22140             var styles = node.getAttribute("style").split(";");
22141             var nstyle = [];
22142             Roo.each(styles, function(s) {
22143                 if (!s.match(/:/)) {
22144                     return;
22145                 }
22146                 var kv = s.split(":");
22147                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22148                     return;
22149                 }
22150                 // what ever is left... we allow.
22151                 nstyle.push(s);
22152             });
22153             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22154             if (!nstyle.length) {
22155                 node.removeAttribute('style');
22156             }
22157         }
22158         this.iterateChildren(node, this.cleanWord);
22159         
22160         
22161         
22162     },
22163     /**
22164      * iterateChildren of a Node, calling fn each time, using this as the scole..
22165      * @param {DomNode} node node to iterate children of.
22166      * @param {Function} fn method of this class to call on each item.
22167      */
22168     iterateChildren : function(node, fn)
22169     {
22170         if (!node.childNodes.length) {
22171                 return;
22172         }
22173         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22174            fn.call(this, node.childNodes[i])
22175         }
22176     },
22177     
22178     
22179     /**
22180      * cleanTableWidths.
22181      *
22182      * Quite often pasting from word etc.. results in tables with column and widths.
22183      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22184      *
22185      */
22186     cleanTableWidths : function(node)
22187     {
22188          
22189          
22190         if (!node) {
22191             this.cleanTableWidths(this.doc.body);
22192             return;
22193         }
22194         
22195         // ignore list...
22196         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22197             return; 
22198         }
22199         Roo.log(node.tagName);
22200         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22201             this.iterateChildren(node, this.cleanTableWidths);
22202             return;
22203         }
22204         if (node.hasAttribute('width')) {
22205             node.removeAttribute('width');
22206         }
22207         
22208          
22209         if (node.hasAttribute("style")) {
22210             // pretty basic...
22211             
22212             var styles = node.getAttribute("style").split(";");
22213             var nstyle = [];
22214             Roo.each(styles, function(s) {
22215                 if (!s.match(/:/)) {
22216                     return;
22217                 }
22218                 var kv = s.split(":");
22219                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22220                     return;
22221                 }
22222                 // what ever is left... we allow.
22223                 nstyle.push(s);
22224             });
22225             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22226             if (!nstyle.length) {
22227                 node.removeAttribute('style');
22228             }
22229         }
22230         
22231         this.iterateChildren(node, this.cleanTableWidths);
22232         
22233         
22234     },
22235     
22236     
22237     
22238     
22239     domToHTML : function(currentElement, depth, nopadtext) {
22240         
22241         depth = depth || 0;
22242         nopadtext = nopadtext || false;
22243     
22244         if (!currentElement) {
22245             return this.domToHTML(this.doc.body);
22246         }
22247         
22248         //Roo.log(currentElement);
22249         var j;
22250         var allText = false;
22251         var nodeName = currentElement.nodeName;
22252         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22253         
22254         if  (nodeName == '#text') {
22255             
22256             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22257         }
22258         
22259         
22260         var ret = '';
22261         if (nodeName != 'BODY') {
22262              
22263             var i = 0;
22264             // Prints the node tagName, such as <A>, <IMG>, etc
22265             if (tagName) {
22266                 var attr = [];
22267                 for(i = 0; i < currentElement.attributes.length;i++) {
22268                     // quoting?
22269                     var aname = currentElement.attributes.item(i).name;
22270                     if (!currentElement.attributes.item(i).value.length) {
22271                         continue;
22272                     }
22273                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22274                 }
22275                 
22276                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22277             } 
22278             else {
22279                 
22280                 // eack
22281             }
22282         } else {
22283             tagName = false;
22284         }
22285         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22286             return ret;
22287         }
22288         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22289             nopadtext = true;
22290         }
22291         
22292         
22293         // Traverse the tree
22294         i = 0;
22295         var currentElementChild = currentElement.childNodes.item(i);
22296         var allText = true;
22297         var innerHTML  = '';
22298         lastnode = '';
22299         while (currentElementChild) {
22300             // Formatting code (indent the tree so it looks nice on the screen)
22301             var nopad = nopadtext;
22302             if (lastnode == 'SPAN') {
22303                 nopad  = true;
22304             }
22305             // text
22306             if  (currentElementChild.nodeName == '#text') {
22307                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22308                 toadd = nopadtext ? toadd : toadd.trim();
22309                 if (!nopad && toadd.length > 80) {
22310                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22311                 }
22312                 innerHTML  += toadd;
22313                 
22314                 i++;
22315                 currentElementChild = currentElement.childNodes.item(i);
22316                 lastNode = '';
22317                 continue;
22318             }
22319             allText = false;
22320             
22321             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22322                 
22323             // Recursively traverse the tree structure of the child node
22324             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22325             lastnode = currentElementChild.nodeName;
22326             i++;
22327             currentElementChild=currentElement.childNodes.item(i);
22328         }
22329         
22330         ret += innerHTML;
22331         
22332         if (!allText) {
22333                 // The remaining code is mostly for formatting the tree
22334             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22335         }
22336         
22337         
22338         if (tagName) {
22339             ret+= "</"+tagName+">";
22340         }
22341         return ret;
22342         
22343     },
22344         
22345     applyBlacklists : function()
22346     {
22347         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22348         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22349         
22350         this.white = [];
22351         this.black = [];
22352         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22353             if (b.indexOf(tag) > -1) {
22354                 return;
22355             }
22356             this.white.push(tag);
22357             
22358         }, this);
22359         
22360         Roo.each(w, function(tag) {
22361             if (b.indexOf(tag) > -1) {
22362                 return;
22363             }
22364             if (this.white.indexOf(tag) > -1) {
22365                 return;
22366             }
22367             this.white.push(tag);
22368             
22369         }, this);
22370         
22371         
22372         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22373             if (w.indexOf(tag) > -1) {
22374                 return;
22375             }
22376             this.black.push(tag);
22377             
22378         }, this);
22379         
22380         Roo.each(b, function(tag) {
22381             if (w.indexOf(tag) > -1) {
22382                 return;
22383             }
22384             if (this.black.indexOf(tag) > -1) {
22385                 return;
22386             }
22387             this.black.push(tag);
22388             
22389         }, this);
22390         
22391         
22392         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22393         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22394         
22395         this.cwhite = [];
22396         this.cblack = [];
22397         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22398             if (b.indexOf(tag) > -1) {
22399                 return;
22400             }
22401             this.cwhite.push(tag);
22402             
22403         }, this);
22404         
22405         Roo.each(w, function(tag) {
22406             if (b.indexOf(tag) > -1) {
22407                 return;
22408             }
22409             if (this.cwhite.indexOf(tag) > -1) {
22410                 return;
22411             }
22412             this.cwhite.push(tag);
22413             
22414         }, this);
22415         
22416         
22417         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22418             if (w.indexOf(tag) > -1) {
22419                 return;
22420             }
22421             this.cblack.push(tag);
22422             
22423         }, this);
22424         
22425         Roo.each(b, function(tag) {
22426             if (w.indexOf(tag) > -1) {
22427                 return;
22428             }
22429             if (this.cblack.indexOf(tag) > -1) {
22430                 return;
22431             }
22432             this.cblack.push(tag);
22433             
22434         }, this);
22435     },
22436     
22437     setStylesheets : function(stylesheets)
22438     {
22439         if(typeof(stylesheets) == 'string'){
22440             Roo.get(this.iframe.contentDocument.head).createChild({
22441                 tag : 'link',
22442                 rel : 'stylesheet',
22443                 type : 'text/css',
22444                 href : stylesheets
22445             });
22446             
22447             return;
22448         }
22449         var _this = this;
22450      
22451         Roo.each(stylesheets, function(s) {
22452             if(!s.length){
22453                 return;
22454             }
22455             
22456             Roo.get(_this.iframe.contentDocument.head).createChild({
22457                 tag : 'link',
22458                 rel : 'stylesheet',
22459                 type : 'text/css',
22460                 href : s
22461             });
22462         });
22463
22464         
22465     },
22466     
22467     removeStylesheets : function()
22468     {
22469         var _this = this;
22470         
22471         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22472             s.remove();
22473         });
22474     },
22475     
22476     setStyle : function(style)
22477     {
22478         Roo.get(this.iframe.contentDocument.head).createChild({
22479             tag : 'style',
22480             type : 'text/css',
22481             html : style
22482         });
22483
22484         return;
22485     }
22486     
22487     // hide stuff that is not compatible
22488     /**
22489      * @event blur
22490      * @hide
22491      */
22492     /**
22493      * @event change
22494      * @hide
22495      */
22496     /**
22497      * @event focus
22498      * @hide
22499      */
22500     /**
22501      * @event specialkey
22502      * @hide
22503      */
22504     /**
22505      * @cfg {String} fieldClass @hide
22506      */
22507     /**
22508      * @cfg {String} focusClass @hide
22509      */
22510     /**
22511      * @cfg {String} autoCreate @hide
22512      */
22513     /**
22514      * @cfg {String} inputType @hide
22515      */
22516     /**
22517      * @cfg {String} invalidClass @hide
22518      */
22519     /**
22520      * @cfg {String} invalidText @hide
22521      */
22522     /**
22523      * @cfg {String} msgFx @hide
22524      */
22525     /**
22526      * @cfg {String} validateOnBlur @hide
22527      */
22528 });
22529
22530 Roo.HtmlEditorCore.white = [
22531         'area', 'br', 'img', 'input', 'hr', 'wbr',
22532         
22533        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22534        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22535        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22536        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22537        'table',   'ul',         'xmp', 
22538        
22539        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22540       'thead',   'tr', 
22541      
22542       'dir', 'menu', 'ol', 'ul', 'dl',
22543        
22544       'embed',  'object'
22545 ];
22546
22547
22548 Roo.HtmlEditorCore.black = [
22549     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22550         'applet', // 
22551         'base',   'basefont', 'bgsound', 'blink',  'body', 
22552         'frame',  'frameset', 'head',    'html',   'ilayer', 
22553         'iframe', 'layer',  'link',     'meta',    'object',   
22554         'script', 'style' ,'title',  'xml' // clean later..
22555 ];
22556 Roo.HtmlEditorCore.clean = [
22557     'script', 'style', 'title', 'xml'
22558 ];
22559 Roo.HtmlEditorCore.remove = [
22560     'font'
22561 ];
22562 // attributes..
22563
22564 Roo.HtmlEditorCore.ablack = [
22565     'on'
22566 ];
22567     
22568 Roo.HtmlEditorCore.aclean = [ 
22569     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22570 ];
22571
22572 // protocols..
22573 Roo.HtmlEditorCore.pwhite= [
22574         'http',  'https',  'mailto'
22575 ];
22576
22577 // white listed style attributes.
22578 Roo.HtmlEditorCore.cwhite= [
22579       //  'text-align', /// default is to allow most things..
22580       
22581          
22582 //        'font-size'//??
22583 ];
22584
22585 // black listed style attributes.
22586 Roo.HtmlEditorCore.cblack= [
22587       //  'font-size' -- this can be set by the project 
22588 ];
22589
22590
22591 Roo.HtmlEditorCore.swapCodes   =[ 
22592     [    8211, "--" ], 
22593     [    8212, "--" ], 
22594     [    8216,  "'" ],  
22595     [    8217, "'" ],  
22596     [    8220, '"' ],  
22597     [    8221, '"' ],  
22598     [    8226, "*" ],  
22599     [    8230, "..." ]
22600 ]; 
22601
22602     //<script type="text/javascript">
22603
22604 /*
22605  * Ext JS Library 1.1.1
22606  * Copyright(c) 2006-2007, Ext JS, LLC.
22607  * Licence LGPL
22608  * 
22609  */
22610  
22611  
22612 Roo.form.HtmlEditor = function(config){
22613     
22614     
22615     
22616     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22617     
22618     if (!this.toolbars) {
22619         this.toolbars = [];
22620     }
22621     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22622     
22623     
22624 };
22625
22626 /**
22627  * @class Roo.form.HtmlEditor
22628  * @extends Roo.form.Field
22629  * Provides a lightweight HTML Editor component.
22630  *
22631  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22632  * 
22633  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22634  * supported by this editor.</b><br/><br/>
22635  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22636  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22637  */
22638 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22639     /**
22640      * @cfg {Boolean} clearUp
22641      */
22642     clearUp : true,
22643       /**
22644      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22645      */
22646     toolbars : false,
22647    
22648      /**
22649      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22650      *                        Roo.resizable.
22651      */
22652     resizable : false,
22653      /**
22654      * @cfg {Number} height (in pixels)
22655      */   
22656     height: 300,
22657    /**
22658      * @cfg {Number} width (in pixels)
22659      */   
22660     width: 500,
22661     
22662     /**
22663      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22664      * 
22665      */
22666     stylesheets: false,
22667     
22668     
22669      /**
22670      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22671      * 
22672      */
22673     cblack: false,
22674     /**
22675      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22676      * 
22677      */
22678     cwhite: false,
22679     
22680      /**
22681      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22682      * 
22683      */
22684     black: false,
22685     /**
22686      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22687      * 
22688      */
22689     white: false,
22690     
22691     // id of frame..
22692     frameId: false,
22693     
22694     // private properties
22695     validationEvent : false,
22696     deferHeight: true,
22697     initialized : false,
22698     activated : false,
22699     
22700     onFocus : Roo.emptyFn,
22701     iframePad:3,
22702     hideMode:'offsets',
22703     
22704     actionMode : 'container', // defaults to hiding it...
22705     
22706     defaultAutoCreate : { // modified by initCompnoent..
22707         tag: "textarea",
22708         style:"width:500px;height:300px;",
22709         autocomplete: "new-password"
22710     },
22711
22712     // private
22713     initComponent : function(){
22714         this.addEvents({
22715             /**
22716              * @event initialize
22717              * Fires when the editor is fully initialized (including the iframe)
22718              * @param {HtmlEditor} this
22719              */
22720             initialize: true,
22721             /**
22722              * @event activate
22723              * Fires when the editor is first receives the focus. Any insertion must wait
22724              * until after this event.
22725              * @param {HtmlEditor} this
22726              */
22727             activate: true,
22728              /**
22729              * @event beforesync
22730              * Fires before the textarea is updated with content from the editor iframe. Return false
22731              * to cancel the sync.
22732              * @param {HtmlEditor} this
22733              * @param {String} html
22734              */
22735             beforesync: true,
22736              /**
22737              * @event beforepush
22738              * Fires before the iframe editor is updated with content from the textarea. Return false
22739              * to cancel the push.
22740              * @param {HtmlEditor} this
22741              * @param {String} html
22742              */
22743             beforepush: true,
22744              /**
22745              * @event sync
22746              * Fires when the textarea is updated with content from the editor iframe.
22747              * @param {HtmlEditor} this
22748              * @param {String} html
22749              */
22750             sync: true,
22751              /**
22752              * @event push
22753              * Fires when the iframe editor is updated with content from the textarea.
22754              * @param {HtmlEditor} this
22755              * @param {String} html
22756              */
22757             push: true,
22758              /**
22759              * @event editmodechange
22760              * Fires when the editor switches edit modes
22761              * @param {HtmlEditor} this
22762              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22763              */
22764             editmodechange: true,
22765             /**
22766              * @event editorevent
22767              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22768              * @param {HtmlEditor} this
22769              */
22770             editorevent: true,
22771             /**
22772              * @event firstfocus
22773              * Fires when on first focus - needed by toolbars..
22774              * @param {HtmlEditor} this
22775              */
22776             firstfocus: true,
22777             /**
22778              * @event autosave
22779              * Auto save the htmlEditor value as a file into Events
22780              * @param {HtmlEditor} this
22781              */
22782             autosave: true,
22783             /**
22784              * @event savedpreview
22785              * preview the saved version of htmlEditor
22786              * @param {HtmlEditor} this
22787              */
22788             savedpreview: true,
22789             
22790             /**
22791             * @event stylesheetsclick
22792             * Fires when press the Sytlesheets button
22793             * @param {Roo.HtmlEditorCore} this
22794             */
22795             stylesheetsclick: true
22796         });
22797         this.defaultAutoCreate =  {
22798             tag: "textarea",
22799             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22800             autocomplete: "new-password"
22801         };
22802     },
22803
22804     /**
22805      * Protected method that will not generally be called directly. It
22806      * is called when the editor creates its toolbar. Override this method if you need to
22807      * add custom toolbar buttons.
22808      * @param {HtmlEditor} editor
22809      */
22810     createToolbar : function(editor){
22811         Roo.log("create toolbars");
22812         if (!editor.toolbars || !editor.toolbars.length) {
22813             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22814         }
22815         
22816         for (var i =0 ; i < editor.toolbars.length;i++) {
22817             editor.toolbars[i] = Roo.factory(
22818                     typeof(editor.toolbars[i]) == 'string' ?
22819                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22820                 Roo.form.HtmlEditor);
22821             editor.toolbars[i].init(editor);
22822         }
22823          
22824         
22825     },
22826
22827      
22828     // private
22829     onRender : function(ct, position)
22830     {
22831         var _t = this;
22832         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22833         
22834         this.wrap = this.el.wrap({
22835             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22836         });
22837         
22838         this.editorcore.onRender(ct, position);
22839          
22840         if (this.resizable) {
22841             this.resizeEl = new Roo.Resizable(this.wrap, {
22842                 pinned : true,
22843                 wrap: true,
22844                 dynamic : true,
22845                 minHeight : this.height,
22846                 height: this.height,
22847                 handles : this.resizable,
22848                 width: this.width,
22849                 listeners : {
22850                     resize : function(r, w, h) {
22851                         _t.onResize(w,h); // -something
22852                     }
22853                 }
22854             });
22855             
22856         }
22857         this.createToolbar(this);
22858        
22859         
22860         if(!this.width){
22861             this.setSize(this.wrap.getSize());
22862         }
22863         if (this.resizeEl) {
22864             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22865             // should trigger onReize..
22866         }
22867         
22868         this.keyNav = new Roo.KeyNav(this.el, {
22869             
22870             "tab" : function(e){
22871                 e.preventDefault();
22872                 
22873                 var value = this.getValue();
22874                 
22875                 var start = this.el.dom.selectionStart;
22876                 var end = this.el.dom.selectionEnd;
22877                 
22878                 if(!e.shiftKey){
22879                     
22880                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22881                     this.el.dom.setSelectionRange(end + 1, end + 1);
22882                     return;
22883                 }
22884                 
22885                 var f = value.substring(0, start).split("\t");
22886                 
22887                 if(f.pop().length != 0){
22888                     return;
22889                 }
22890                 
22891                 this.setValue(f.join("\t") + value.substring(end));
22892                 this.el.dom.setSelectionRange(start - 1, start - 1);
22893                 
22894             },
22895             
22896             "home" : function(e){
22897                 e.preventDefault();
22898                 
22899                 var curr = this.el.dom.selectionStart;
22900                 var lines = this.getValue().split("\n");
22901                 
22902                 if(!lines.length){
22903                     return;
22904                 }
22905                 
22906                 if(e.ctrlKey){
22907                     this.el.dom.setSelectionRange(0, 0);
22908                     return;
22909                 }
22910                 
22911                 var pos = 0;
22912                 
22913                 for (var i = 0; i < lines.length;i++) {
22914                     pos += lines[i].length;
22915                     
22916                     if(i != 0){
22917                         pos += 1;
22918                     }
22919                     
22920                     if(pos < curr){
22921                         continue;
22922                     }
22923                     
22924                     pos -= lines[i].length;
22925                     
22926                     break;
22927                 }
22928                 
22929                 if(!e.shiftKey){
22930                     this.el.dom.setSelectionRange(pos, pos);
22931                     return;
22932                 }
22933                 
22934                 this.el.dom.selectionStart = pos;
22935                 this.el.dom.selectionEnd = curr;
22936             },
22937             
22938             "end" : function(e){
22939                 e.preventDefault();
22940                 
22941                 var curr = this.el.dom.selectionStart;
22942                 var lines = this.getValue().split("\n");
22943                 
22944                 if(!lines.length){
22945                     return;
22946                 }
22947                 
22948                 if(e.ctrlKey){
22949                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22950                     return;
22951                 }
22952                 
22953                 var pos = 0;
22954                 
22955                 for (var i = 0; i < lines.length;i++) {
22956                     
22957                     pos += lines[i].length;
22958                     
22959                     if(i != 0){
22960                         pos += 1;
22961                     }
22962                     
22963                     if(pos < curr){
22964                         continue;
22965                     }
22966                     
22967                     break;
22968                 }
22969                 
22970                 if(!e.shiftKey){
22971                     this.el.dom.setSelectionRange(pos, pos);
22972                     return;
22973                 }
22974                 
22975                 this.el.dom.selectionStart = curr;
22976                 this.el.dom.selectionEnd = pos;
22977             },
22978
22979             scope : this,
22980
22981             doRelay : function(foo, bar, hname){
22982                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22983             },
22984
22985             forceKeyDown: true
22986         });
22987         
22988 //        if(this.autosave && this.w){
22989 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22990 //        }
22991     },
22992
22993     // private
22994     onResize : function(w, h)
22995     {
22996         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22997         var ew = false;
22998         var eh = false;
22999         
23000         if(this.el ){
23001             if(typeof w == 'number'){
23002                 var aw = w - this.wrap.getFrameWidth('lr');
23003                 this.el.setWidth(this.adjustWidth('textarea', aw));
23004                 ew = aw;
23005             }
23006             if(typeof h == 'number'){
23007                 var tbh = 0;
23008                 for (var i =0; i < this.toolbars.length;i++) {
23009                     // fixme - ask toolbars for heights?
23010                     tbh += this.toolbars[i].tb.el.getHeight();
23011                     if (this.toolbars[i].footer) {
23012                         tbh += this.toolbars[i].footer.el.getHeight();
23013                     }
23014                 }
23015                 
23016                 
23017                 
23018                 
23019                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23020                 ah -= 5; // knock a few pixes off for look..
23021 //                Roo.log(ah);
23022                 this.el.setHeight(this.adjustWidth('textarea', ah));
23023                 var eh = ah;
23024             }
23025         }
23026         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23027         this.editorcore.onResize(ew,eh);
23028         
23029     },
23030
23031     /**
23032      * Toggles the editor between standard and source edit mode.
23033      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23034      */
23035     toggleSourceEdit : function(sourceEditMode)
23036     {
23037         this.editorcore.toggleSourceEdit(sourceEditMode);
23038         
23039         if(this.editorcore.sourceEditMode){
23040             Roo.log('editor - showing textarea');
23041             
23042 //            Roo.log('in');
23043 //            Roo.log(this.syncValue());
23044             this.editorcore.syncValue();
23045             this.el.removeClass('x-hidden');
23046             this.el.dom.removeAttribute('tabIndex');
23047             this.el.focus();
23048             
23049             for (var i = 0; i < this.toolbars.length; i++) {
23050                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23051                     this.toolbars[i].tb.hide();
23052                     this.toolbars[i].footer.hide();
23053                 }
23054             }
23055             
23056         }else{
23057             Roo.log('editor - hiding textarea');
23058 //            Roo.log('out')
23059 //            Roo.log(this.pushValue()); 
23060             this.editorcore.pushValue();
23061             
23062             this.el.addClass('x-hidden');
23063             this.el.dom.setAttribute('tabIndex', -1);
23064             
23065             for (var i = 0; i < this.toolbars.length; i++) {
23066                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23067                     this.toolbars[i].tb.show();
23068                     this.toolbars[i].footer.show();
23069                 }
23070             }
23071             
23072             //this.deferFocus();
23073         }
23074         
23075         this.setSize(this.wrap.getSize());
23076         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23077         
23078         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23079     },
23080  
23081     // private (for BoxComponent)
23082     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23083
23084     // private (for BoxComponent)
23085     getResizeEl : function(){
23086         return this.wrap;
23087     },
23088
23089     // private (for BoxComponent)
23090     getPositionEl : function(){
23091         return this.wrap;
23092     },
23093
23094     // private
23095     initEvents : function(){
23096         this.originalValue = this.getValue();
23097     },
23098
23099     /**
23100      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23101      * @method
23102      */
23103     markInvalid : Roo.emptyFn,
23104     /**
23105      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23106      * @method
23107      */
23108     clearInvalid : Roo.emptyFn,
23109
23110     setValue : function(v){
23111         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23112         this.editorcore.pushValue();
23113     },
23114
23115      
23116     // private
23117     deferFocus : function(){
23118         this.focus.defer(10, this);
23119     },
23120
23121     // doc'ed in Field
23122     focus : function(){
23123         this.editorcore.focus();
23124         
23125     },
23126       
23127
23128     // private
23129     onDestroy : function(){
23130         
23131         
23132         
23133         if(this.rendered){
23134             
23135             for (var i =0; i < this.toolbars.length;i++) {
23136                 // fixme - ask toolbars for heights?
23137                 this.toolbars[i].onDestroy();
23138             }
23139             
23140             this.wrap.dom.innerHTML = '';
23141             this.wrap.remove();
23142         }
23143     },
23144
23145     // private
23146     onFirstFocus : function(){
23147         //Roo.log("onFirstFocus");
23148         this.editorcore.onFirstFocus();
23149          for (var i =0; i < this.toolbars.length;i++) {
23150             this.toolbars[i].onFirstFocus();
23151         }
23152         
23153     },
23154     
23155     // private
23156     syncValue : function()
23157     {
23158         this.editorcore.syncValue();
23159     },
23160     
23161     pushValue : function()
23162     {
23163         this.editorcore.pushValue();
23164     },
23165     
23166     setStylesheets : function(stylesheets)
23167     {
23168         this.editorcore.setStylesheets(stylesheets);
23169     },
23170     
23171     removeStylesheets : function()
23172     {
23173         this.editorcore.removeStylesheets();
23174     }
23175      
23176     
23177     // hide stuff that is not compatible
23178     /**
23179      * @event blur
23180      * @hide
23181      */
23182     /**
23183      * @event change
23184      * @hide
23185      */
23186     /**
23187      * @event focus
23188      * @hide
23189      */
23190     /**
23191      * @event specialkey
23192      * @hide
23193      */
23194     /**
23195      * @cfg {String} fieldClass @hide
23196      */
23197     /**
23198      * @cfg {String} focusClass @hide
23199      */
23200     /**
23201      * @cfg {String} autoCreate @hide
23202      */
23203     /**
23204      * @cfg {String} inputType @hide
23205      */
23206     /**
23207      * @cfg {String} invalidClass @hide
23208      */
23209     /**
23210      * @cfg {String} invalidText @hide
23211      */
23212     /**
23213      * @cfg {String} msgFx @hide
23214      */
23215     /**
23216      * @cfg {String} validateOnBlur @hide
23217      */
23218 });
23219  
23220     // <script type="text/javascript">
23221 /*
23222  * Based on
23223  * Ext JS Library 1.1.1
23224  * Copyright(c) 2006-2007, Ext JS, LLC.
23225  *  
23226  
23227  */
23228
23229 /**
23230  * @class Roo.form.HtmlEditorToolbar1
23231  * Basic Toolbar
23232  * 
23233  * Usage:
23234  *
23235  new Roo.form.HtmlEditor({
23236     ....
23237     toolbars : [
23238         new Roo.form.HtmlEditorToolbar1({
23239             disable : { fonts: 1 , format: 1, ..., ... , ...],
23240             btns : [ .... ]
23241         })
23242     }
23243      
23244  * 
23245  * @cfg {Object} disable List of elements to disable..
23246  * @cfg {Array} btns List of additional buttons.
23247  * 
23248  * 
23249  * NEEDS Extra CSS? 
23250  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23251  */
23252  
23253 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23254 {
23255     
23256     Roo.apply(this, config);
23257     
23258     // default disabled, based on 'good practice'..
23259     this.disable = this.disable || {};
23260     Roo.applyIf(this.disable, {
23261         fontSize : true,
23262         colors : true,
23263         specialElements : true
23264     });
23265     
23266     
23267     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23268     // dont call parent... till later.
23269 }
23270
23271 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23272     
23273     tb: false,
23274     
23275     rendered: false,
23276     
23277     editor : false,
23278     editorcore : false,
23279     /**
23280      * @cfg {Object} disable  List of toolbar elements to disable
23281          
23282      */
23283     disable : false,
23284     
23285     
23286      /**
23287      * @cfg {String} createLinkText The default text for the create link prompt
23288      */
23289     createLinkText : 'Please enter the URL for the link:',
23290     /**
23291      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23292      */
23293     defaultLinkValue : 'http:/'+'/',
23294    
23295     
23296       /**
23297      * @cfg {Array} fontFamilies An array of available font families
23298      */
23299     fontFamilies : [
23300         'Arial',
23301         'Courier New',
23302         'Tahoma',
23303         'Times New Roman',
23304         'Verdana'
23305     ],
23306     
23307     specialChars : [
23308            "&#169;",
23309           "&#174;",     
23310           "&#8482;",    
23311           "&#163;" ,    
23312          // "&#8212;",    
23313           "&#8230;",    
23314           "&#247;" ,    
23315         //  "&#225;" ,     ?? a acute?
23316            "&#8364;"    , //Euro
23317        //   "&#8220;"    ,
23318         //  "&#8221;"    ,
23319         //  "&#8226;"    ,
23320           "&#176;"  //   , // degrees
23321
23322          // "&#233;"     , // e ecute
23323          // "&#250;"     , // u ecute?
23324     ],
23325     
23326     specialElements : [
23327         {
23328             text: "Insert Table",
23329             xtype: 'MenuItem',
23330             xns : Roo.Menu,
23331             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23332                 
23333         },
23334         {    
23335             text: "Insert Image",
23336             xtype: 'MenuItem',
23337             xns : Roo.Menu,
23338             ihtml : '<img src="about:blank"/>'
23339             
23340         }
23341         
23342          
23343     ],
23344     
23345     
23346     inputElements : [ 
23347             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23348             "input:submit", "input:button", "select", "textarea", "label" ],
23349     formats : [
23350         ["p"] ,  
23351         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23352         ["pre"],[ "code"], 
23353         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23354         ['div'],['span'],
23355         ['sup'],['sub']
23356     ],
23357     
23358     cleanStyles : [
23359         "font-size"
23360     ],
23361      /**
23362      * @cfg {String} defaultFont default font to use.
23363      */
23364     defaultFont: 'tahoma',
23365    
23366     fontSelect : false,
23367     
23368     
23369     formatCombo : false,
23370     
23371     init : function(editor)
23372     {
23373         this.editor = editor;
23374         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23375         var editorcore = this.editorcore;
23376         
23377         var _t = this;
23378         
23379         var fid = editorcore.frameId;
23380         var etb = this;
23381         function btn(id, toggle, handler){
23382             var xid = fid + '-'+ id ;
23383             return {
23384                 id : xid,
23385                 cmd : id,
23386                 cls : 'x-btn-icon x-edit-'+id,
23387                 enableToggle:toggle !== false,
23388                 scope: _t, // was editor...
23389                 handler:handler||_t.relayBtnCmd,
23390                 clickEvent:'mousedown',
23391                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23392                 tabIndex:-1
23393             };
23394         }
23395         
23396         
23397         
23398         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23399         this.tb = tb;
23400          // stop form submits
23401         tb.el.on('click', function(e){
23402             e.preventDefault(); // what does this do?
23403         });
23404
23405         if(!this.disable.font) { // && !Roo.isSafari){
23406             /* why no safari for fonts 
23407             editor.fontSelect = tb.el.createChild({
23408                 tag:'select',
23409                 tabIndex: -1,
23410                 cls:'x-font-select',
23411                 html: this.createFontOptions()
23412             });
23413             
23414             editor.fontSelect.on('change', function(){
23415                 var font = editor.fontSelect.dom.value;
23416                 editor.relayCmd('fontname', font);
23417                 editor.deferFocus();
23418             }, editor);
23419             
23420             tb.add(
23421                 editor.fontSelect.dom,
23422                 '-'
23423             );
23424             */
23425             
23426         };
23427         if(!this.disable.formats){
23428             this.formatCombo = new Roo.form.ComboBox({
23429                 store: new Roo.data.SimpleStore({
23430                     id : 'tag',
23431                     fields: ['tag'],
23432                     data : this.formats // from states.js
23433                 }),
23434                 blockFocus : true,
23435                 name : '',
23436                 //autoCreate : {tag: "div",  size: "20"},
23437                 displayField:'tag',
23438                 typeAhead: false,
23439                 mode: 'local',
23440                 editable : false,
23441                 triggerAction: 'all',
23442                 emptyText:'Add tag',
23443                 selectOnFocus:true,
23444                 width:135,
23445                 listeners : {
23446                     'select': function(c, r, i) {
23447                         editorcore.insertTag(r.get('tag'));
23448                         editor.focus();
23449                     }
23450                 }
23451
23452             });
23453             tb.addField(this.formatCombo);
23454             
23455         }
23456         
23457         if(!this.disable.format){
23458             tb.add(
23459                 btn('bold'),
23460                 btn('italic'),
23461                 btn('underline'),
23462                 btn('strikethrough')
23463             );
23464         };
23465         if(!this.disable.fontSize){
23466             tb.add(
23467                 '-',
23468                 
23469                 
23470                 btn('increasefontsize', false, editorcore.adjustFont),
23471                 btn('decreasefontsize', false, editorcore.adjustFont)
23472             );
23473         };
23474         
23475         
23476         if(!this.disable.colors){
23477             tb.add(
23478                 '-', {
23479                     id:editorcore.frameId +'-forecolor',
23480                     cls:'x-btn-icon x-edit-forecolor',
23481                     clickEvent:'mousedown',
23482                     tooltip: this.buttonTips['forecolor'] || undefined,
23483                     tabIndex:-1,
23484                     menu : new Roo.menu.ColorMenu({
23485                         allowReselect: true,
23486                         focus: Roo.emptyFn,
23487                         value:'000000',
23488                         plain:true,
23489                         selectHandler: function(cp, color){
23490                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23491                             editor.deferFocus();
23492                         },
23493                         scope: editorcore,
23494                         clickEvent:'mousedown'
23495                     })
23496                 }, {
23497                     id:editorcore.frameId +'backcolor',
23498                     cls:'x-btn-icon x-edit-backcolor',
23499                     clickEvent:'mousedown',
23500                     tooltip: this.buttonTips['backcolor'] || undefined,
23501                     tabIndex:-1,
23502                     menu : new Roo.menu.ColorMenu({
23503                         focus: Roo.emptyFn,
23504                         value:'FFFFFF',
23505                         plain:true,
23506                         allowReselect: true,
23507                         selectHandler: function(cp, color){
23508                             if(Roo.isGecko){
23509                                 editorcore.execCmd('useCSS', false);
23510                                 editorcore.execCmd('hilitecolor', color);
23511                                 editorcore.execCmd('useCSS', true);
23512                                 editor.deferFocus();
23513                             }else{
23514                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23515                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23516                                 editor.deferFocus();
23517                             }
23518                         },
23519                         scope:editorcore,
23520                         clickEvent:'mousedown'
23521                     })
23522                 }
23523             );
23524         };
23525         // now add all the items...
23526         
23527
23528         if(!this.disable.alignments){
23529             tb.add(
23530                 '-',
23531                 btn('justifyleft'),
23532                 btn('justifycenter'),
23533                 btn('justifyright')
23534             );
23535         };
23536
23537         //if(!Roo.isSafari){
23538             if(!this.disable.links){
23539                 tb.add(
23540                     '-',
23541                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23542                 );
23543             };
23544
23545             if(!this.disable.lists){
23546                 tb.add(
23547                     '-',
23548                     btn('insertorderedlist'),
23549                     btn('insertunorderedlist')
23550                 );
23551             }
23552             if(!this.disable.sourceEdit){
23553                 tb.add(
23554                     '-',
23555                     btn('sourceedit', true, function(btn){
23556                         this.toggleSourceEdit(btn.pressed);
23557                     })
23558                 );
23559             }
23560         //}
23561         
23562         var smenu = { };
23563         // special menu.. - needs to be tidied up..
23564         if (!this.disable.special) {
23565             smenu = {
23566                 text: "&#169;",
23567                 cls: 'x-edit-none',
23568                 
23569                 menu : {
23570                     items : []
23571                 }
23572             };
23573             for (var i =0; i < this.specialChars.length; i++) {
23574                 smenu.menu.items.push({
23575                     
23576                     html: this.specialChars[i],
23577                     handler: function(a,b) {
23578                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23579                         //editor.insertAtCursor(a.html);
23580                         
23581                     },
23582                     tabIndex:-1
23583                 });
23584             }
23585             
23586             
23587             tb.add(smenu);
23588             
23589             
23590         }
23591         
23592         var cmenu = { };
23593         if (!this.disable.cleanStyles) {
23594             cmenu = {
23595                 cls: 'x-btn-icon x-btn-clear',
23596                 
23597                 menu : {
23598                     items : []
23599                 }
23600             };
23601             for (var i =0; i < this.cleanStyles.length; i++) {
23602                 cmenu.menu.items.push({
23603                     actiontype : this.cleanStyles[i],
23604                     html: 'Remove ' + this.cleanStyles[i],
23605                     handler: function(a,b) {
23606 //                        Roo.log(a);
23607 //                        Roo.log(b);
23608                         var c = Roo.get(editorcore.doc.body);
23609                         c.select('[style]').each(function(s) {
23610                             s.dom.style.removeProperty(a.actiontype);
23611                         });
23612                         editorcore.syncValue();
23613                     },
23614                     tabIndex:-1
23615                 });
23616             }
23617              cmenu.menu.items.push({
23618                 actiontype : 'tablewidths',
23619                 html: 'Remove Table Widths',
23620                 handler: function(a,b) {
23621                     editorcore.cleanTableWidths();
23622                     editorcore.syncValue();
23623                 },
23624                 tabIndex:-1
23625             });
23626             cmenu.menu.items.push({
23627                 actiontype : 'word',
23628                 html: 'Remove MS Word Formating',
23629                 handler: function(a,b) {
23630                     editorcore.cleanWord();
23631                     editorcore.syncValue();
23632                 },
23633                 tabIndex:-1
23634             });
23635             
23636             cmenu.menu.items.push({
23637                 actiontype : 'all',
23638                 html: 'Remove All Styles',
23639                 handler: function(a,b) {
23640                     
23641                     var c = Roo.get(editorcore.doc.body);
23642                     c.select('[style]').each(function(s) {
23643                         s.dom.removeAttribute('style');
23644                     });
23645                     editorcore.syncValue();
23646                 },
23647                 tabIndex:-1
23648             });
23649             
23650             cmenu.menu.items.push({
23651                 actiontype : 'all',
23652                 html: 'Remove All CSS Classes',
23653                 handler: function(a,b) {
23654                     
23655                     var c = Roo.get(editorcore.doc.body);
23656                     c.select('[class]').each(function(s) {
23657                         s.dom.removeAttribute('class');
23658                     });
23659                     editorcore.cleanWord();
23660                     editorcore.syncValue();
23661                 },
23662                 tabIndex:-1
23663             });
23664             
23665              cmenu.menu.items.push({
23666                 actiontype : 'tidy',
23667                 html: 'Tidy HTML Source',
23668                 handler: function(a,b) {
23669                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23670                     editorcore.syncValue();
23671                 },
23672                 tabIndex:-1
23673             });
23674             
23675             
23676             tb.add(cmenu);
23677         }
23678          
23679         if (!this.disable.specialElements) {
23680             var semenu = {
23681                 text: "Other;",
23682                 cls: 'x-edit-none',
23683                 menu : {
23684                     items : []
23685                 }
23686             };
23687             for (var i =0; i < this.specialElements.length; i++) {
23688                 semenu.menu.items.push(
23689                     Roo.apply({ 
23690                         handler: function(a,b) {
23691                             editor.insertAtCursor(this.ihtml);
23692                         }
23693                     }, this.specialElements[i])
23694                 );
23695                     
23696             }
23697             
23698             tb.add(semenu);
23699             
23700             
23701         }
23702          
23703         
23704         if (this.btns) {
23705             for(var i =0; i< this.btns.length;i++) {
23706                 var b = Roo.factory(this.btns[i],Roo.form);
23707                 b.cls =  'x-edit-none';
23708                 
23709                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23710                     b.cls += ' x-init-enable';
23711                 }
23712                 
23713                 b.scope = editorcore;
23714                 tb.add(b);
23715             }
23716         
23717         }
23718         
23719         
23720         
23721         // disable everything...
23722         
23723         this.tb.items.each(function(item){
23724             
23725            if(
23726                 item.id != editorcore.frameId+ '-sourceedit' && 
23727                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23728             ){
23729                 
23730                 item.disable();
23731             }
23732         });
23733         this.rendered = true;
23734         
23735         // the all the btns;
23736         editor.on('editorevent', this.updateToolbar, this);
23737         // other toolbars need to implement this..
23738         //editor.on('editmodechange', this.updateToolbar, this);
23739     },
23740     
23741     
23742     relayBtnCmd : function(btn) {
23743         this.editorcore.relayCmd(btn.cmd);
23744     },
23745     // private used internally
23746     createLink : function(){
23747         Roo.log("create link?");
23748         var url = prompt(this.createLinkText, this.defaultLinkValue);
23749         if(url && url != 'http:/'+'/'){
23750             this.editorcore.relayCmd('createlink', url);
23751         }
23752     },
23753
23754     
23755     /**
23756      * Protected method that will not generally be called directly. It triggers
23757      * a toolbar update by reading the markup state of the current selection in the editor.
23758      */
23759     updateToolbar: function(){
23760
23761         if(!this.editorcore.activated){
23762             this.editor.onFirstFocus();
23763             return;
23764         }
23765
23766         var btns = this.tb.items.map, 
23767             doc = this.editorcore.doc,
23768             frameId = this.editorcore.frameId;
23769
23770         if(!this.disable.font && !Roo.isSafari){
23771             /*
23772             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23773             if(name != this.fontSelect.dom.value){
23774                 this.fontSelect.dom.value = name;
23775             }
23776             */
23777         }
23778         if(!this.disable.format){
23779             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23780             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23781             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23782             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23783         }
23784         if(!this.disable.alignments){
23785             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23786             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23787             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23788         }
23789         if(!Roo.isSafari && !this.disable.lists){
23790             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23791             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23792         }
23793         
23794         var ans = this.editorcore.getAllAncestors();
23795         if (this.formatCombo) {
23796             
23797             
23798             var store = this.formatCombo.store;
23799             this.formatCombo.setValue("");
23800             for (var i =0; i < ans.length;i++) {
23801                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23802                     // select it..
23803                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23804                     break;
23805                 }
23806             }
23807         }
23808         
23809         
23810         
23811         // hides menus... - so this cant be on a menu...
23812         Roo.menu.MenuMgr.hideAll();
23813
23814         //this.editorsyncValue();
23815     },
23816    
23817     
23818     createFontOptions : function(){
23819         var buf = [], fs = this.fontFamilies, ff, lc;
23820         
23821         
23822         
23823         for(var i = 0, len = fs.length; i< len; i++){
23824             ff = fs[i];
23825             lc = ff.toLowerCase();
23826             buf.push(
23827                 '<option value="',lc,'" style="font-family:',ff,';"',
23828                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23829                     ff,
23830                 '</option>'
23831             );
23832         }
23833         return buf.join('');
23834     },
23835     
23836     toggleSourceEdit : function(sourceEditMode){
23837         
23838         Roo.log("toolbar toogle");
23839         if(sourceEditMode === undefined){
23840             sourceEditMode = !this.sourceEditMode;
23841         }
23842         this.sourceEditMode = sourceEditMode === true;
23843         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23844         // just toggle the button?
23845         if(btn.pressed !== this.sourceEditMode){
23846             btn.toggle(this.sourceEditMode);
23847             return;
23848         }
23849         
23850         if(sourceEditMode){
23851             Roo.log("disabling buttons");
23852             this.tb.items.each(function(item){
23853                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23854                     item.disable();
23855                 }
23856             });
23857           
23858         }else{
23859             Roo.log("enabling buttons");
23860             if(this.editorcore.initialized){
23861                 this.tb.items.each(function(item){
23862                     item.enable();
23863                 });
23864             }
23865             
23866         }
23867         Roo.log("calling toggole on editor");
23868         // tell the editor that it's been pressed..
23869         this.editor.toggleSourceEdit(sourceEditMode);
23870        
23871     },
23872      /**
23873      * Object collection of toolbar tooltips for the buttons in the editor. The key
23874      * is the command id associated with that button and the value is a valid QuickTips object.
23875      * For example:
23876 <pre><code>
23877 {
23878     bold : {
23879         title: 'Bold (Ctrl+B)',
23880         text: 'Make the selected text bold.',
23881         cls: 'x-html-editor-tip'
23882     },
23883     italic : {
23884         title: 'Italic (Ctrl+I)',
23885         text: 'Make the selected text italic.',
23886         cls: 'x-html-editor-tip'
23887     },
23888     ...
23889 </code></pre>
23890     * @type Object
23891      */
23892     buttonTips : {
23893         bold : {
23894             title: 'Bold (Ctrl+B)',
23895             text: 'Make the selected text bold.',
23896             cls: 'x-html-editor-tip'
23897         },
23898         italic : {
23899             title: 'Italic (Ctrl+I)',
23900             text: 'Make the selected text italic.',
23901             cls: 'x-html-editor-tip'
23902         },
23903         underline : {
23904             title: 'Underline (Ctrl+U)',
23905             text: 'Underline the selected text.',
23906             cls: 'x-html-editor-tip'
23907         },
23908         strikethrough : {
23909             title: 'Strikethrough',
23910             text: 'Strikethrough the selected text.',
23911             cls: 'x-html-editor-tip'
23912         },
23913         increasefontsize : {
23914             title: 'Grow Text',
23915             text: 'Increase the font size.',
23916             cls: 'x-html-editor-tip'
23917         },
23918         decreasefontsize : {
23919             title: 'Shrink Text',
23920             text: 'Decrease the font size.',
23921             cls: 'x-html-editor-tip'
23922         },
23923         backcolor : {
23924             title: 'Text Highlight Color',
23925             text: 'Change the background color of the selected text.',
23926             cls: 'x-html-editor-tip'
23927         },
23928         forecolor : {
23929             title: 'Font Color',
23930             text: 'Change the color of the selected text.',
23931             cls: 'x-html-editor-tip'
23932         },
23933         justifyleft : {
23934             title: 'Align Text Left',
23935             text: 'Align text to the left.',
23936             cls: 'x-html-editor-tip'
23937         },
23938         justifycenter : {
23939             title: 'Center Text',
23940             text: 'Center text in the editor.',
23941             cls: 'x-html-editor-tip'
23942         },
23943         justifyright : {
23944             title: 'Align Text Right',
23945             text: 'Align text to the right.',
23946             cls: 'x-html-editor-tip'
23947         },
23948         insertunorderedlist : {
23949             title: 'Bullet List',
23950             text: 'Start a bulleted list.',
23951             cls: 'x-html-editor-tip'
23952         },
23953         insertorderedlist : {
23954             title: 'Numbered List',
23955             text: 'Start a numbered list.',
23956             cls: 'x-html-editor-tip'
23957         },
23958         createlink : {
23959             title: 'Hyperlink',
23960             text: 'Make the selected text a hyperlink.',
23961             cls: 'x-html-editor-tip'
23962         },
23963         sourceedit : {
23964             title: 'Source Edit',
23965             text: 'Switch to source editing mode.',
23966             cls: 'x-html-editor-tip'
23967         }
23968     },
23969     // private
23970     onDestroy : function(){
23971         if(this.rendered){
23972             
23973             this.tb.items.each(function(item){
23974                 if(item.menu){
23975                     item.menu.removeAll();
23976                     if(item.menu.el){
23977                         item.menu.el.destroy();
23978                     }
23979                 }
23980                 item.destroy();
23981             });
23982              
23983         }
23984     },
23985     onFirstFocus: function() {
23986         this.tb.items.each(function(item){
23987            item.enable();
23988         });
23989     }
23990 });
23991
23992
23993
23994
23995 // <script type="text/javascript">
23996 /*
23997  * Based on
23998  * Ext JS Library 1.1.1
23999  * Copyright(c) 2006-2007, Ext JS, LLC.
24000  *  
24001  
24002  */
24003
24004  
24005 /**
24006  * @class Roo.form.HtmlEditor.ToolbarContext
24007  * Context Toolbar
24008  * 
24009  * Usage:
24010  *
24011  new Roo.form.HtmlEditor({
24012     ....
24013     toolbars : [
24014         { xtype: 'ToolbarStandard', styles : {} }
24015         { xtype: 'ToolbarContext', disable : {} }
24016     ]
24017 })
24018
24019      
24020  * 
24021  * @config : {Object} disable List of elements to disable.. (not done yet.)
24022  * @config : {Object} styles  Map of styles available.
24023  * 
24024  */
24025
24026 Roo.form.HtmlEditor.ToolbarContext = function(config)
24027 {
24028     
24029     Roo.apply(this, config);
24030     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24031     // dont call parent... till later.
24032     this.styles = this.styles || {};
24033 }
24034
24035  
24036
24037 Roo.form.HtmlEditor.ToolbarContext.types = {
24038     'IMG' : {
24039         width : {
24040             title: "Width",
24041             width: 40
24042         },
24043         height:  {
24044             title: "Height",
24045             width: 40
24046         },
24047         align: {
24048             title: "Align",
24049             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24050             width : 80
24051             
24052         },
24053         border: {
24054             title: "Border",
24055             width: 40
24056         },
24057         alt: {
24058             title: "Alt",
24059             width: 120
24060         },
24061         src : {
24062             title: "Src",
24063             width: 220
24064         }
24065         
24066     },
24067     'A' : {
24068         name : {
24069             title: "Name",
24070             width: 50
24071         },
24072         target:  {
24073             title: "Target",
24074             width: 120
24075         },
24076         href:  {
24077             title: "Href",
24078             width: 220
24079         } // border?
24080         
24081     },
24082     'TABLE' : {
24083         rows : {
24084             title: "Rows",
24085             width: 20
24086         },
24087         cols : {
24088             title: "Cols",
24089             width: 20
24090         },
24091         width : {
24092             title: "Width",
24093             width: 40
24094         },
24095         height : {
24096             title: "Height",
24097             width: 40
24098         },
24099         border : {
24100             title: "Border",
24101             width: 20
24102         }
24103     },
24104     'TD' : {
24105         width : {
24106             title: "Width",
24107             width: 40
24108         },
24109         height : {
24110             title: "Height",
24111             width: 40
24112         },   
24113         align: {
24114             title: "Align",
24115             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24116             width: 80
24117         },
24118         valign: {
24119             title: "Valign",
24120             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24121             width: 80
24122         },
24123         colspan: {
24124             title: "Colspan",
24125             width: 20
24126             
24127         },
24128          'font-family'  : {
24129             title : "Font",
24130             style : 'fontFamily',
24131             displayField: 'display',
24132             optname : 'font-family',
24133             width: 140
24134         }
24135     },
24136     'INPUT' : {
24137         name : {
24138             title: "name",
24139             width: 120
24140         },
24141         value : {
24142             title: "Value",
24143             width: 120
24144         },
24145         width : {
24146             title: "Width",
24147             width: 40
24148         }
24149     },
24150     'LABEL' : {
24151         'for' : {
24152             title: "For",
24153             width: 120
24154         }
24155     },
24156     'TEXTAREA' : {
24157           name : {
24158             title: "name",
24159             width: 120
24160         },
24161         rows : {
24162             title: "Rows",
24163             width: 20
24164         },
24165         cols : {
24166             title: "Cols",
24167             width: 20
24168         }
24169     },
24170     'SELECT' : {
24171         name : {
24172             title: "name",
24173             width: 120
24174         },
24175         selectoptions : {
24176             title: "Options",
24177             width: 200
24178         }
24179     },
24180     
24181     // should we really allow this??
24182     // should this just be 
24183     'BODY' : {
24184         title : {
24185             title: "Title",
24186             width: 200,
24187             disabled : true
24188         }
24189     },
24190     'SPAN' : {
24191         'font-family'  : {
24192             title : "Font",
24193             style : 'fontFamily',
24194             displayField: 'display',
24195             optname : 'font-family',
24196             width: 140
24197         }
24198     },
24199     'DIV' : {
24200         'font-family'  : {
24201             title : "Font",
24202             style : 'fontFamily',
24203             displayField: 'display',
24204             optname : 'font-family',
24205             width: 140
24206         }
24207     },
24208      'P' : {
24209         'font-family'  : {
24210             title : "Font",
24211             style : 'fontFamily',
24212             displayField: 'display',
24213             optname : 'font-family',
24214             width: 140
24215         }
24216     },
24217     
24218     '*' : {
24219         // empty..
24220     }
24221
24222 };
24223
24224 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24225 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24226
24227 Roo.form.HtmlEditor.ToolbarContext.options = {
24228         'font-family'  : [ 
24229                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24230                 [ 'Courier New', 'Courier New'],
24231                 [ 'Tahoma', 'Tahoma'],
24232                 [ 'Times New Roman,serif', 'Times'],
24233                 [ 'Verdana','Verdana' ]
24234         ]
24235 };
24236
24237 // fixme - these need to be configurable..
24238  
24239
24240 //Roo.form.HtmlEditor.ToolbarContext.types
24241
24242
24243 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24244     
24245     tb: false,
24246     
24247     rendered: false,
24248     
24249     editor : false,
24250     editorcore : false,
24251     /**
24252      * @cfg {Object} disable  List of toolbar elements to disable
24253          
24254      */
24255     disable : false,
24256     /**
24257      * @cfg {Object} styles List of styles 
24258      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24259      *
24260      * These must be defined in the page, so they get rendered correctly..
24261      * .headline { }
24262      * TD.underline { }
24263      * 
24264      */
24265     styles : false,
24266     
24267     options: false,
24268     
24269     toolbars : false,
24270     
24271     init : function(editor)
24272     {
24273         this.editor = editor;
24274         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24275         var editorcore = this.editorcore;
24276         
24277         var fid = editorcore.frameId;
24278         var etb = this;
24279         function btn(id, toggle, handler){
24280             var xid = fid + '-'+ id ;
24281             return {
24282                 id : xid,
24283                 cmd : id,
24284                 cls : 'x-btn-icon x-edit-'+id,
24285                 enableToggle:toggle !== false,
24286                 scope: editorcore, // was editor...
24287                 handler:handler||editorcore.relayBtnCmd,
24288                 clickEvent:'mousedown',
24289                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24290                 tabIndex:-1
24291             };
24292         }
24293         // create a new element.
24294         var wdiv = editor.wrap.createChild({
24295                 tag: 'div'
24296             }, editor.wrap.dom.firstChild.nextSibling, true);
24297         
24298         // can we do this more than once??
24299         
24300          // stop form submits
24301       
24302  
24303         // disable everything...
24304         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24305         this.toolbars = {};
24306            
24307         for (var i in  ty) {
24308           
24309             this.toolbars[i] = this.buildToolbar(ty[i],i);
24310         }
24311         this.tb = this.toolbars.BODY;
24312         this.tb.el.show();
24313         this.buildFooter();
24314         this.footer.show();
24315         editor.on('hide', function( ) { this.footer.hide() }, this);
24316         editor.on('show', function( ) { this.footer.show() }, this);
24317         
24318          
24319         this.rendered = true;
24320         
24321         // the all the btns;
24322         editor.on('editorevent', this.updateToolbar, this);
24323         // other toolbars need to implement this..
24324         //editor.on('editmodechange', this.updateToolbar, this);
24325     },
24326     
24327     
24328     
24329     /**
24330      * Protected method that will not generally be called directly. It triggers
24331      * a toolbar update by reading the markup state of the current selection in the editor.
24332      *
24333      * Note you can force an update by calling on('editorevent', scope, false)
24334      */
24335     updateToolbar: function(editor,ev,sel){
24336
24337         //Roo.log(ev);
24338         // capture mouse up - this is handy for selecting images..
24339         // perhaps should go somewhere else...
24340         if(!this.editorcore.activated){
24341              this.editor.onFirstFocus();
24342             return;
24343         }
24344         
24345         
24346         
24347         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24348         // selectNode - might want to handle IE?
24349         if (ev &&
24350             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24351             ev.target && ev.target.tagName == 'IMG') {
24352             // they have click on an image...
24353             // let's see if we can change the selection...
24354             sel = ev.target;
24355          
24356               var nodeRange = sel.ownerDocument.createRange();
24357             try {
24358                 nodeRange.selectNode(sel);
24359             } catch (e) {
24360                 nodeRange.selectNodeContents(sel);
24361             }
24362             //nodeRange.collapse(true);
24363             var s = this.editorcore.win.getSelection();
24364             s.removeAllRanges();
24365             s.addRange(nodeRange);
24366         }  
24367         
24368       
24369         var updateFooter = sel ? false : true;
24370         
24371         
24372         var ans = this.editorcore.getAllAncestors();
24373         
24374         // pick
24375         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24376         
24377         if (!sel) { 
24378             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24379             sel = sel ? sel : this.editorcore.doc.body;
24380             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24381             
24382         }
24383         // pick a menu that exists..
24384         var tn = sel.tagName.toUpperCase();
24385         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24386         
24387         tn = sel.tagName.toUpperCase();
24388         
24389         var lastSel = this.tb.selectedNode;
24390         
24391         this.tb.selectedNode = sel;
24392         
24393         // if current menu does not match..
24394         
24395         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24396                 
24397             this.tb.el.hide();
24398             ///console.log("show: " + tn);
24399             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24400             this.tb.el.show();
24401             // update name
24402             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24403             
24404             
24405             // update attributes
24406             if (this.tb.fields) {
24407                 this.tb.fields.each(function(e) {
24408                     if (e.stylename) {
24409                         e.setValue(sel.style[e.stylename]);
24410                         return;
24411                     } 
24412                    e.setValue(sel.getAttribute(e.attrname));
24413                 });
24414             }
24415             
24416             var hasStyles = false;
24417             for(var i in this.styles) {
24418                 hasStyles = true;
24419                 break;
24420             }
24421             
24422             // update styles
24423             if (hasStyles) { 
24424                 var st = this.tb.fields.item(0);
24425                 
24426                 st.store.removeAll();
24427                
24428                 
24429                 var cn = sel.className.split(/\s+/);
24430                 
24431                 var avs = [];
24432                 if (this.styles['*']) {
24433                     
24434                     Roo.each(this.styles['*'], function(v) {
24435                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24436                     });
24437                 }
24438                 if (this.styles[tn]) { 
24439                     Roo.each(this.styles[tn], function(v) {
24440                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24441                     });
24442                 }
24443                 
24444                 st.store.loadData(avs);
24445                 st.collapse();
24446                 st.setValue(cn);
24447             }
24448             // flag our selected Node.
24449             this.tb.selectedNode = sel;
24450            
24451            
24452             Roo.menu.MenuMgr.hideAll();
24453
24454         }
24455         
24456         if (!updateFooter) {
24457             //this.footDisp.dom.innerHTML = ''; 
24458             return;
24459         }
24460         // update the footer
24461         //
24462         var html = '';
24463         
24464         this.footerEls = ans.reverse();
24465         Roo.each(this.footerEls, function(a,i) {
24466             if (!a) { return; }
24467             html += html.length ? ' &gt; '  :  '';
24468             
24469             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24470             
24471         });
24472        
24473         // 
24474         var sz = this.footDisp.up('td').getSize();
24475         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24476         this.footDisp.dom.style.marginLeft = '5px';
24477         
24478         this.footDisp.dom.style.overflow = 'hidden';
24479         
24480         this.footDisp.dom.innerHTML = html;
24481             
24482         //this.editorsyncValue();
24483     },
24484      
24485     
24486    
24487        
24488     // private
24489     onDestroy : function(){
24490         if(this.rendered){
24491             
24492             this.tb.items.each(function(item){
24493                 if(item.menu){
24494                     item.menu.removeAll();
24495                     if(item.menu.el){
24496                         item.menu.el.destroy();
24497                     }
24498                 }
24499                 item.destroy();
24500             });
24501              
24502         }
24503     },
24504     onFirstFocus: function() {
24505         // need to do this for all the toolbars..
24506         this.tb.items.each(function(item){
24507            item.enable();
24508         });
24509     },
24510     buildToolbar: function(tlist, nm)
24511     {
24512         var editor = this.editor;
24513         var editorcore = this.editorcore;
24514          // create a new element.
24515         var wdiv = editor.wrap.createChild({
24516                 tag: 'div'
24517             }, editor.wrap.dom.firstChild.nextSibling, true);
24518         
24519        
24520         var tb = new Roo.Toolbar(wdiv);
24521         // add the name..
24522         
24523         tb.add(nm+ ":&nbsp;");
24524         
24525         var styles = [];
24526         for(var i in this.styles) {
24527             styles.push(i);
24528         }
24529         
24530         // styles...
24531         if (styles && styles.length) {
24532             
24533             // this needs a multi-select checkbox...
24534             tb.addField( new Roo.form.ComboBox({
24535                 store: new Roo.data.SimpleStore({
24536                     id : 'val',
24537                     fields: ['val', 'selected'],
24538                     data : [] 
24539                 }),
24540                 name : '-roo-edit-className',
24541                 attrname : 'className',
24542                 displayField: 'val',
24543                 typeAhead: false,
24544                 mode: 'local',
24545                 editable : false,
24546                 triggerAction: 'all',
24547                 emptyText:'Select Style',
24548                 selectOnFocus:true,
24549                 width: 130,
24550                 listeners : {
24551                     'select': function(c, r, i) {
24552                         // initial support only for on class per el..
24553                         tb.selectedNode.className =  r ? r.get('val') : '';
24554                         editorcore.syncValue();
24555                     }
24556                 }
24557     
24558             }));
24559         }
24560         
24561         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24562         var tbops = tbc.options;
24563         
24564         for (var i in tlist) {
24565             
24566             var item = tlist[i];
24567             tb.add(item.title + ":&nbsp;");
24568             
24569             
24570             //optname == used so you can configure the options available..
24571             var opts = item.opts ? item.opts : false;
24572             if (item.optname) {
24573                 opts = tbops[item.optname];
24574            
24575             }
24576             
24577             if (opts) {
24578                 // opts == pulldown..
24579                 tb.addField( new Roo.form.ComboBox({
24580                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24581                         id : 'val',
24582                         fields: ['val', 'display'],
24583                         data : opts  
24584                     }),
24585                     name : '-roo-edit-' + i,
24586                     attrname : i,
24587                     stylename : item.style ? item.style : false,
24588                     displayField: item.displayField ? item.displayField : 'val',
24589                     valueField :  'val',
24590                     typeAhead: false,
24591                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24592                     editable : false,
24593                     triggerAction: 'all',
24594                     emptyText:'Select',
24595                     selectOnFocus:true,
24596                     width: item.width ? item.width  : 130,
24597                     listeners : {
24598                         'select': function(c, r, i) {
24599                             if (c.stylename) {
24600                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24601                                 return;
24602                             }
24603                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24604                         }
24605                     }
24606
24607                 }));
24608                 continue;
24609                     
24610                  
24611                 
24612                 tb.addField( new Roo.form.TextField({
24613                     name: i,
24614                     width: 100,
24615                     //allowBlank:false,
24616                     value: ''
24617                 }));
24618                 continue;
24619             }
24620             tb.addField( new Roo.form.TextField({
24621                 name: '-roo-edit-' + i,
24622                 attrname : i,
24623                 
24624                 width: item.width,
24625                 //allowBlank:true,
24626                 value: '',
24627                 listeners: {
24628                     'change' : function(f, nv, ov) {
24629                         tb.selectedNode.setAttribute(f.attrname, nv);
24630                         editorcore.syncValue();
24631                     }
24632                 }
24633             }));
24634              
24635         }
24636         
24637         var _this = this;
24638         
24639         if(nm == 'BODY'){
24640             tb.addSeparator();
24641         
24642             tb.addButton( {
24643                 text: 'Stylesheets',
24644
24645                 listeners : {
24646                     click : function ()
24647                     {
24648                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24649                     }
24650                 }
24651             });
24652         }
24653         
24654         tb.addFill();
24655         tb.addButton( {
24656             text: 'Remove Tag',
24657     
24658             listeners : {
24659                 click : function ()
24660                 {
24661                     // remove
24662                     // undo does not work.
24663                      
24664                     var sn = tb.selectedNode;
24665                     
24666                     var pn = sn.parentNode;
24667                     
24668                     var stn =  sn.childNodes[0];
24669                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24670                     while (sn.childNodes.length) {
24671                         var node = sn.childNodes[0];
24672                         sn.removeChild(node);
24673                         //Roo.log(node);
24674                         pn.insertBefore(node, sn);
24675                         
24676                     }
24677                     pn.removeChild(sn);
24678                     var range = editorcore.createRange();
24679         
24680                     range.setStart(stn,0);
24681                     range.setEnd(en,0); //????
24682                     //range.selectNode(sel);
24683                     
24684                     
24685                     var selection = editorcore.getSelection();
24686                     selection.removeAllRanges();
24687                     selection.addRange(range);
24688                     
24689                     
24690                     
24691                     //_this.updateToolbar(null, null, pn);
24692                     _this.updateToolbar(null, null, null);
24693                     _this.footDisp.dom.innerHTML = ''; 
24694                 }
24695             }
24696             
24697                     
24698                 
24699             
24700         });
24701         
24702         
24703         tb.el.on('click', function(e){
24704             e.preventDefault(); // what does this do?
24705         });
24706         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24707         tb.el.hide();
24708         tb.name = nm;
24709         // dont need to disable them... as they will get hidden
24710         return tb;
24711          
24712         
24713     },
24714     buildFooter : function()
24715     {
24716         
24717         var fel = this.editor.wrap.createChild();
24718         this.footer = new Roo.Toolbar(fel);
24719         // toolbar has scrolly on left / right?
24720         var footDisp= new Roo.Toolbar.Fill();
24721         var _t = this;
24722         this.footer.add(
24723             {
24724                 text : '&lt;',
24725                 xtype: 'Button',
24726                 handler : function() {
24727                     _t.footDisp.scrollTo('left',0,true)
24728                 }
24729             }
24730         );
24731         this.footer.add( footDisp );
24732         this.footer.add( 
24733             {
24734                 text : '&gt;',
24735                 xtype: 'Button',
24736                 handler : function() {
24737                     // no animation..
24738                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24739                 }
24740             }
24741         );
24742         var fel = Roo.get(footDisp.el);
24743         fel.addClass('x-editor-context');
24744         this.footDispWrap = fel; 
24745         this.footDispWrap.overflow  = 'hidden';
24746         
24747         this.footDisp = fel.createChild();
24748         this.footDispWrap.on('click', this.onContextClick, this)
24749         
24750         
24751     },
24752     onContextClick : function (ev,dom)
24753     {
24754         ev.preventDefault();
24755         var  cn = dom.className;
24756         //Roo.log(cn);
24757         if (!cn.match(/x-ed-loc-/)) {
24758             return;
24759         }
24760         var n = cn.split('-').pop();
24761         var ans = this.footerEls;
24762         var sel = ans[n];
24763         
24764          // pick
24765         var range = this.editorcore.createRange();
24766         
24767         range.selectNodeContents(sel);
24768         //range.selectNode(sel);
24769         
24770         
24771         var selection = this.editorcore.getSelection();
24772         selection.removeAllRanges();
24773         selection.addRange(range);
24774         
24775         
24776         
24777         this.updateToolbar(null, null, sel);
24778         
24779         
24780     }
24781     
24782     
24783     
24784     
24785     
24786 });
24787
24788
24789
24790
24791
24792 /*
24793  * Based on:
24794  * Ext JS Library 1.1.1
24795  * Copyright(c) 2006-2007, Ext JS, LLC.
24796  *
24797  * Originally Released Under LGPL - original licence link has changed is not relivant.
24798  *
24799  * Fork - LGPL
24800  * <script type="text/javascript">
24801  */
24802  
24803 /**
24804  * @class Roo.form.BasicForm
24805  * @extends Roo.util.Observable
24806  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24807  * @constructor
24808  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24809  * @param {Object} config Configuration options
24810  */
24811 Roo.form.BasicForm = function(el, config){
24812     this.allItems = [];
24813     this.childForms = [];
24814     Roo.apply(this, config);
24815     /*
24816      * The Roo.form.Field items in this form.
24817      * @type MixedCollection
24818      */
24819      
24820      
24821     this.items = new Roo.util.MixedCollection(false, function(o){
24822         return o.id || (o.id = Roo.id());
24823     });
24824     this.addEvents({
24825         /**
24826          * @event beforeaction
24827          * Fires before any action is performed. Return false to cancel the action.
24828          * @param {Form} this
24829          * @param {Action} action The action to be performed
24830          */
24831         beforeaction: true,
24832         /**
24833          * @event actionfailed
24834          * Fires when an action fails.
24835          * @param {Form} this
24836          * @param {Action} action The action that failed
24837          */
24838         actionfailed : true,
24839         /**
24840          * @event actioncomplete
24841          * Fires when an action is completed.
24842          * @param {Form} this
24843          * @param {Action} action The action that completed
24844          */
24845         actioncomplete : true
24846     });
24847     if(el){
24848         this.initEl(el);
24849     }
24850     Roo.form.BasicForm.superclass.constructor.call(this);
24851     
24852     Roo.form.BasicForm.popover.apply();
24853 };
24854
24855 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24856     /**
24857      * @cfg {String} method
24858      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24859      */
24860     /**
24861      * @cfg {DataReader} reader
24862      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24863      * This is optional as there is built-in support for processing JSON.
24864      */
24865     /**
24866      * @cfg {DataReader} errorReader
24867      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24868      * This is completely optional as there is built-in support for processing JSON.
24869      */
24870     /**
24871      * @cfg {String} url
24872      * The URL to use for form actions if one isn't supplied in the action options.
24873      */
24874     /**
24875      * @cfg {Boolean} fileUpload
24876      * Set to true if this form is a file upload.
24877      */
24878      
24879     /**
24880      * @cfg {Object} baseParams
24881      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24882      */
24883      /**
24884      
24885     /**
24886      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24887      */
24888     timeout: 30,
24889
24890     // private
24891     activeAction : null,
24892
24893     /**
24894      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24895      * or setValues() data instead of when the form was first created.
24896      */
24897     trackResetOnLoad : false,
24898     
24899     
24900     /**
24901      * childForms - used for multi-tab forms
24902      * @type {Array}
24903      */
24904     childForms : false,
24905     
24906     /**
24907      * allItems - full list of fields.
24908      * @type {Array}
24909      */
24910     allItems : false,
24911     
24912     /**
24913      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24914      * element by passing it or its id or mask the form itself by passing in true.
24915      * @type Mixed
24916      */
24917     waitMsgTarget : false,
24918     
24919     /**
24920      * @type Boolean
24921      */
24922     disableMask : false,
24923     
24924     /**
24925      * @cfg {Boolean} errorMask (true|false) default false
24926      */
24927     errorMask : false,
24928     
24929     /**
24930      * @cfg {Number} maskOffset Default 100
24931      */
24932     maskOffset : 100,
24933
24934     // private
24935     initEl : function(el){
24936         this.el = Roo.get(el);
24937         this.id = this.el.id || Roo.id();
24938         this.el.on('submit', this.onSubmit, this);
24939         this.el.addClass('x-form');
24940     },
24941
24942     // private
24943     onSubmit : function(e){
24944         e.stopEvent();
24945     },
24946
24947     /**
24948      * Returns true if client-side validation on the form is successful.
24949      * @return Boolean
24950      */
24951     isValid : function(){
24952         var valid = true;
24953         var target = false;
24954         this.items.each(function(f){
24955             if(f.validate()){
24956                 return;
24957             }
24958             
24959             valid = false;
24960                 
24961             if(!target && f.el.isVisible(true)){
24962                 target = f;
24963             }
24964         });
24965         
24966         if(this.errorMask && !valid){
24967             Roo.form.BasicForm.popover.mask(this, target);
24968         }
24969         
24970         return valid;
24971     },
24972
24973     /**
24974      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24975      * @return Boolean
24976      */
24977     isDirty : function(){
24978         var dirty = false;
24979         this.items.each(function(f){
24980            if(f.isDirty()){
24981                dirty = true;
24982                return false;
24983            }
24984         });
24985         return dirty;
24986     },
24987     
24988     /**
24989      * Returns true if any fields in this form have changed since their original load. (New version)
24990      * @return Boolean
24991      */
24992     
24993     hasChanged : function()
24994     {
24995         var dirty = false;
24996         this.items.each(function(f){
24997            if(f.hasChanged()){
24998                dirty = true;
24999                return false;
25000            }
25001         });
25002         return dirty;
25003         
25004     },
25005     /**
25006      * Resets all hasChanged to 'false' -
25007      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
25008      * So hasChanged storage is only to be used for this purpose
25009      * @return Boolean
25010      */
25011     resetHasChanged : function()
25012     {
25013         this.items.each(function(f){
25014            f.resetHasChanged();
25015         });
25016         
25017     },
25018     
25019     
25020     /**
25021      * Performs a predefined action (submit or load) or custom actions you define on this form.
25022      * @param {String} actionName The name of the action type
25023      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25024      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25025      * accept other config options):
25026      * <pre>
25027 Property          Type             Description
25028 ----------------  ---------------  ----------------------------------------------------------------------------------
25029 url               String           The url for the action (defaults to the form's url)
25030 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25031 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25032 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25033                                    validate the form on the client (defaults to false)
25034      * </pre>
25035      * @return {BasicForm} this
25036      */
25037     doAction : function(action, options){
25038         if(typeof action == 'string'){
25039             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25040         }
25041         if(this.fireEvent('beforeaction', this, action) !== false){
25042             this.beforeAction(action);
25043             action.run.defer(100, action);
25044         }
25045         return this;
25046     },
25047
25048     /**
25049      * Shortcut to do a submit action.
25050      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25051      * @return {BasicForm} this
25052      */
25053     submit : function(options){
25054         this.doAction('submit', options);
25055         return this;
25056     },
25057
25058     /**
25059      * Shortcut to do a load action.
25060      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25061      * @return {BasicForm} this
25062      */
25063     load : function(options){
25064         this.doAction('load', options);
25065         return this;
25066     },
25067
25068     /**
25069      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25070      * @param {Record} record The record to edit
25071      * @return {BasicForm} this
25072      */
25073     updateRecord : function(record){
25074         record.beginEdit();
25075         var fs = record.fields;
25076         fs.each(function(f){
25077             var field = this.findField(f.name);
25078             if(field){
25079                 record.set(f.name, field.getValue());
25080             }
25081         }, this);
25082         record.endEdit();
25083         return this;
25084     },
25085
25086     /**
25087      * Loads an Roo.data.Record into this form.
25088      * @param {Record} record The record to load
25089      * @return {BasicForm} this
25090      */
25091     loadRecord : function(record){
25092         this.setValues(record.data);
25093         return this;
25094     },
25095
25096     // private
25097     beforeAction : function(action){
25098         var o = action.options;
25099         
25100         if(!this.disableMask) {
25101             if(this.waitMsgTarget === true){
25102                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25103             }else if(this.waitMsgTarget){
25104                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25105                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25106             }else {
25107                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25108             }
25109         }
25110         
25111          
25112     },
25113
25114     // private
25115     afterAction : function(action, success){
25116         this.activeAction = null;
25117         var o = action.options;
25118         
25119         if(!this.disableMask) {
25120             if(this.waitMsgTarget === true){
25121                 this.el.unmask();
25122             }else if(this.waitMsgTarget){
25123                 this.waitMsgTarget.unmask();
25124             }else{
25125                 Roo.MessageBox.updateProgress(1);
25126                 Roo.MessageBox.hide();
25127             }
25128         }
25129         
25130         if(success){
25131             if(o.reset){
25132                 this.reset();
25133             }
25134             Roo.callback(o.success, o.scope, [this, action]);
25135             this.fireEvent('actioncomplete', this, action);
25136             
25137         }else{
25138             
25139             // failure condition..
25140             // we have a scenario where updates need confirming.
25141             // eg. if a locking scenario exists..
25142             // we look for { errors : { needs_confirm : true }} in the response.
25143             if (
25144                 (typeof(action.result) != 'undefined')  &&
25145                 (typeof(action.result.errors) != 'undefined')  &&
25146                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25147            ){
25148                 var _t = this;
25149                 Roo.MessageBox.confirm(
25150                     "Change requires confirmation",
25151                     action.result.errorMsg,
25152                     function(r) {
25153                         if (r != 'yes') {
25154                             return;
25155                         }
25156                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25157                     }
25158                     
25159                 );
25160                 
25161                 
25162                 
25163                 return;
25164             }
25165             
25166             Roo.callback(o.failure, o.scope, [this, action]);
25167             // show an error message if no failed handler is set..
25168             if (!this.hasListener('actionfailed')) {
25169                 Roo.MessageBox.alert("Error",
25170                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25171                         action.result.errorMsg :
25172                         "Saving Failed, please check your entries or try again"
25173                 );
25174             }
25175             
25176             this.fireEvent('actionfailed', this, action);
25177         }
25178         
25179     },
25180
25181     /**
25182      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25183      * @param {String} id The value to search for
25184      * @return Field
25185      */
25186     findField : function(id){
25187         var field = this.items.get(id);
25188         if(!field){
25189             this.items.each(function(f){
25190                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25191                     field = f;
25192                     return false;
25193                 }
25194             });
25195         }
25196         return field || null;
25197     },
25198
25199     /**
25200      * Add a secondary form to this one, 
25201      * Used to provide tabbed forms. One form is primary, with hidden values 
25202      * which mirror the elements from the other forms.
25203      * 
25204      * @param {Roo.form.Form} form to add.
25205      * 
25206      */
25207     addForm : function(form)
25208     {
25209        
25210         if (this.childForms.indexOf(form) > -1) {
25211             // already added..
25212             return;
25213         }
25214         this.childForms.push(form);
25215         var n = '';
25216         Roo.each(form.allItems, function (fe) {
25217             
25218             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25219             if (this.findField(n)) { // already added..
25220                 return;
25221             }
25222             var add = new Roo.form.Hidden({
25223                 name : n
25224             });
25225             add.render(this.el);
25226             
25227             this.add( add );
25228         }, this);
25229         
25230     },
25231     /**
25232      * Mark fields in this form invalid in bulk.
25233      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25234      * @return {BasicForm} this
25235      */
25236     markInvalid : function(errors){
25237         if(errors instanceof Array){
25238             for(var i = 0, len = errors.length; i < len; i++){
25239                 var fieldError = errors[i];
25240                 var f = this.findField(fieldError.id);
25241                 if(f){
25242                     f.markInvalid(fieldError.msg);
25243                 }
25244             }
25245         }else{
25246             var field, id;
25247             for(id in errors){
25248                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25249                     field.markInvalid(errors[id]);
25250                 }
25251             }
25252         }
25253         Roo.each(this.childForms || [], function (f) {
25254             f.markInvalid(errors);
25255         });
25256         
25257         return this;
25258     },
25259
25260     /**
25261      * Set values for fields in this form in bulk.
25262      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25263      * @return {BasicForm} this
25264      */
25265     setValues : function(values){
25266         if(values instanceof Array){ // array of objects
25267             for(var i = 0, len = values.length; i < len; i++){
25268                 var v = values[i];
25269                 var f = this.findField(v.id);
25270                 if(f){
25271                     f.setValue(v.value);
25272                     if(this.trackResetOnLoad){
25273                         f.originalValue = f.getValue();
25274                     }
25275                 }
25276             }
25277         }else{ // object hash
25278             var field, id;
25279             for(id in values){
25280                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25281                     
25282                     if (field.setFromData && 
25283                         field.valueField && 
25284                         field.displayField &&
25285                         // combos' with local stores can 
25286                         // be queried via setValue()
25287                         // to set their value..
25288                         (field.store && !field.store.isLocal)
25289                         ) {
25290                         // it's a combo
25291                         var sd = { };
25292                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25293                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25294                         field.setFromData(sd);
25295                         
25296                     } else {
25297                         field.setValue(values[id]);
25298                     }
25299                     
25300                     
25301                     if(this.trackResetOnLoad){
25302                         field.originalValue = field.getValue();
25303                     }
25304                 }
25305             }
25306         }
25307         this.resetHasChanged();
25308         
25309         
25310         Roo.each(this.childForms || [], function (f) {
25311             f.setValues(values);
25312             f.resetHasChanged();
25313         });
25314                 
25315         return this;
25316     },
25317  
25318     /**
25319      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25320      * they are returned as an array.
25321      * @param {Boolean} asString
25322      * @return {Object}
25323      */
25324     getValues : function(asString){
25325         if (this.childForms) {
25326             // copy values from the child forms
25327             Roo.each(this.childForms, function (f) {
25328                 this.setValues(f.getValues());
25329             }, this);
25330         }
25331         
25332         // use formdata
25333         if (typeof(FormData) != 'undefined' && asString !== true) {
25334             var fd = (new FormData(this.el.dom)).entries();
25335             var ret = {};
25336             var ent = fd.next();
25337             while (!ent.done) {
25338                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25339                 ent = fd.next();
25340             };
25341             return ret;
25342         }
25343         
25344         
25345         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25346         if(asString === true){
25347             return fs;
25348         }
25349         return Roo.urlDecode(fs);
25350     },
25351     
25352     /**
25353      * Returns the fields in this form as an object with key/value pairs. 
25354      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25355      * @return {Object}
25356      */
25357     getFieldValues : function(with_hidden)
25358     {
25359         if (this.childForms) {
25360             // copy values from the child forms
25361             // should this call getFieldValues - probably not as we do not currently copy
25362             // hidden fields when we generate..
25363             Roo.each(this.childForms, function (f) {
25364                 this.setValues(f.getValues());
25365             }, this);
25366         }
25367         
25368         var ret = {};
25369         this.items.each(function(f){
25370             if (!f.getName()) {
25371                 return;
25372             }
25373             var v = f.getValue();
25374             if (f.inputType =='radio') {
25375                 if (typeof(ret[f.getName()]) == 'undefined') {
25376                     ret[f.getName()] = ''; // empty..
25377                 }
25378                 
25379                 if (!f.el.dom.checked) {
25380                     return;
25381                     
25382                 }
25383                 v = f.el.dom.value;
25384                 
25385             }
25386             
25387             // not sure if this supported any more..
25388             if ((typeof(v) == 'object') && f.getRawValue) {
25389                 v = f.getRawValue() ; // dates..
25390             }
25391             // combo boxes where name != hiddenName...
25392             if (f.name != f.getName()) {
25393                 ret[f.name] = f.getRawValue();
25394             }
25395             ret[f.getName()] = v;
25396         });
25397         
25398         return ret;
25399     },
25400
25401     /**
25402      * Clears all invalid messages in this form.
25403      * @return {BasicForm} this
25404      */
25405     clearInvalid : function(){
25406         this.items.each(function(f){
25407            f.clearInvalid();
25408         });
25409         
25410         Roo.each(this.childForms || [], function (f) {
25411             f.clearInvalid();
25412         });
25413         
25414         
25415         return this;
25416     },
25417
25418     /**
25419      * Resets this form.
25420      * @return {BasicForm} this
25421      */
25422     reset : function(){
25423         this.items.each(function(f){
25424             f.reset();
25425         });
25426         
25427         Roo.each(this.childForms || [], function (f) {
25428             f.reset();
25429         });
25430         this.resetHasChanged();
25431         
25432         return this;
25433     },
25434
25435     /**
25436      * Add Roo.form components to this form.
25437      * @param {Field} field1
25438      * @param {Field} field2 (optional)
25439      * @param {Field} etc (optional)
25440      * @return {BasicForm} this
25441      */
25442     add : function(){
25443         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25444         return this;
25445     },
25446
25447
25448     /**
25449      * Removes a field from the items collection (does NOT remove its markup).
25450      * @param {Field} field
25451      * @return {BasicForm} this
25452      */
25453     remove : function(field){
25454         this.items.remove(field);
25455         return this;
25456     },
25457
25458     /**
25459      * Looks at the fields in this form, checks them for an id attribute,
25460      * and calls applyTo on the existing dom element with that id.
25461      * @return {BasicForm} this
25462      */
25463     render : function(){
25464         this.items.each(function(f){
25465             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25466                 f.applyTo(f.id);
25467             }
25468         });
25469         return this;
25470     },
25471
25472     /**
25473      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25474      * @param {Object} values
25475      * @return {BasicForm} this
25476      */
25477     applyToFields : function(o){
25478         this.items.each(function(f){
25479            Roo.apply(f, o);
25480         });
25481         return this;
25482     },
25483
25484     /**
25485      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25486      * @param {Object} values
25487      * @return {BasicForm} this
25488      */
25489     applyIfToFields : function(o){
25490         this.items.each(function(f){
25491            Roo.applyIf(f, o);
25492         });
25493         return this;
25494     }
25495 });
25496
25497 // back compat
25498 Roo.BasicForm = Roo.form.BasicForm;
25499
25500 Roo.apply(Roo.form.BasicForm, {
25501     
25502     popover : {
25503         
25504         padding : 5,
25505         
25506         isApplied : false,
25507         
25508         isMasked : false,
25509         
25510         form : false,
25511         
25512         target : false,
25513         
25514         intervalID : false,
25515         
25516         maskEl : false,
25517         
25518         apply : function()
25519         {
25520             if(this.isApplied){
25521                 return;
25522             }
25523             
25524             this.maskEl = {
25525                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25526                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25527                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25528                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25529             };
25530             
25531             this.maskEl.top.enableDisplayMode("block");
25532             this.maskEl.left.enableDisplayMode("block");
25533             this.maskEl.bottom.enableDisplayMode("block");
25534             this.maskEl.right.enableDisplayMode("block");
25535             
25536             Roo.get(document.body).on('click', function(){
25537                 this.unmask();
25538             }, this);
25539             
25540             Roo.get(document.body).on('touchstart', function(){
25541                 this.unmask();
25542             }, this);
25543             
25544             this.isApplied = true
25545         },
25546         
25547         mask : function(form, target)
25548         {
25549             this.form = form;
25550             
25551             this.target = target;
25552             
25553             if(!this.form.errorMask || !target.el){
25554                 return;
25555             }
25556             
25557             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25558             
25559             var ot = this.target.el.calcOffsetsTo(scrollable);
25560             
25561             var scrollTo = ot[1] - this.form.maskOffset;
25562             
25563             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25564             
25565             scrollable.scrollTo('top', scrollTo);
25566             
25567             var el = this.target.wrap || this.target.el;
25568             
25569             var box = el.getBox();
25570             
25571             this.maskEl.top.setStyle('position', 'absolute');
25572             this.maskEl.top.setStyle('z-index', 10000);
25573             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25574             this.maskEl.top.setLeft(0);
25575             this.maskEl.top.setTop(0);
25576             this.maskEl.top.show();
25577             
25578             this.maskEl.left.setStyle('position', 'absolute');
25579             this.maskEl.left.setStyle('z-index', 10000);
25580             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25581             this.maskEl.left.setLeft(0);
25582             this.maskEl.left.setTop(box.y - this.padding);
25583             this.maskEl.left.show();
25584
25585             this.maskEl.bottom.setStyle('position', 'absolute');
25586             this.maskEl.bottom.setStyle('z-index', 10000);
25587             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25588             this.maskEl.bottom.setLeft(0);
25589             this.maskEl.bottom.setTop(box.bottom + this.padding);
25590             this.maskEl.bottom.show();
25591
25592             this.maskEl.right.setStyle('position', 'absolute');
25593             this.maskEl.right.setStyle('z-index', 10000);
25594             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25595             this.maskEl.right.setLeft(box.right + this.padding);
25596             this.maskEl.right.setTop(box.y - this.padding);
25597             this.maskEl.right.show();
25598
25599             this.intervalID = window.setInterval(function() {
25600                 Roo.form.BasicForm.popover.unmask();
25601             }, 10000);
25602
25603             window.onwheel = function(){ return false;};
25604             
25605             (function(){ this.isMasked = true; }).defer(500, this);
25606             
25607         },
25608         
25609         unmask : function()
25610         {
25611             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25612                 return;
25613             }
25614             
25615             this.maskEl.top.setStyle('position', 'absolute');
25616             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25617             this.maskEl.top.hide();
25618
25619             this.maskEl.left.setStyle('position', 'absolute');
25620             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25621             this.maskEl.left.hide();
25622
25623             this.maskEl.bottom.setStyle('position', 'absolute');
25624             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25625             this.maskEl.bottom.hide();
25626
25627             this.maskEl.right.setStyle('position', 'absolute');
25628             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25629             this.maskEl.right.hide();
25630             
25631             window.onwheel = function(){ return true;};
25632             
25633             if(this.intervalID){
25634                 window.clearInterval(this.intervalID);
25635                 this.intervalID = false;
25636             }
25637             
25638             this.isMasked = false;
25639             
25640         }
25641         
25642     }
25643     
25644 });/*
25645  * Based on:
25646  * Ext JS Library 1.1.1
25647  * Copyright(c) 2006-2007, Ext JS, LLC.
25648  *
25649  * Originally Released Under LGPL - original licence link has changed is not relivant.
25650  *
25651  * Fork - LGPL
25652  * <script type="text/javascript">
25653  */
25654
25655 /**
25656  * @class Roo.form.Form
25657  * @extends Roo.form.BasicForm
25658  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25659  * @constructor
25660  * @param {Object} config Configuration options
25661  */
25662 Roo.form.Form = function(config){
25663     var xitems =  [];
25664     if (config.items) {
25665         xitems = config.items;
25666         delete config.items;
25667     }
25668    
25669     
25670     Roo.form.Form.superclass.constructor.call(this, null, config);
25671     this.url = this.url || this.action;
25672     if(!this.root){
25673         this.root = new Roo.form.Layout(Roo.applyIf({
25674             id: Roo.id()
25675         }, config));
25676     }
25677     this.active = this.root;
25678     /**
25679      * Array of all the buttons that have been added to this form via {@link addButton}
25680      * @type Array
25681      */
25682     this.buttons = [];
25683     this.allItems = [];
25684     this.addEvents({
25685         /**
25686          * @event clientvalidation
25687          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25688          * @param {Form} this
25689          * @param {Boolean} valid true if the form has passed client-side validation
25690          */
25691         clientvalidation: true,
25692         /**
25693          * @event rendered
25694          * Fires when the form is rendered
25695          * @param {Roo.form.Form} form
25696          */
25697         rendered : true
25698     });
25699     
25700     if (this.progressUrl) {
25701             // push a hidden field onto the list of fields..
25702             this.addxtype( {
25703                     xns: Roo.form, 
25704                     xtype : 'Hidden', 
25705                     name : 'UPLOAD_IDENTIFIER' 
25706             });
25707         }
25708         
25709     
25710     Roo.each(xitems, this.addxtype, this);
25711     
25712 };
25713
25714 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25715     /**
25716      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25717      */
25718     /**
25719      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25720      */
25721     /**
25722      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25723      */
25724     buttonAlign:'center',
25725
25726     /**
25727      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25728      */
25729     minButtonWidth:75,
25730
25731     /**
25732      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25733      * This property cascades to child containers if not set.
25734      */
25735     labelAlign:'left',
25736
25737     /**
25738      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25739      * fires a looping event with that state. This is required to bind buttons to the valid
25740      * state using the config value formBind:true on the button.
25741      */
25742     monitorValid : false,
25743
25744     /**
25745      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25746      */
25747     monitorPoll : 200,
25748     
25749     /**
25750      * @cfg {String} progressUrl - Url to return progress data 
25751      */
25752     
25753     progressUrl : false,
25754     /**
25755      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25756      * sending a formdata with extra parameters - eg uploaded elements.
25757      */
25758     
25759     formData : false,
25760     
25761     /**
25762      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25763      * fields are added and the column is closed. If no fields are passed the column remains open
25764      * until end() is called.
25765      * @param {Object} config The config to pass to the column
25766      * @param {Field} field1 (optional)
25767      * @param {Field} field2 (optional)
25768      * @param {Field} etc (optional)
25769      * @return Column The column container object
25770      */
25771     column : function(c){
25772         var col = new Roo.form.Column(c);
25773         this.start(col);
25774         if(arguments.length > 1){ // duplicate code required because of Opera
25775             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25776             this.end();
25777         }
25778         return col;
25779     },
25780
25781     /**
25782      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25783      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25784      * until end() is called.
25785      * @param {Object} config The config to pass to the fieldset
25786      * @param {Field} field1 (optional)
25787      * @param {Field} field2 (optional)
25788      * @param {Field} etc (optional)
25789      * @return FieldSet The fieldset container object
25790      */
25791     fieldset : function(c){
25792         var fs = new Roo.form.FieldSet(c);
25793         this.start(fs);
25794         if(arguments.length > 1){ // duplicate code required because of Opera
25795             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25796             this.end();
25797         }
25798         return fs;
25799     },
25800
25801     /**
25802      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25803      * fields are added and the container is closed. If no fields are passed the container remains open
25804      * until end() is called.
25805      * @param {Object} config The config to pass to the Layout
25806      * @param {Field} field1 (optional)
25807      * @param {Field} field2 (optional)
25808      * @param {Field} etc (optional)
25809      * @return Layout The container object
25810      */
25811     container : function(c){
25812         var l = new Roo.form.Layout(c);
25813         this.start(l);
25814         if(arguments.length > 1){ // duplicate code required because of Opera
25815             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25816             this.end();
25817         }
25818         return l;
25819     },
25820
25821     /**
25822      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25823      * @param {Object} container A Roo.form.Layout or subclass of Layout
25824      * @return {Form} this
25825      */
25826     start : function(c){
25827         // cascade label info
25828         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25829         this.active.stack.push(c);
25830         c.ownerCt = this.active;
25831         this.active = c;
25832         return this;
25833     },
25834
25835     /**
25836      * Closes the current open container
25837      * @return {Form} this
25838      */
25839     end : function(){
25840         if(this.active == this.root){
25841             return this;
25842         }
25843         this.active = this.active.ownerCt;
25844         return this;
25845     },
25846
25847     /**
25848      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25849      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25850      * as the label of the field.
25851      * @param {Field} field1
25852      * @param {Field} field2 (optional)
25853      * @param {Field} etc. (optional)
25854      * @return {Form} this
25855      */
25856     add : function(){
25857         this.active.stack.push.apply(this.active.stack, arguments);
25858         this.allItems.push.apply(this.allItems,arguments);
25859         var r = [];
25860         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25861             if(a[i].isFormField){
25862                 r.push(a[i]);
25863             }
25864         }
25865         if(r.length > 0){
25866             Roo.form.Form.superclass.add.apply(this, r);
25867         }
25868         return this;
25869     },
25870     
25871
25872     
25873     
25874     
25875      /**
25876      * Find any element that has been added to a form, using it's ID or name
25877      * This can include framesets, columns etc. along with regular fields..
25878      * @param {String} id - id or name to find.
25879      
25880      * @return {Element} e - or false if nothing found.
25881      */
25882     findbyId : function(id)
25883     {
25884         var ret = false;
25885         if (!id) {
25886             return ret;
25887         }
25888         Roo.each(this.allItems, function(f){
25889             if (f.id == id || f.name == id ){
25890                 ret = f;
25891                 return false;
25892             }
25893         });
25894         return ret;
25895     },
25896
25897     
25898     
25899     /**
25900      * Render this form into the passed container. This should only be called once!
25901      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25902      * @return {Form} this
25903      */
25904     render : function(ct)
25905     {
25906         
25907         
25908         
25909         ct = Roo.get(ct);
25910         var o = this.autoCreate || {
25911             tag: 'form',
25912             method : this.method || 'POST',
25913             id : this.id || Roo.id()
25914         };
25915         this.initEl(ct.createChild(o));
25916
25917         this.root.render(this.el);
25918         
25919        
25920              
25921         this.items.each(function(f){
25922             f.render('x-form-el-'+f.id);
25923         });
25924
25925         if(this.buttons.length > 0){
25926             // tables are required to maintain order and for correct IE layout
25927             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25928                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25929                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25930             }}, null, true);
25931             var tr = tb.getElementsByTagName('tr')[0];
25932             for(var i = 0, len = this.buttons.length; i < len; i++) {
25933                 var b = this.buttons[i];
25934                 var td = document.createElement('td');
25935                 td.className = 'x-form-btn-td';
25936                 b.render(tr.appendChild(td));
25937             }
25938         }
25939         if(this.monitorValid){ // initialize after render
25940             this.startMonitoring();
25941         }
25942         this.fireEvent('rendered', this);
25943         return this;
25944     },
25945
25946     /**
25947      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25948      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25949      * object or a valid Roo.DomHelper element config
25950      * @param {Function} handler The function called when the button is clicked
25951      * @param {Object} scope (optional) The scope of the handler function
25952      * @return {Roo.Button}
25953      */
25954     addButton : function(config, handler, scope){
25955         var bc = {
25956             handler: handler,
25957             scope: scope,
25958             minWidth: this.minButtonWidth,
25959             hideParent:true
25960         };
25961         if(typeof config == "string"){
25962             bc.text = config;
25963         }else{
25964             Roo.apply(bc, config);
25965         }
25966         var btn = new Roo.Button(null, bc);
25967         this.buttons.push(btn);
25968         return btn;
25969     },
25970
25971      /**
25972      * Adds a series of form elements (using the xtype property as the factory method.
25973      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25974      * @param {Object} config 
25975      */
25976     
25977     addxtype : function()
25978     {
25979         var ar = Array.prototype.slice.call(arguments, 0);
25980         var ret = false;
25981         for(var i = 0; i < ar.length; i++) {
25982             if (!ar[i]) {
25983                 continue; // skip -- if this happends something invalid got sent, we 
25984                 // should ignore it, as basically that interface element will not show up
25985                 // and that should be pretty obvious!!
25986             }
25987             
25988             if (Roo.form[ar[i].xtype]) {
25989                 ar[i].form = this;
25990                 var fe = Roo.factory(ar[i], Roo.form);
25991                 if (!ret) {
25992                     ret = fe;
25993                 }
25994                 fe.form = this;
25995                 if (fe.store) {
25996                     fe.store.form = this;
25997                 }
25998                 if (fe.isLayout) {  
25999                          
26000                     this.start(fe);
26001                     this.allItems.push(fe);
26002                     if (fe.items && fe.addxtype) {
26003                         fe.addxtype.apply(fe, fe.items);
26004                         delete fe.items;
26005                     }
26006                      this.end();
26007                     continue;
26008                 }
26009                 
26010                 
26011                  
26012                 this.add(fe);
26013               //  console.log('adding ' + ar[i].xtype);
26014             }
26015             if (ar[i].xtype == 'Button') {  
26016                 //console.log('adding button');
26017                 //console.log(ar[i]);
26018                 this.addButton(ar[i]);
26019                 this.allItems.push(fe);
26020                 continue;
26021             }
26022             
26023             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26024                 alert('end is not supported on xtype any more, use items');
26025             //    this.end();
26026             //    //console.log('adding end');
26027             }
26028             
26029         }
26030         return ret;
26031     },
26032     
26033     /**
26034      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26035      * option "monitorValid"
26036      */
26037     startMonitoring : function(){
26038         if(!this.bound){
26039             this.bound = true;
26040             Roo.TaskMgr.start({
26041                 run : this.bindHandler,
26042                 interval : this.monitorPoll || 200,
26043                 scope: this
26044             });
26045         }
26046     },
26047
26048     /**
26049      * Stops monitoring of the valid state of this form
26050      */
26051     stopMonitoring : function(){
26052         this.bound = false;
26053     },
26054
26055     // private
26056     bindHandler : function(){
26057         if(!this.bound){
26058             return false; // stops binding
26059         }
26060         var valid = true;
26061         this.items.each(function(f){
26062             if(!f.isValid(true)){
26063                 valid = false;
26064                 return false;
26065             }
26066         });
26067         for(var i = 0, len = this.buttons.length; i < len; i++){
26068             var btn = this.buttons[i];
26069             if(btn.formBind === true && btn.disabled === valid){
26070                 btn.setDisabled(!valid);
26071             }
26072         }
26073         this.fireEvent('clientvalidation', this, valid);
26074     }
26075     
26076     
26077     
26078     
26079     
26080     
26081     
26082     
26083 });
26084
26085
26086 // back compat
26087 Roo.Form = Roo.form.Form;
26088 /*
26089  * Based on:
26090  * Ext JS Library 1.1.1
26091  * Copyright(c) 2006-2007, Ext JS, LLC.
26092  *
26093  * Originally Released Under LGPL - original licence link has changed is not relivant.
26094  *
26095  * Fork - LGPL
26096  * <script type="text/javascript">
26097  */
26098
26099 // as we use this in bootstrap.
26100 Roo.namespace('Roo.form');
26101  /**
26102  * @class Roo.form.Action
26103  * Internal Class used to handle form actions
26104  * @constructor
26105  * @param {Roo.form.BasicForm} el The form element or its id
26106  * @param {Object} config Configuration options
26107  */
26108
26109  
26110  
26111 // define the action interface
26112 Roo.form.Action = function(form, options){
26113     this.form = form;
26114     this.options = options || {};
26115 };
26116 /**
26117  * Client Validation Failed
26118  * @const 
26119  */
26120 Roo.form.Action.CLIENT_INVALID = 'client';
26121 /**
26122  * Server Validation Failed
26123  * @const 
26124  */
26125 Roo.form.Action.SERVER_INVALID = 'server';
26126  /**
26127  * Connect to Server Failed
26128  * @const 
26129  */
26130 Roo.form.Action.CONNECT_FAILURE = 'connect';
26131 /**
26132  * Reading Data from Server Failed
26133  * @const 
26134  */
26135 Roo.form.Action.LOAD_FAILURE = 'load';
26136
26137 Roo.form.Action.prototype = {
26138     type : 'default',
26139     failureType : undefined,
26140     response : undefined,
26141     result : undefined,
26142
26143     // interface method
26144     run : function(options){
26145
26146     },
26147
26148     // interface method
26149     success : function(response){
26150
26151     },
26152
26153     // interface method
26154     handleResponse : function(response){
26155
26156     },
26157
26158     // default connection failure
26159     failure : function(response){
26160         
26161         this.response = response;
26162         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26163         this.form.afterAction(this, false);
26164     },
26165
26166     processResponse : function(response){
26167         this.response = response;
26168         if(!response.responseText){
26169             return true;
26170         }
26171         this.result = this.handleResponse(response);
26172         return this.result;
26173     },
26174
26175     // utility functions used internally
26176     getUrl : function(appendParams){
26177         var url = this.options.url || this.form.url || this.form.el.dom.action;
26178         if(appendParams){
26179             var p = this.getParams();
26180             if(p){
26181                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26182             }
26183         }
26184         return url;
26185     },
26186
26187     getMethod : function(){
26188         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26189     },
26190
26191     getParams : function(){
26192         var bp = this.form.baseParams;
26193         var p = this.options.params;
26194         if(p){
26195             if(typeof p == "object"){
26196                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26197             }else if(typeof p == 'string' && bp){
26198                 p += '&' + Roo.urlEncode(bp);
26199             }
26200         }else if(bp){
26201             p = Roo.urlEncode(bp);
26202         }
26203         return p;
26204     },
26205
26206     createCallback : function(){
26207         return {
26208             success: this.success,
26209             failure: this.failure,
26210             scope: this,
26211             timeout: (this.form.timeout*1000),
26212             upload: this.form.fileUpload ? this.success : undefined
26213         };
26214     }
26215 };
26216
26217 Roo.form.Action.Submit = function(form, options){
26218     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26219 };
26220
26221 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26222     type : 'submit',
26223
26224     haveProgress : false,
26225     uploadComplete : false,
26226     
26227     // uploadProgress indicator.
26228     uploadProgress : function()
26229     {
26230         if (!this.form.progressUrl) {
26231             return;
26232         }
26233         
26234         if (!this.haveProgress) {
26235             Roo.MessageBox.progress("Uploading", "Uploading");
26236         }
26237         if (this.uploadComplete) {
26238            Roo.MessageBox.hide();
26239            return;
26240         }
26241         
26242         this.haveProgress = true;
26243    
26244         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26245         
26246         var c = new Roo.data.Connection();
26247         c.request({
26248             url : this.form.progressUrl,
26249             params: {
26250                 id : uid
26251             },
26252             method: 'GET',
26253             success : function(req){
26254                //console.log(data);
26255                 var rdata = false;
26256                 var edata;
26257                 try  {
26258                    rdata = Roo.decode(req.responseText)
26259                 } catch (e) {
26260                     Roo.log("Invalid data from server..");
26261                     Roo.log(edata);
26262                     return;
26263                 }
26264                 if (!rdata || !rdata.success) {
26265                     Roo.log(rdata);
26266                     Roo.MessageBox.alert(Roo.encode(rdata));
26267                     return;
26268                 }
26269                 var data = rdata.data;
26270                 
26271                 if (this.uploadComplete) {
26272                    Roo.MessageBox.hide();
26273                    return;
26274                 }
26275                    
26276                 if (data){
26277                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26278                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26279                     );
26280                 }
26281                 this.uploadProgress.defer(2000,this);
26282             },
26283        
26284             failure: function(data) {
26285                 Roo.log('progress url failed ');
26286                 Roo.log(data);
26287             },
26288             scope : this
26289         });
26290            
26291     },
26292     
26293     
26294     run : function()
26295     {
26296         // run get Values on the form, so it syncs any secondary forms.
26297         this.form.getValues();
26298         
26299         var o = this.options;
26300         var method = this.getMethod();
26301         var isPost = method == 'POST';
26302         if(o.clientValidation === false || this.form.isValid()){
26303             
26304             if (this.form.progressUrl) {
26305                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26306                     (new Date() * 1) + '' + Math.random());
26307                     
26308             } 
26309             
26310             
26311             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26312                 form:this.form.el.dom,
26313                 url:this.getUrl(!isPost),
26314                 method: method,
26315                 params:isPost ? this.getParams() : null,
26316                 isUpload: this.form.fileUpload,
26317                 formData : this.form.formData
26318             }));
26319             
26320             this.uploadProgress();
26321
26322         }else if (o.clientValidation !== false){ // client validation failed
26323             this.failureType = Roo.form.Action.CLIENT_INVALID;
26324             this.form.afterAction(this, false);
26325         }
26326     },
26327
26328     success : function(response)
26329     {
26330         this.uploadComplete= true;
26331         if (this.haveProgress) {
26332             Roo.MessageBox.hide();
26333         }
26334         
26335         
26336         var result = this.processResponse(response);
26337         if(result === true || result.success){
26338             this.form.afterAction(this, true);
26339             return;
26340         }
26341         if(result.errors){
26342             this.form.markInvalid(result.errors);
26343             this.failureType = Roo.form.Action.SERVER_INVALID;
26344         }
26345         this.form.afterAction(this, false);
26346     },
26347     failure : function(response)
26348     {
26349         this.uploadComplete= true;
26350         if (this.haveProgress) {
26351             Roo.MessageBox.hide();
26352         }
26353         
26354         this.response = response;
26355         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26356         this.form.afterAction(this, false);
26357     },
26358     
26359     handleResponse : function(response){
26360         if(this.form.errorReader){
26361             var rs = this.form.errorReader.read(response);
26362             var errors = [];
26363             if(rs.records){
26364                 for(var i = 0, len = rs.records.length; i < len; i++) {
26365                     var r = rs.records[i];
26366                     errors[i] = r.data;
26367                 }
26368             }
26369             if(errors.length < 1){
26370                 errors = null;
26371             }
26372             return {
26373                 success : rs.success,
26374                 errors : errors
26375             };
26376         }
26377         var ret = false;
26378         try {
26379             ret = Roo.decode(response.responseText);
26380         } catch (e) {
26381             ret = {
26382                 success: false,
26383                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26384                 errors : []
26385             };
26386         }
26387         return ret;
26388         
26389     }
26390 });
26391
26392
26393 Roo.form.Action.Load = function(form, options){
26394     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26395     this.reader = this.form.reader;
26396 };
26397
26398 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26399     type : 'load',
26400
26401     run : function(){
26402         
26403         Roo.Ajax.request(Roo.apply(
26404                 this.createCallback(), {
26405                     method:this.getMethod(),
26406                     url:this.getUrl(false),
26407                     params:this.getParams()
26408         }));
26409     },
26410
26411     success : function(response){
26412         
26413         var result = this.processResponse(response);
26414         if(result === true || !result.success || !result.data){
26415             this.failureType = Roo.form.Action.LOAD_FAILURE;
26416             this.form.afterAction(this, false);
26417             return;
26418         }
26419         this.form.clearInvalid();
26420         this.form.setValues(result.data);
26421         this.form.afterAction(this, true);
26422     },
26423
26424     handleResponse : function(response){
26425         if(this.form.reader){
26426             var rs = this.form.reader.read(response);
26427             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26428             return {
26429                 success : rs.success,
26430                 data : data
26431             };
26432         }
26433         return Roo.decode(response.responseText);
26434     }
26435 });
26436
26437 Roo.form.Action.ACTION_TYPES = {
26438     'load' : Roo.form.Action.Load,
26439     'submit' : Roo.form.Action.Submit
26440 };/*
26441  * Based on:
26442  * Ext JS Library 1.1.1
26443  * Copyright(c) 2006-2007, Ext JS, LLC.
26444  *
26445  * Originally Released Under LGPL - original licence link has changed is not relivant.
26446  *
26447  * Fork - LGPL
26448  * <script type="text/javascript">
26449  */
26450  
26451 /**
26452  * @class Roo.form.Layout
26453  * @extends Roo.Component
26454  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26455  * @constructor
26456  * @param {Object} config Configuration options
26457  */
26458 Roo.form.Layout = function(config){
26459     var xitems = [];
26460     if (config.items) {
26461         xitems = config.items;
26462         delete config.items;
26463     }
26464     Roo.form.Layout.superclass.constructor.call(this, config);
26465     this.stack = [];
26466     Roo.each(xitems, this.addxtype, this);
26467      
26468 };
26469
26470 Roo.extend(Roo.form.Layout, Roo.Component, {
26471     /**
26472      * @cfg {String/Object} autoCreate
26473      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26474      */
26475     /**
26476      * @cfg {String/Object/Function} style
26477      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26478      * a function which returns such a specification.
26479      */
26480     /**
26481      * @cfg {String} labelAlign
26482      * Valid values are "left," "top" and "right" (defaults to "left")
26483      */
26484     /**
26485      * @cfg {Number} labelWidth
26486      * Fixed width in pixels of all field labels (defaults to undefined)
26487      */
26488     /**
26489      * @cfg {Boolean} clear
26490      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26491      */
26492     clear : true,
26493     /**
26494      * @cfg {String} labelSeparator
26495      * The separator to use after field labels (defaults to ':')
26496      */
26497     labelSeparator : ':',
26498     /**
26499      * @cfg {Boolean} hideLabels
26500      * True to suppress the display of field labels in this layout (defaults to false)
26501      */
26502     hideLabels : false,
26503
26504     // private
26505     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26506     
26507     isLayout : true,
26508     
26509     // private
26510     onRender : function(ct, position){
26511         if(this.el){ // from markup
26512             this.el = Roo.get(this.el);
26513         }else {  // generate
26514             var cfg = this.getAutoCreate();
26515             this.el = ct.createChild(cfg, position);
26516         }
26517         if(this.style){
26518             this.el.applyStyles(this.style);
26519         }
26520         if(this.labelAlign){
26521             this.el.addClass('x-form-label-'+this.labelAlign);
26522         }
26523         if(this.hideLabels){
26524             this.labelStyle = "display:none";
26525             this.elementStyle = "padding-left:0;";
26526         }else{
26527             if(typeof this.labelWidth == 'number'){
26528                 this.labelStyle = "width:"+this.labelWidth+"px;";
26529                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26530             }
26531             if(this.labelAlign == 'top'){
26532                 this.labelStyle = "width:auto;";
26533                 this.elementStyle = "padding-left:0;";
26534             }
26535         }
26536         var stack = this.stack;
26537         var slen = stack.length;
26538         if(slen > 0){
26539             if(!this.fieldTpl){
26540                 var t = new Roo.Template(
26541                     '<div class="x-form-item {5}">',
26542                         '<label for="{0}" style="{2}">{1}{4}</label>',
26543                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26544                         '</div>',
26545                     '</div><div class="x-form-clear-left"></div>'
26546                 );
26547                 t.disableFormats = true;
26548                 t.compile();
26549                 Roo.form.Layout.prototype.fieldTpl = t;
26550             }
26551             for(var i = 0; i < slen; i++) {
26552                 if(stack[i].isFormField){
26553                     this.renderField(stack[i]);
26554                 }else{
26555                     this.renderComponent(stack[i]);
26556                 }
26557             }
26558         }
26559         if(this.clear){
26560             this.el.createChild({cls:'x-form-clear'});
26561         }
26562     },
26563
26564     // private
26565     renderField : function(f){
26566         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26567                f.id, //0
26568                f.fieldLabel, //1
26569                f.labelStyle||this.labelStyle||'', //2
26570                this.elementStyle||'', //3
26571                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26572                f.itemCls||this.itemCls||''  //5
26573        ], true).getPrevSibling());
26574     },
26575
26576     // private
26577     renderComponent : function(c){
26578         c.render(c.isLayout ? this.el : this.el.createChild());    
26579     },
26580     /**
26581      * Adds a object form elements (using the xtype property as the factory method.)
26582      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26583      * @param {Object} config 
26584      */
26585     addxtype : function(o)
26586     {
26587         // create the lement.
26588         o.form = this.form;
26589         var fe = Roo.factory(o, Roo.form);
26590         this.form.allItems.push(fe);
26591         this.stack.push(fe);
26592         
26593         if (fe.isFormField) {
26594             this.form.items.add(fe);
26595         }
26596          
26597         return fe;
26598     }
26599 });
26600
26601 /**
26602  * @class Roo.form.Column
26603  * @extends Roo.form.Layout
26604  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26605  * @constructor
26606  * @param {Object} config Configuration options
26607  */
26608 Roo.form.Column = function(config){
26609     Roo.form.Column.superclass.constructor.call(this, config);
26610 };
26611
26612 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26613     /**
26614      * @cfg {Number/String} width
26615      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26616      */
26617     /**
26618      * @cfg {String/Object} autoCreate
26619      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26620      */
26621
26622     // private
26623     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26624
26625     // private
26626     onRender : function(ct, position){
26627         Roo.form.Column.superclass.onRender.call(this, ct, position);
26628         if(this.width){
26629             this.el.setWidth(this.width);
26630         }
26631     }
26632 });
26633
26634
26635 /**
26636  * @class Roo.form.Row
26637  * @extends Roo.form.Layout
26638  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26639  * @constructor
26640  * @param {Object} config Configuration options
26641  */
26642
26643  
26644 Roo.form.Row = function(config){
26645     Roo.form.Row.superclass.constructor.call(this, config);
26646 };
26647  
26648 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26649       /**
26650      * @cfg {Number/String} width
26651      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26652      */
26653     /**
26654      * @cfg {Number/String} height
26655      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26656      */
26657     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26658     
26659     padWidth : 20,
26660     // private
26661     onRender : function(ct, position){
26662         //console.log('row render');
26663         if(!this.rowTpl){
26664             var t = new Roo.Template(
26665                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26666                     '<label for="{0}" style="{2}">{1}{4}</label>',
26667                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26668                     '</div>',
26669                 '</div>'
26670             );
26671             t.disableFormats = true;
26672             t.compile();
26673             Roo.form.Layout.prototype.rowTpl = t;
26674         }
26675         this.fieldTpl = this.rowTpl;
26676         
26677         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26678         var labelWidth = 100;
26679         
26680         if ((this.labelAlign != 'top')) {
26681             if (typeof this.labelWidth == 'number') {
26682                 labelWidth = this.labelWidth
26683             }
26684             this.padWidth =  20 + labelWidth;
26685             
26686         }
26687         
26688         Roo.form.Column.superclass.onRender.call(this, ct, position);
26689         if(this.width){
26690             this.el.setWidth(this.width);
26691         }
26692         if(this.height){
26693             this.el.setHeight(this.height);
26694         }
26695     },
26696     
26697     // private
26698     renderField : function(f){
26699         f.fieldEl = this.fieldTpl.append(this.el, [
26700                f.id, f.fieldLabel,
26701                f.labelStyle||this.labelStyle||'',
26702                this.elementStyle||'',
26703                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26704                f.itemCls||this.itemCls||'',
26705                f.width ? f.width + this.padWidth : 160 + this.padWidth
26706        ],true);
26707     }
26708 });
26709  
26710
26711 /**
26712  * @class Roo.form.FieldSet
26713  * @extends Roo.form.Layout
26714  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26715  * @constructor
26716  * @param {Object} config Configuration options
26717  */
26718 Roo.form.FieldSet = function(config){
26719     Roo.form.FieldSet.superclass.constructor.call(this, config);
26720 };
26721
26722 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26723     /**
26724      * @cfg {String} legend
26725      * The text to display as the legend for the FieldSet (defaults to '')
26726      */
26727     /**
26728      * @cfg {String/Object} autoCreate
26729      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26730      */
26731
26732     // private
26733     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26734
26735     // private
26736     onRender : function(ct, position){
26737         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26738         if(this.legend){
26739             this.setLegend(this.legend);
26740         }
26741     },
26742
26743     // private
26744     setLegend : function(text){
26745         if(this.rendered){
26746             this.el.child('legend').update(text);
26747         }
26748     }
26749 });/*
26750  * Based on:
26751  * Ext JS Library 1.1.1
26752  * Copyright(c) 2006-2007, Ext JS, LLC.
26753  *
26754  * Originally Released Under LGPL - original licence link has changed is not relivant.
26755  *
26756  * Fork - LGPL
26757  * <script type="text/javascript">
26758  */
26759 /**
26760  * @class Roo.form.VTypes
26761  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26762  * @singleton
26763  */
26764 Roo.form.VTypes = function(){
26765     // closure these in so they are only created once.
26766     var alpha = /^[a-zA-Z_]+$/;
26767     var alphanum = /^[a-zA-Z0-9_]+$/;
26768     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26769     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26770
26771     // All these messages and functions are configurable
26772     return {
26773         /**
26774          * The function used to validate email addresses
26775          * @param {String} value The email address
26776          */
26777         'email' : function(v){
26778             return email.test(v);
26779         },
26780         /**
26781          * The error text to display when the email validation function returns false
26782          * @type String
26783          */
26784         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26785         /**
26786          * The keystroke filter mask to be applied on email input
26787          * @type RegExp
26788          */
26789         'emailMask' : /[a-z0-9_\.\-@]/i,
26790
26791         /**
26792          * The function used to validate URLs
26793          * @param {String} value The URL
26794          */
26795         'url' : function(v){
26796             return url.test(v);
26797         },
26798         /**
26799          * The error text to display when the url validation function returns false
26800          * @type String
26801          */
26802         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26803         
26804         /**
26805          * The function used to validate alpha values
26806          * @param {String} value The value
26807          */
26808         'alpha' : function(v){
26809             return alpha.test(v);
26810         },
26811         /**
26812          * The error text to display when the alpha validation function returns false
26813          * @type String
26814          */
26815         'alphaText' : 'This field should only contain letters and _',
26816         /**
26817          * The keystroke filter mask to be applied on alpha input
26818          * @type RegExp
26819          */
26820         'alphaMask' : /[a-z_]/i,
26821
26822         /**
26823          * The function used to validate alphanumeric values
26824          * @param {String} value The value
26825          */
26826         'alphanum' : function(v){
26827             return alphanum.test(v);
26828         },
26829         /**
26830          * The error text to display when the alphanumeric validation function returns false
26831          * @type String
26832          */
26833         'alphanumText' : 'This field should only contain letters, numbers and _',
26834         /**
26835          * The keystroke filter mask to be applied on alphanumeric input
26836          * @type RegExp
26837          */
26838         'alphanumMask' : /[a-z0-9_]/i
26839     };
26840 }();//<script type="text/javascript">
26841
26842 /**
26843  * @class Roo.form.FCKeditor
26844  * @extends Roo.form.TextArea
26845  * Wrapper around the FCKEditor http://www.fckeditor.net
26846  * @constructor
26847  * Creates a new FCKeditor
26848  * @param {Object} config Configuration options
26849  */
26850 Roo.form.FCKeditor = function(config){
26851     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26852     this.addEvents({
26853          /**
26854          * @event editorinit
26855          * Fired when the editor is initialized - you can add extra handlers here..
26856          * @param {FCKeditor} this
26857          * @param {Object} the FCK object.
26858          */
26859         editorinit : true
26860     });
26861     
26862     
26863 };
26864 Roo.form.FCKeditor.editors = { };
26865 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26866 {
26867     //defaultAutoCreate : {
26868     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26869     //},
26870     // private
26871     /**
26872      * @cfg {Object} fck options - see fck manual for details.
26873      */
26874     fckconfig : false,
26875     
26876     /**
26877      * @cfg {Object} fck toolbar set (Basic or Default)
26878      */
26879     toolbarSet : 'Basic',
26880     /**
26881      * @cfg {Object} fck BasePath
26882      */ 
26883     basePath : '/fckeditor/',
26884     
26885     
26886     frame : false,
26887     
26888     value : '',
26889     
26890    
26891     onRender : function(ct, position)
26892     {
26893         if(!this.el){
26894             this.defaultAutoCreate = {
26895                 tag: "textarea",
26896                 style:"width:300px;height:60px;",
26897                 autocomplete: "new-password"
26898             };
26899         }
26900         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26901         /*
26902         if(this.grow){
26903             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26904             if(this.preventScrollbars){
26905                 this.el.setStyle("overflow", "hidden");
26906             }
26907             this.el.setHeight(this.growMin);
26908         }
26909         */
26910         //console.log('onrender' + this.getId() );
26911         Roo.form.FCKeditor.editors[this.getId()] = this;
26912          
26913
26914         this.replaceTextarea() ;
26915         
26916     },
26917     
26918     getEditor : function() {
26919         return this.fckEditor;
26920     },
26921     /**
26922      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26923      * @param {Mixed} value The value to set
26924      */
26925     
26926     
26927     setValue : function(value)
26928     {
26929         //console.log('setValue: ' + value);
26930         
26931         if(typeof(value) == 'undefined') { // not sure why this is happending...
26932             return;
26933         }
26934         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26935         
26936         //if(!this.el || !this.getEditor()) {
26937         //    this.value = value;
26938             //this.setValue.defer(100,this,[value]);    
26939         //    return;
26940         //} 
26941         
26942         if(!this.getEditor()) {
26943             return;
26944         }
26945         
26946         this.getEditor().SetData(value);
26947         
26948         //
26949
26950     },
26951
26952     /**
26953      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26954      * @return {Mixed} value The field value
26955      */
26956     getValue : function()
26957     {
26958         
26959         if (this.frame && this.frame.dom.style.display == 'none') {
26960             return Roo.form.FCKeditor.superclass.getValue.call(this);
26961         }
26962         
26963         if(!this.el || !this.getEditor()) {
26964            
26965            // this.getValue.defer(100,this); 
26966             return this.value;
26967         }
26968        
26969         
26970         var value=this.getEditor().GetData();
26971         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26972         return Roo.form.FCKeditor.superclass.getValue.call(this);
26973         
26974
26975     },
26976
26977     /**
26978      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26979      * @return {Mixed} value The field value
26980      */
26981     getRawValue : function()
26982     {
26983         if (this.frame && this.frame.dom.style.display == 'none') {
26984             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26985         }
26986         
26987         if(!this.el || !this.getEditor()) {
26988             //this.getRawValue.defer(100,this); 
26989             return this.value;
26990             return;
26991         }
26992         
26993         
26994         
26995         var value=this.getEditor().GetData();
26996         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26997         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26998          
26999     },
27000     
27001     setSize : function(w,h) {
27002         
27003         
27004         
27005         //if (this.frame && this.frame.dom.style.display == 'none') {
27006         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27007         //    return;
27008         //}
27009         //if(!this.el || !this.getEditor()) {
27010         //    this.setSize.defer(100,this, [w,h]); 
27011         //    return;
27012         //}
27013         
27014         
27015         
27016         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27017         
27018         this.frame.dom.setAttribute('width', w);
27019         this.frame.dom.setAttribute('height', h);
27020         this.frame.setSize(w,h);
27021         
27022     },
27023     
27024     toggleSourceEdit : function(value) {
27025         
27026       
27027          
27028         this.el.dom.style.display = value ? '' : 'none';
27029         this.frame.dom.style.display = value ?  'none' : '';
27030         
27031     },
27032     
27033     
27034     focus: function(tag)
27035     {
27036         if (this.frame.dom.style.display == 'none') {
27037             return Roo.form.FCKeditor.superclass.focus.call(this);
27038         }
27039         if(!this.el || !this.getEditor()) {
27040             this.focus.defer(100,this, [tag]); 
27041             return;
27042         }
27043         
27044         
27045         
27046         
27047         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27048         this.getEditor().Focus();
27049         if (tgs.length) {
27050             if (!this.getEditor().Selection.GetSelection()) {
27051                 this.focus.defer(100,this, [tag]); 
27052                 return;
27053             }
27054             
27055             
27056             var r = this.getEditor().EditorDocument.createRange();
27057             r.setStart(tgs[0],0);
27058             r.setEnd(tgs[0],0);
27059             this.getEditor().Selection.GetSelection().removeAllRanges();
27060             this.getEditor().Selection.GetSelection().addRange(r);
27061             this.getEditor().Focus();
27062         }
27063         
27064     },
27065     
27066     
27067     
27068     replaceTextarea : function()
27069     {
27070         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27071             return ;
27072         }
27073         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27074         //{
27075             // We must check the elements firstly using the Id and then the name.
27076         var oTextarea = document.getElementById( this.getId() );
27077         
27078         var colElementsByName = document.getElementsByName( this.getId() ) ;
27079          
27080         oTextarea.style.display = 'none' ;
27081
27082         if ( oTextarea.tabIndex ) {            
27083             this.TabIndex = oTextarea.tabIndex ;
27084         }
27085         
27086         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27087         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27088         this.frame = Roo.get(this.getId() + '___Frame')
27089     },
27090     
27091     _getConfigHtml : function()
27092     {
27093         var sConfig = '' ;
27094
27095         for ( var o in this.fckconfig ) {
27096             sConfig += sConfig.length > 0  ? '&amp;' : '';
27097             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27098         }
27099
27100         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27101     },
27102     
27103     
27104     _getIFrameHtml : function()
27105     {
27106         var sFile = 'fckeditor.html' ;
27107         /* no idea what this is about..
27108         try
27109         {
27110             if ( (/fcksource=true/i).test( window.top.location.search ) )
27111                 sFile = 'fckeditor.original.html' ;
27112         }
27113         catch (e) { 
27114         */
27115
27116         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27117         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27118         
27119         
27120         var html = '<iframe id="' + this.getId() +
27121             '___Frame" src="' + sLink +
27122             '" width="' + this.width +
27123             '" height="' + this.height + '"' +
27124             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27125             ' frameborder="0" scrolling="no"></iframe>' ;
27126
27127         return html ;
27128     },
27129     
27130     _insertHtmlBefore : function( html, element )
27131     {
27132         if ( element.insertAdjacentHTML )       {
27133             // IE
27134             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27135         } else { // Gecko
27136             var oRange = document.createRange() ;
27137             oRange.setStartBefore( element ) ;
27138             var oFragment = oRange.createContextualFragment( html );
27139             element.parentNode.insertBefore( oFragment, element ) ;
27140         }
27141     }
27142     
27143     
27144   
27145     
27146     
27147     
27148     
27149
27150 });
27151
27152 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27153
27154 function FCKeditor_OnComplete(editorInstance){
27155     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27156     f.fckEditor = editorInstance;
27157     //console.log("loaded");
27158     f.fireEvent('editorinit', f, editorInstance);
27159
27160   
27161
27162  
27163
27164
27165
27166
27167
27168
27169
27170
27171
27172
27173
27174
27175
27176
27177
27178 //<script type="text/javascript">
27179 /**
27180  * @class Roo.form.GridField
27181  * @extends Roo.form.Field
27182  * Embed a grid (or editable grid into a form)
27183  * STATUS ALPHA
27184  * 
27185  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27186  * it needs 
27187  * xgrid.store = Roo.data.Store
27188  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27189  * xgrid.store.reader = Roo.data.JsonReader 
27190  * 
27191  * 
27192  * @constructor
27193  * Creates a new GridField
27194  * @param {Object} config Configuration options
27195  */
27196 Roo.form.GridField = function(config){
27197     Roo.form.GridField.superclass.constructor.call(this, config);
27198      
27199 };
27200
27201 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27202     /**
27203      * @cfg {Number} width  - used to restrict width of grid..
27204      */
27205     width : 100,
27206     /**
27207      * @cfg {Number} height - used to restrict height of grid..
27208      */
27209     height : 50,
27210      /**
27211      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27212          * 
27213          *}
27214      */
27215     xgrid : false, 
27216     /**
27217      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27218      * {tag: "input", type: "checkbox", autocomplete: "off"})
27219      */
27220    // defaultAutoCreate : { tag: 'div' },
27221     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27222     /**
27223      * @cfg {String} addTitle Text to include for adding a title.
27224      */
27225     addTitle : false,
27226     //
27227     onResize : function(){
27228         Roo.form.Field.superclass.onResize.apply(this, arguments);
27229     },
27230
27231     initEvents : function(){
27232         // Roo.form.Checkbox.superclass.initEvents.call(this);
27233         // has no events...
27234        
27235     },
27236
27237
27238     getResizeEl : function(){
27239         return this.wrap;
27240     },
27241
27242     getPositionEl : function(){
27243         return this.wrap;
27244     },
27245
27246     // private
27247     onRender : function(ct, position){
27248         
27249         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27250         var style = this.style;
27251         delete this.style;
27252         
27253         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27254         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27255         this.viewEl = this.wrap.createChild({ tag: 'div' });
27256         if (style) {
27257             this.viewEl.applyStyles(style);
27258         }
27259         if (this.width) {
27260             this.viewEl.setWidth(this.width);
27261         }
27262         if (this.height) {
27263             this.viewEl.setHeight(this.height);
27264         }
27265         //if(this.inputValue !== undefined){
27266         //this.setValue(this.value);
27267         
27268         
27269         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27270         
27271         
27272         this.grid.render();
27273         this.grid.getDataSource().on('remove', this.refreshValue, this);
27274         this.grid.getDataSource().on('update', this.refreshValue, this);
27275         this.grid.on('afteredit', this.refreshValue, this);
27276  
27277     },
27278      
27279     
27280     /**
27281      * Sets the value of the item. 
27282      * @param {String} either an object  or a string..
27283      */
27284     setValue : function(v){
27285         //this.value = v;
27286         v = v || []; // empty set..
27287         // this does not seem smart - it really only affects memoryproxy grids..
27288         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27289             var ds = this.grid.getDataSource();
27290             // assumes a json reader..
27291             var data = {}
27292             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27293             ds.loadData( data);
27294         }
27295         // clear selection so it does not get stale.
27296         if (this.grid.sm) { 
27297             this.grid.sm.clearSelections();
27298         }
27299         
27300         Roo.form.GridField.superclass.setValue.call(this, v);
27301         this.refreshValue();
27302         // should load data in the grid really....
27303     },
27304     
27305     // private
27306     refreshValue: function() {
27307          var val = [];
27308         this.grid.getDataSource().each(function(r) {
27309             val.push(r.data);
27310         });
27311         this.el.dom.value = Roo.encode(val);
27312     }
27313     
27314      
27315     
27316     
27317 });/*
27318  * Based on:
27319  * Ext JS Library 1.1.1
27320  * Copyright(c) 2006-2007, Ext JS, LLC.
27321  *
27322  * Originally Released Under LGPL - original licence link has changed is not relivant.
27323  *
27324  * Fork - LGPL
27325  * <script type="text/javascript">
27326  */
27327 /**
27328  * @class Roo.form.DisplayField
27329  * @extends Roo.form.Field
27330  * A generic Field to display non-editable data.
27331  * @cfg {Boolean} closable (true|false) default false
27332  * @constructor
27333  * Creates a new Display Field item.
27334  * @param {Object} config Configuration options
27335  */
27336 Roo.form.DisplayField = function(config){
27337     Roo.form.DisplayField.superclass.constructor.call(this, config);
27338     
27339     this.addEvents({
27340         /**
27341          * @event close
27342          * Fires after the click the close btn
27343              * @param {Roo.form.DisplayField} this
27344              */
27345         close : true
27346     });
27347 };
27348
27349 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27350     inputType:      'hidden',
27351     allowBlank:     true,
27352     readOnly:         true,
27353     
27354  
27355     /**
27356      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27357      */
27358     focusClass : undefined,
27359     /**
27360      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27361      */
27362     fieldClass: 'x-form-field',
27363     
27364      /**
27365      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27366      */
27367     valueRenderer: undefined,
27368     
27369     width: 100,
27370     /**
27371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27372      * {tag: "input", type: "checkbox", autocomplete: "off"})
27373      */
27374      
27375  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27376  
27377     closable : false,
27378     
27379     onResize : function(){
27380         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27381         
27382     },
27383
27384     initEvents : function(){
27385         // Roo.form.Checkbox.superclass.initEvents.call(this);
27386         // has no events...
27387         
27388         if(this.closable){
27389             this.closeEl.on('click', this.onClose, this);
27390         }
27391        
27392     },
27393
27394
27395     getResizeEl : function(){
27396         return this.wrap;
27397     },
27398
27399     getPositionEl : function(){
27400         return this.wrap;
27401     },
27402
27403     // private
27404     onRender : function(ct, position){
27405         
27406         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27407         //if(this.inputValue !== undefined){
27408         this.wrap = this.el.wrap();
27409         
27410         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27411         
27412         if(this.closable){
27413             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27414         }
27415         
27416         if (this.bodyStyle) {
27417             this.viewEl.applyStyles(this.bodyStyle);
27418         }
27419         //this.viewEl.setStyle('padding', '2px');
27420         
27421         this.setValue(this.value);
27422         
27423     },
27424 /*
27425     // private
27426     initValue : Roo.emptyFn,
27427
27428   */
27429
27430         // private
27431     onClick : function(){
27432         
27433     },
27434
27435     /**
27436      * Sets the checked state of the checkbox.
27437      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27438      */
27439     setValue : function(v){
27440         this.value = v;
27441         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27442         // this might be called before we have a dom element..
27443         if (!this.viewEl) {
27444             return;
27445         }
27446         this.viewEl.dom.innerHTML = html;
27447         Roo.form.DisplayField.superclass.setValue.call(this, v);
27448
27449     },
27450     
27451     onClose : function(e)
27452     {
27453         e.preventDefault();
27454         
27455         this.fireEvent('close', this);
27456     }
27457 });/*
27458  * 
27459  * Licence- LGPL
27460  * 
27461  */
27462
27463 /**
27464  * @class Roo.form.DayPicker
27465  * @extends Roo.form.Field
27466  * A Day picker show [M] [T] [W] ....
27467  * @constructor
27468  * Creates a new Day Picker
27469  * @param {Object} config Configuration options
27470  */
27471 Roo.form.DayPicker= function(config){
27472     Roo.form.DayPicker.superclass.constructor.call(this, config);
27473      
27474 };
27475
27476 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27477     /**
27478      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27479      */
27480     focusClass : undefined,
27481     /**
27482      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27483      */
27484     fieldClass: "x-form-field",
27485    
27486     /**
27487      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27488      * {tag: "input", type: "checkbox", autocomplete: "off"})
27489      */
27490     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27491     
27492    
27493     actionMode : 'viewEl', 
27494     //
27495     // private
27496  
27497     inputType : 'hidden',
27498     
27499      
27500     inputElement: false, // real input element?
27501     basedOn: false, // ????
27502     
27503     isFormField: true, // not sure where this is needed!!!!
27504
27505     onResize : function(){
27506         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27507         if(!this.boxLabel){
27508             this.el.alignTo(this.wrap, 'c-c');
27509         }
27510     },
27511
27512     initEvents : function(){
27513         Roo.form.Checkbox.superclass.initEvents.call(this);
27514         this.el.on("click", this.onClick,  this);
27515         this.el.on("change", this.onClick,  this);
27516     },
27517
27518
27519     getResizeEl : function(){
27520         return this.wrap;
27521     },
27522
27523     getPositionEl : function(){
27524         return this.wrap;
27525     },
27526
27527     
27528     // private
27529     onRender : function(ct, position){
27530         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27531        
27532         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27533         
27534         var r1 = '<table><tr>';
27535         var r2 = '<tr class="x-form-daypick-icons">';
27536         for (var i=0; i < 7; i++) {
27537             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27538             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27539         }
27540         
27541         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27542         viewEl.select('img').on('click', this.onClick, this);
27543         this.viewEl = viewEl;   
27544         
27545         
27546         // this will not work on Chrome!!!
27547         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27548         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27549         
27550         
27551           
27552
27553     },
27554
27555     // private
27556     initValue : Roo.emptyFn,
27557
27558     /**
27559      * Returns the checked state of the checkbox.
27560      * @return {Boolean} True if checked, else false
27561      */
27562     getValue : function(){
27563         return this.el.dom.value;
27564         
27565     },
27566
27567         // private
27568     onClick : function(e){ 
27569         //this.setChecked(!this.checked);
27570         Roo.get(e.target).toggleClass('x-menu-item-checked');
27571         this.refreshValue();
27572         //if(this.el.dom.checked != this.checked){
27573         //    this.setValue(this.el.dom.checked);
27574        // }
27575     },
27576     
27577     // private
27578     refreshValue : function()
27579     {
27580         var val = '';
27581         this.viewEl.select('img',true).each(function(e,i,n)  {
27582             val += e.is(".x-menu-item-checked") ? String(n) : '';
27583         });
27584         this.setValue(val, true);
27585     },
27586
27587     /**
27588      * Sets the checked state of the checkbox.
27589      * On is always based on a string comparison between inputValue and the param.
27590      * @param {Boolean/String} value - the value to set 
27591      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27592      */
27593     setValue : function(v,suppressEvent){
27594         if (!this.el.dom) {
27595             return;
27596         }
27597         var old = this.el.dom.value ;
27598         this.el.dom.value = v;
27599         if (suppressEvent) {
27600             return ;
27601         }
27602          
27603         // update display..
27604         this.viewEl.select('img',true).each(function(e,i,n)  {
27605             
27606             var on = e.is(".x-menu-item-checked");
27607             var newv = v.indexOf(String(n)) > -1;
27608             if (on != newv) {
27609                 e.toggleClass('x-menu-item-checked');
27610             }
27611             
27612         });
27613         
27614         
27615         this.fireEvent('change', this, v, old);
27616         
27617         
27618     },
27619    
27620     // handle setting of hidden value by some other method!!?!?
27621     setFromHidden: function()
27622     {
27623         if(!this.el){
27624             return;
27625         }
27626         //console.log("SET FROM HIDDEN");
27627         //alert('setFrom hidden');
27628         this.setValue(this.el.dom.value);
27629     },
27630     
27631     onDestroy : function()
27632     {
27633         if(this.viewEl){
27634             Roo.get(this.viewEl).remove();
27635         }
27636          
27637         Roo.form.DayPicker.superclass.onDestroy.call(this);
27638     }
27639
27640 });/*
27641  * RooJS Library 1.1.1
27642  * Copyright(c) 2008-2011  Alan Knowles
27643  *
27644  * License - LGPL
27645  */
27646  
27647
27648 /**
27649  * @class Roo.form.ComboCheck
27650  * @extends Roo.form.ComboBox
27651  * A combobox for multiple select items.
27652  *
27653  * FIXME - could do with a reset button..
27654  * 
27655  * @constructor
27656  * Create a new ComboCheck
27657  * @param {Object} config Configuration options
27658  */
27659 Roo.form.ComboCheck = function(config){
27660     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27661     // should verify some data...
27662     // like
27663     // hiddenName = required..
27664     // displayField = required
27665     // valudField == required
27666     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27667     var _t = this;
27668     Roo.each(req, function(e) {
27669         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27670             throw "Roo.form.ComboCheck : missing value for: " + e;
27671         }
27672     });
27673     
27674     
27675 };
27676
27677 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27678      
27679      
27680     editable : false,
27681      
27682     selectedClass: 'x-menu-item-checked', 
27683     
27684     // private
27685     onRender : function(ct, position){
27686         var _t = this;
27687         
27688         
27689         
27690         if(!this.tpl){
27691             var cls = 'x-combo-list';
27692
27693             
27694             this.tpl =  new Roo.Template({
27695                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27696                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27697                    '<span>{' + this.displayField + '}</span>' +
27698                     '</div>' 
27699                 
27700             });
27701         }
27702  
27703         
27704         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27705         this.view.singleSelect = false;
27706         this.view.multiSelect = true;
27707         this.view.toggleSelect = true;
27708         this.pageTb.add(new Roo.Toolbar.Fill(), {
27709             
27710             text: 'Done',
27711             handler: function()
27712             {
27713                 _t.collapse();
27714             }
27715         });
27716     },
27717     
27718     onViewOver : function(e, t){
27719         // do nothing...
27720         return;
27721         
27722     },
27723     
27724     onViewClick : function(doFocus,index){
27725         return;
27726         
27727     },
27728     select: function () {
27729         //Roo.log("SELECT CALLED");
27730     },
27731      
27732     selectByValue : function(xv, scrollIntoView){
27733         var ar = this.getValueArray();
27734         var sels = [];
27735         
27736         Roo.each(ar, function(v) {
27737             if(v === undefined || v === null){
27738                 return;
27739             }
27740             var r = this.findRecord(this.valueField, v);
27741             if(r){
27742                 sels.push(this.store.indexOf(r))
27743                 
27744             }
27745         },this);
27746         this.view.select(sels);
27747         return false;
27748     },
27749     
27750     
27751     
27752     onSelect : function(record, index){
27753        // Roo.log("onselect Called");
27754        // this is only called by the clear button now..
27755         this.view.clearSelections();
27756         this.setValue('[]');
27757         if (this.value != this.valueBefore) {
27758             this.fireEvent('change', this, this.value, this.valueBefore);
27759             this.valueBefore = this.value;
27760         }
27761     },
27762     getValueArray : function()
27763     {
27764         var ar = [] ;
27765         
27766         try {
27767             //Roo.log(this.value);
27768             if (typeof(this.value) == 'undefined') {
27769                 return [];
27770             }
27771             var ar = Roo.decode(this.value);
27772             return  ar instanceof Array ? ar : []; //?? valid?
27773             
27774         } catch(e) {
27775             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27776             return [];
27777         }
27778          
27779     },
27780     expand : function ()
27781     {
27782         
27783         Roo.form.ComboCheck.superclass.expand.call(this);
27784         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27785         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27786         
27787
27788     },
27789     
27790     collapse : function(){
27791         Roo.form.ComboCheck.superclass.collapse.call(this);
27792         var sl = this.view.getSelectedIndexes();
27793         var st = this.store;
27794         var nv = [];
27795         var tv = [];
27796         var r;
27797         Roo.each(sl, function(i) {
27798             r = st.getAt(i);
27799             nv.push(r.get(this.valueField));
27800         },this);
27801         this.setValue(Roo.encode(nv));
27802         if (this.value != this.valueBefore) {
27803
27804             this.fireEvent('change', this, this.value, this.valueBefore);
27805             this.valueBefore = this.value;
27806         }
27807         
27808     },
27809     
27810     setValue : function(v){
27811         // Roo.log(v);
27812         this.value = v;
27813         
27814         var vals = this.getValueArray();
27815         var tv = [];
27816         Roo.each(vals, function(k) {
27817             var r = this.findRecord(this.valueField, k);
27818             if(r){
27819                 tv.push(r.data[this.displayField]);
27820             }else if(this.valueNotFoundText !== undefined){
27821                 tv.push( this.valueNotFoundText );
27822             }
27823         },this);
27824        // Roo.log(tv);
27825         
27826         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27827         this.hiddenField.value = v;
27828         this.value = v;
27829     }
27830     
27831 });/*
27832  * Based on:
27833  * Ext JS Library 1.1.1
27834  * Copyright(c) 2006-2007, Ext JS, LLC.
27835  *
27836  * Originally Released Under LGPL - original licence link has changed is not relivant.
27837  *
27838  * Fork - LGPL
27839  * <script type="text/javascript">
27840  */
27841  
27842 /**
27843  * @class Roo.form.Signature
27844  * @extends Roo.form.Field
27845  * Signature field.  
27846  * @constructor
27847  * 
27848  * @param {Object} config Configuration options
27849  */
27850
27851 Roo.form.Signature = function(config){
27852     Roo.form.Signature.superclass.constructor.call(this, config);
27853     
27854     this.addEvents({// not in used??
27855          /**
27856          * @event confirm
27857          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27858              * @param {Roo.form.Signature} combo This combo box
27859              */
27860         'confirm' : true,
27861         /**
27862          * @event reset
27863          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27864              * @param {Roo.form.ComboBox} combo This combo box
27865              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27866              */
27867         'reset' : true
27868     });
27869 };
27870
27871 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27872     /**
27873      * @cfg {Object} labels Label to use when rendering a form.
27874      * defaults to 
27875      * labels : { 
27876      *      clear : "Clear",
27877      *      confirm : "Confirm"
27878      *  }
27879      */
27880     labels : { 
27881         clear : "Clear",
27882         confirm : "Confirm"
27883     },
27884     /**
27885      * @cfg {Number} width The signature panel width (defaults to 300)
27886      */
27887     width: 300,
27888     /**
27889      * @cfg {Number} height The signature panel height (defaults to 100)
27890      */
27891     height : 100,
27892     /**
27893      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27894      */
27895     allowBlank : false,
27896     
27897     //private
27898     // {Object} signPanel The signature SVG panel element (defaults to {})
27899     signPanel : {},
27900     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27901     isMouseDown : false,
27902     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27903     isConfirmed : false,
27904     // {String} signatureTmp SVG mapping string (defaults to empty string)
27905     signatureTmp : '',
27906     
27907     
27908     defaultAutoCreate : { // modified by initCompnoent..
27909         tag: "input",
27910         type:"hidden"
27911     },
27912
27913     // private
27914     onRender : function(ct, position){
27915         
27916         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27917         
27918         this.wrap = this.el.wrap({
27919             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27920         });
27921         
27922         this.createToolbar(this);
27923         this.signPanel = this.wrap.createChild({
27924                 tag: 'div',
27925                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27926             }, this.el
27927         );
27928             
27929         this.svgID = Roo.id();
27930         this.svgEl = this.signPanel.createChild({
27931               xmlns : 'http://www.w3.org/2000/svg',
27932               tag : 'svg',
27933               id : this.svgID + "-svg",
27934               width: this.width,
27935               height: this.height,
27936               viewBox: '0 0 '+this.width+' '+this.height,
27937               cn : [
27938                 {
27939                     tag: "rect",
27940                     id: this.svgID + "-svg-r",
27941                     width: this.width,
27942                     height: this.height,
27943                     fill: "#ffa"
27944                 },
27945                 {
27946                     tag: "line",
27947                     id: this.svgID + "-svg-l",
27948                     x1: "0", // start
27949                     y1: (this.height*0.8), // start set the line in 80% of height
27950                     x2: this.width, // end
27951                     y2: (this.height*0.8), // end set the line in 80% of height
27952                     'stroke': "#666",
27953                     'stroke-width': "1",
27954                     'stroke-dasharray': "3",
27955                     'shape-rendering': "crispEdges",
27956                     'pointer-events': "none"
27957                 },
27958                 {
27959                     tag: "path",
27960                     id: this.svgID + "-svg-p",
27961                     'stroke': "navy",
27962                     'stroke-width': "3",
27963                     'fill': "none",
27964                     'pointer-events': 'none'
27965                 }
27966               ]
27967         });
27968         this.createSVG();
27969         this.svgBox = this.svgEl.dom.getScreenCTM();
27970     },
27971     createSVG : function(){ 
27972         var svg = this.signPanel;
27973         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27974         var t = this;
27975
27976         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27977         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27978         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27979         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27980         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27981         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27982         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27983         
27984     },
27985     isTouchEvent : function(e){
27986         return e.type.match(/^touch/);
27987     },
27988     getCoords : function (e) {
27989         var pt    = this.svgEl.dom.createSVGPoint();
27990         pt.x = e.clientX; 
27991         pt.y = e.clientY;
27992         if (this.isTouchEvent(e)) {
27993             pt.x =  e.targetTouches[0].clientX;
27994             pt.y = e.targetTouches[0].clientY;
27995         }
27996         var a = this.svgEl.dom.getScreenCTM();
27997         var b = a.inverse();
27998         var mx = pt.matrixTransform(b);
27999         return mx.x + ',' + mx.y;
28000     },
28001     //mouse event headler 
28002     down : function (e) {
28003         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
28004         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
28005         
28006         this.isMouseDown = true;
28007         
28008         e.preventDefault();
28009     },
28010     move : function (e) {
28011         if (this.isMouseDown) {
28012             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
28013             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
28014         }
28015         
28016         e.preventDefault();
28017     },
28018     up : function (e) {
28019         this.isMouseDown = false;
28020         var sp = this.signatureTmp.split(' ');
28021         
28022         if(sp.length > 1){
28023             if(!sp[sp.length-2].match(/^L/)){
28024                 sp.pop();
28025                 sp.pop();
28026                 sp.push("");
28027                 this.signatureTmp = sp.join(" ");
28028             }
28029         }
28030         if(this.getValue() != this.signatureTmp){
28031             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28032             this.isConfirmed = false;
28033         }
28034         e.preventDefault();
28035     },
28036     
28037     /**
28038      * Protected method that will not generally be called directly. It
28039      * is called when the editor creates its toolbar. Override this method if you need to
28040      * add custom toolbar buttons.
28041      * @param {HtmlEditor} editor
28042      */
28043     createToolbar : function(editor){
28044          function btn(id, toggle, handler){
28045             var xid = fid + '-'+ id ;
28046             return {
28047                 id : xid,
28048                 cmd : id,
28049                 cls : 'x-btn-icon x-edit-'+id,
28050                 enableToggle:toggle !== false,
28051                 scope: editor, // was editor...
28052                 handler:handler||editor.relayBtnCmd,
28053                 clickEvent:'mousedown',
28054                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28055                 tabIndex:-1
28056             };
28057         }
28058         
28059         
28060         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28061         this.tb = tb;
28062         this.tb.add(
28063            {
28064                 cls : ' x-signature-btn x-signature-'+id,
28065                 scope: editor, // was editor...
28066                 handler: this.reset,
28067                 clickEvent:'mousedown',
28068                 text: this.labels.clear
28069             },
28070             {
28071                  xtype : 'Fill',
28072                  xns: Roo.Toolbar
28073             }, 
28074             {
28075                 cls : '  x-signature-btn x-signature-'+id,
28076                 scope: editor, // was editor...
28077                 handler: this.confirmHandler,
28078                 clickEvent:'mousedown',
28079                 text: this.labels.confirm
28080             }
28081         );
28082     
28083     },
28084     //public
28085     /**
28086      * when user is clicked confirm then show this image.....
28087      * 
28088      * @return {String} Image Data URI
28089      */
28090     getImageDataURI : function(){
28091         var svg = this.svgEl.dom.parentNode.innerHTML;
28092         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28093         return src; 
28094     },
28095     /**
28096      * 
28097      * @return {Boolean} this.isConfirmed
28098      */
28099     getConfirmed : function(){
28100         return this.isConfirmed;
28101     },
28102     /**
28103      * 
28104      * @return {Number} this.width
28105      */
28106     getWidth : function(){
28107         return this.width;
28108     },
28109     /**
28110      * 
28111      * @return {Number} this.height
28112      */
28113     getHeight : function(){
28114         return this.height;
28115     },
28116     // private
28117     getSignature : function(){
28118         return this.signatureTmp;
28119     },
28120     // private
28121     reset : function(){
28122         this.signatureTmp = '';
28123         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28124         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28125         this.isConfirmed = false;
28126         Roo.form.Signature.superclass.reset.call(this);
28127     },
28128     setSignature : function(s){
28129         this.signatureTmp = s;
28130         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28131         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28132         this.setValue(s);
28133         this.isConfirmed = false;
28134         Roo.form.Signature.superclass.reset.call(this);
28135     }, 
28136     test : function(){
28137 //        Roo.log(this.signPanel.dom.contentWindow.up())
28138     },
28139     //private
28140     setConfirmed : function(){
28141         
28142         
28143         
28144 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28145     },
28146     // private
28147     confirmHandler : function(){
28148         if(!this.getSignature()){
28149             return;
28150         }
28151         
28152         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28153         this.setValue(this.getSignature());
28154         this.isConfirmed = true;
28155         
28156         this.fireEvent('confirm', this);
28157     },
28158     // private
28159     // Subclasses should provide the validation implementation by overriding this
28160     validateValue : function(value){
28161         if(this.allowBlank){
28162             return true;
28163         }
28164         
28165         if(this.isConfirmed){
28166             return true;
28167         }
28168         return false;
28169     }
28170 });/*
28171  * Based on:
28172  * Ext JS Library 1.1.1
28173  * Copyright(c) 2006-2007, Ext JS, LLC.
28174  *
28175  * Originally Released Under LGPL - original licence link has changed is not relivant.
28176  *
28177  * Fork - LGPL
28178  * <script type="text/javascript">
28179  */
28180  
28181
28182 /**
28183  * @class Roo.form.ComboBox
28184  * @extends Roo.form.TriggerField
28185  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28186  * @constructor
28187  * Create a new ComboBox.
28188  * @param {Object} config Configuration options
28189  */
28190 Roo.form.Select = function(config){
28191     Roo.form.Select.superclass.constructor.call(this, config);
28192      
28193 };
28194
28195 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28196     /**
28197      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28198      */
28199     /**
28200      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28201      * rendering into an Roo.Editor, defaults to false)
28202      */
28203     /**
28204      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28205      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28206      */
28207     /**
28208      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28209      */
28210     /**
28211      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28212      * the dropdown list (defaults to undefined, with no header element)
28213      */
28214
28215      /**
28216      * @cfg {String/Roo.Template} tpl The template to use to render the output
28217      */
28218      
28219     // private
28220     defaultAutoCreate : {tag: "select"  },
28221     /**
28222      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28223      */
28224     listWidth: undefined,
28225     /**
28226      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28227      * mode = 'remote' or 'text' if mode = 'local')
28228      */
28229     displayField: undefined,
28230     /**
28231      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28232      * mode = 'remote' or 'value' if mode = 'local'). 
28233      * Note: use of a valueField requires the user make a selection
28234      * in order for a value to be mapped.
28235      */
28236     valueField: undefined,
28237     
28238     
28239     /**
28240      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28241      * field's data value (defaults to the underlying DOM element's name)
28242      */
28243     hiddenName: undefined,
28244     /**
28245      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28246      */
28247     listClass: '',
28248     /**
28249      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28250      */
28251     selectedClass: 'x-combo-selected',
28252     /**
28253      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28254      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28255      * which displays a downward arrow icon).
28256      */
28257     triggerClass : 'x-form-arrow-trigger',
28258     /**
28259      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28260      */
28261     shadow:'sides',
28262     /**
28263      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28264      * anchor positions (defaults to 'tl-bl')
28265      */
28266     listAlign: 'tl-bl?',
28267     /**
28268      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28269      */
28270     maxHeight: 300,
28271     /**
28272      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28273      * query specified by the allQuery config option (defaults to 'query')
28274      */
28275     triggerAction: 'query',
28276     /**
28277      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28278      * (defaults to 4, does not apply if editable = false)
28279      */
28280     minChars : 4,
28281     /**
28282      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28283      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28284      */
28285     typeAhead: false,
28286     /**
28287      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28288      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28289      */
28290     queryDelay: 500,
28291     /**
28292      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28293      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28294      */
28295     pageSize: 0,
28296     /**
28297      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28298      * when editable = true (defaults to false)
28299      */
28300     selectOnFocus:false,
28301     /**
28302      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28303      */
28304     queryParam: 'query',
28305     /**
28306      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28307      * when mode = 'remote' (defaults to 'Loading...')
28308      */
28309     loadingText: 'Loading...',
28310     /**
28311      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28312      */
28313     resizable: false,
28314     /**
28315      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28316      */
28317     handleHeight : 8,
28318     /**
28319      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28320      * traditional select (defaults to true)
28321      */
28322     editable: true,
28323     /**
28324      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28325      */
28326     allQuery: '',
28327     /**
28328      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28329      */
28330     mode: 'remote',
28331     /**
28332      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28333      * listWidth has a higher value)
28334      */
28335     minListWidth : 70,
28336     /**
28337      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28338      * allow the user to set arbitrary text into the field (defaults to false)
28339      */
28340     forceSelection:false,
28341     /**
28342      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28343      * if typeAhead = true (defaults to 250)
28344      */
28345     typeAheadDelay : 250,
28346     /**
28347      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28348      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28349      */
28350     valueNotFoundText : undefined,
28351     
28352     /**
28353      * @cfg {String} defaultValue The value displayed after loading the store.
28354      */
28355     defaultValue: '',
28356     
28357     /**
28358      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28359      */
28360     blockFocus : false,
28361     
28362     /**
28363      * @cfg {Boolean} disableClear Disable showing of clear button.
28364      */
28365     disableClear : false,
28366     /**
28367      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28368      */
28369     alwaysQuery : false,
28370     
28371     //private
28372     addicon : false,
28373     editicon: false,
28374     
28375     // element that contains real text value.. (when hidden is used..)
28376      
28377     // private
28378     onRender : function(ct, position){
28379         Roo.form.Field.prototype.onRender.call(this, ct, position);
28380         
28381         if(this.store){
28382             this.store.on('beforeload', this.onBeforeLoad, this);
28383             this.store.on('load', this.onLoad, this);
28384             this.store.on('loadexception', this.onLoadException, this);
28385             this.store.load({});
28386         }
28387         
28388         
28389         
28390     },
28391
28392     // private
28393     initEvents : function(){
28394         //Roo.form.ComboBox.superclass.initEvents.call(this);
28395  
28396     },
28397
28398     onDestroy : function(){
28399        
28400         if(this.store){
28401             this.store.un('beforeload', this.onBeforeLoad, this);
28402             this.store.un('load', this.onLoad, this);
28403             this.store.un('loadexception', this.onLoadException, this);
28404         }
28405         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28406     },
28407
28408     // private
28409     fireKey : function(e){
28410         if(e.isNavKeyPress() && !this.list.isVisible()){
28411             this.fireEvent("specialkey", this, e);
28412         }
28413     },
28414
28415     // private
28416     onResize: function(w, h){
28417         
28418         return; 
28419     
28420         
28421     },
28422
28423     /**
28424      * Allow or prevent the user from directly editing the field text.  If false is passed,
28425      * the user will only be able to select from the items defined in the dropdown list.  This method
28426      * is the runtime equivalent of setting the 'editable' config option at config time.
28427      * @param {Boolean} value True to allow the user to directly edit the field text
28428      */
28429     setEditable : function(value){
28430          
28431     },
28432
28433     // private
28434     onBeforeLoad : function(){
28435         
28436         Roo.log("Select before load");
28437         return;
28438     
28439         this.innerList.update(this.loadingText ?
28440                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28441         //this.restrictHeight();
28442         this.selectedIndex = -1;
28443     },
28444
28445     // private
28446     onLoad : function(){
28447
28448     
28449         var dom = this.el.dom;
28450         dom.innerHTML = '';
28451          var od = dom.ownerDocument;
28452          
28453         if (this.emptyText) {
28454             var op = od.createElement('option');
28455             op.setAttribute('value', '');
28456             op.innerHTML = String.format('{0}', this.emptyText);
28457             dom.appendChild(op);
28458         }
28459         if(this.store.getCount() > 0){
28460            
28461             var vf = this.valueField;
28462             var df = this.displayField;
28463             this.store.data.each(function(r) {
28464                 // which colmsn to use... testing - cdoe / title..
28465                 var op = od.createElement('option');
28466                 op.setAttribute('value', r.data[vf]);
28467                 op.innerHTML = String.format('{0}', r.data[df]);
28468                 dom.appendChild(op);
28469             });
28470             if (typeof(this.defaultValue != 'undefined')) {
28471                 this.setValue(this.defaultValue);
28472             }
28473             
28474              
28475         }else{
28476             //this.onEmptyResults();
28477         }
28478         //this.el.focus();
28479     },
28480     // private
28481     onLoadException : function()
28482     {
28483         dom.innerHTML = '';
28484             
28485         Roo.log("Select on load exception");
28486         return;
28487     
28488         this.collapse();
28489         Roo.log(this.store.reader.jsonData);
28490         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28491             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28492         }
28493         
28494         
28495     },
28496     // private
28497     onTypeAhead : function(){
28498          
28499     },
28500
28501     // private
28502     onSelect : function(record, index){
28503         Roo.log('on select?');
28504         return;
28505         if(this.fireEvent('beforeselect', this, record, index) !== false){
28506             this.setFromData(index > -1 ? record.data : false);
28507             this.collapse();
28508             this.fireEvent('select', this, record, index);
28509         }
28510     },
28511
28512     /**
28513      * Returns the currently selected field value or empty string if no value is set.
28514      * @return {String} value The selected value
28515      */
28516     getValue : function(){
28517         var dom = this.el.dom;
28518         this.value = dom.options[dom.selectedIndex].value;
28519         return this.value;
28520         
28521     },
28522
28523     /**
28524      * Clears any text/value currently set in the field
28525      */
28526     clearValue : function(){
28527         this.value = '';
28528         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28529         
28530     },
28531
28532     /**
28533      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28534      * will be displayed in the field.  If the value does not match the data value of an existing item,
28535      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28536      * Otherwise the field will be blank (although the value will still be set).
28537      * @param {String} value The value to match
28538      */
28539     setValue : function(v){
28540         var d = this.el.dom;
28541         for (var i =0; i < d.options.length;i++) {
28542             if (v == d.options[i].value) {
28543                 d.selectedIndex = i;
28544                 this.value = v;
28545                 return;
28546             }
28547         }
28548         this.clearValue();
28549     },
28550     /**
28551      * @property {Object} the last set data for the element
28552      */
28553     
28554     lastData : false,
28555     /**
28556      * Sets the value of the field based on a object which is related to the record format for the store.
28557      * @param {Object} value the value to set as. or false on reset?
28558      */
28559     setFromData : function(o){
28560         Roo.log('setfrom data?');
28561          
28562         
28563         
28564     },
28565     // private
28566     reset : function(){
28567         this.clearValue();
28568     },
28569     // private
28570     findRecord : function(prop, value){
28571         
28572         return false;
28573     
28574         var record;
28575         if(this.store.getCount() > 0){
28576             this.store.each(function(r){
28577                 if(r.data[prop] == value){
28578                     record = r;
28579                     return false;
28580                 }
28581                 return true;
28582             });
28583         }
28584         return record;
28585     },
28586     
28587     getName: function()
28588     {
28589         // returns hidden if it's set..
28590         if (!this.rendered) {return ''};
28591         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28592         
28593     },
28594      
28595
28596     
28597
28598     // private
28599     onEmptyResults : function(){
28600         Roo.log('empty results');
28601         //this.collapse();
28602     },
28603
28604     /**
28605      * Returns true if the dropdown list is expanded, else false.
28606      */
28607     isExpanded : function(){
28608         return false;
28609     },
28610
28611     /**
28612      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28613      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28614      * @param {String} value The data value of the item to select
28615      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28616      * selected item if it is not currently in view (defaults to true)
28617      * @return {Boolean} True if the value matched an item in the list, else false
28618      */
28619     selectByValue : function(v, scrollIntoView){
28620         Roo.log('select By Value');
28621         return false;
28622     
28623         if(v !== undefined && v !== null){
28624             var r = this.findRecord(this.valueField || this.displayField, v);
28625             if(r){
28626                 this.select(this.store.indexOf(r), scrollIntoView);
28627                 return true;
28628             }
28629         }
28630         return false;
28631     },
28632
28633     /**
28634      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28635      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28636      * @param {Number} index The zero-based index of the list item to select
28637      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28638      * selected item if it is not currently in view (defaults to true)
28639      */
28640     select : function(index, scrollIntoView){
28641         Roo.log('select ');
28642         return  ;
28643         
28644         this.selectedIndex = index;
28645         this.view.select(index);
28646         if(scrollIntoView !== false){
28647             var el = this.view.getNode(index);
28648             if(el){
28649                 this.innerList.scrollChildIntoView(el, false);
28650             }
28651         }
28652     },
28653
28654       
28655
28656     // private
28657     validateBlur : function(){
28658         
28659         return;
28660         
28661     },
28662
28663     // private
28664     initQuery : function(){
28665         this.doQuery(this.getRawValue());
28666     },
28667
28668     // private
28669     doForce : function(){
28670         if(this.el.dom.value.length > 0){
28671             this.el.dom.value =
28672                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28673              
28674         }
28675     },
28676
28677     /**
28678      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28679      * query allowing the query action to be canceled if needed.
28680      * @param {String} query The SQL query to execute
28681      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28682      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28683      * saved in the current store (defaults to false)
28684      */
28685     doQuery : function(q, forceAll){
28686         
28687         Roo.log('doQuery?');
28688         if(q === undefined || q === null){
28689             q = '';
28690         }
28691         var qe = {
28692             query: q,
28693             forceAll: forceAll,
28694             combo: this,
28695             cancel:false
28696         };
28697         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28698             return false;
28699         }
28700         q = qe.query;
28701         forceAll = qe.forceAll;
28702         if(forceAll === true || (q.length >= this.minChars)){
28703             if(this.lastQuery != q || this.alwaysQuery){
28704                 this.lastQuery = q;
28705                 if(this.mode == 'local'){
28706                     this.selectedIndex = -1;
28707                     if(forceAll){
28708                         this.store.clearFilter();
28709                     }else{
28710                         this.store.filter(this.displayField, q);
28711                     }
28712                     this.onLoad();
28713                 }else{
28714                     this.store.baseParams[this.queryParam] = q;
28715                     this.store.load({
28716                         params: this.getParams(q)
28717                     });
28718                     this.expand();
28719                 }
28720             }else{
28721                 this.selectedIndex = -1;
28722                 this.onLoad();   
28723             }
28724         }
28725     },
28726
28727     // private
28728     getParams : function(q){
28729         var p = {};
28730         //p[this.queryParam] = q;
28731         if(this.pageSize){
28732             p.start = 0;
28733             p.limit = this.pageSize;
28734         }
28735         return p;
28736     },
28737
28738     /**
28739      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28740      */
28741     collapse : function(){
28742         
28743     },
28744
28745     // private
28746     collapseIf : function(e){
28747         
28748     },
28749
28750     /**
28751      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28752      */
28753     expand : function(){
28754         
28755     } ,
28756
28757     // private
28758      
28759
28760     /** 
28761     * @cfg {Boolean} grow 
28762     * @hide 
28763     */
28764     /** 
28765     * @cfg {Number} growMin 
28766     * @hide 
28767     */
28768     /** 
28769     * @cfg {Number} growMax 
28770     * @hide 
28771     */
28772     /**
28773      * @hide
28774      * @method autoSize
28775      */
28776     
28777     setWidth : function()
28778     {
28779         
28780     },
28781     getResizeEl : function(){
28782         return this.el;
28783     }
28784 });//<script type="text/javasscript">
28785  
28786
28787 /**
28788  * @class Roo.DDView
28789  * A DnD enabled version of Roo.View.
28790  * @param {Element/String} container The Element in which to create the View.
28791  * @param {String} tpl The template string used to create the markup for each element of the View
28792  * @param {Object} config The configuration properties. These include all the config options of
28793  * {@link Roo.View} plus some specific to this class.<br>
28794  * <p>
28795  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28796  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28797  * <p>
28798  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28799 .x-view-drag-insert-above {
28800         border-top:1px dotted #3366cc;
28801 }
28802 .x-view-drag-insert-below {
28803         border-bottom:1px dotted #3366cc;
28804 }
28805 </code></pre>
28806  * 
28807  */
28808  
28809 Roo.DDView = function(container, tpl, config) {
28810     Roo.DDView.superclass.constructor.apply(this, arguments);
28811     this.getEl().setStyle("outline", "0px none");
28812     this.getEl().unselectable();
28813     if (this.dragGroup) {
28814                 this.setDraggable(this.dragGroup.split(","));
28815     }
28816     if (this.dropGroup) {
28817                 this.setDroppable(this.dropGroup.split(","));
28818     }
28819     if (this.deletable) {
28820         this.setDeletable();
28821     }
28822     this.isDirtyFlag = false;
28823         this.addEvents({
28824                 "drop" : true
28825         });
28826 };
28827
28828 Roo.extend(Roo.DDView, Roo.View, {
28829 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28830 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28831 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28832 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28833
28834         isFormField: true,
28835
28836         reset: Roo.emptyFn,
28837         
28838         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28839
28840         validate: function() {
28841                 return true;
28842         },
28843         
28844         destroy: function() {
28845                 this.purgeListeners();
28846                 this.getEl.removeAllListeners();
28847                 this.getEl().remove();
28848                 if (this.dragZone) {
28849                         if (this.dragZone.destroy) {
28850                                 this.dragZone.destroy();
28851                         }
28852                 }
28853                 if (this.dropZone) {
28854                         if (this.dropZone.destroy) {
28855                                 this.dropZone.destroy();
28856                         }
28857                 }
28858         },
28859
28860 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28861         getName: function() {
28862                 return this.name;
28863         },
28864
28865 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28866         setValue: function(v) {
28867                 if (!this.store) {
28868                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28869                 }
28870                 var data = {};
28871                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28872                 this.store.proxy = new Roo.data.MemoryProxy(data);
28873                 this.store.load();
28874         },
28875
28876 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28877         getValue: function() {
28878                 var result = '(';
28879                 this.store.each(function(rec) {
28880                         result += rec.id + ',';
28881                 });
28882                 return result.substr(0, result.length - 1) + ')';
28883         },
28884         
28885         getIds: function() {
28886                 var i = 0, result = new Array(this.store.getCount());
28887                 this.store.each(function(rec) {
28888                         result[i++] = rec.id;
28889                 });
28890                 return result;
28891         },
28892         
28893         isDirty: function() {
28894                 return this.isDirtyFlag;
28895         },
28896
28897 /**
28898  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28899  *      whole Element becomes the target, and this causes the drop gesture to append.
28900  */
28901     getTargetFromEvent : function(e) {
28902                 var target = e.getTarget();
28903                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28904                 target = target.parentNode;
28905                 }
28906                 if (!target) {
28907                         target = this.el.dom.lastChild || this.el.dom;
28908                 }
28909                 return target;
28910     },
28911
28912 /**
28913  *      Create the drag data which consists of an object which has the property "ddel" as
28914  *      the drag proxy element. 
28915  */
28916     getDragData : function(e) {
28917         var target = this.findItemFromChild(e.getTarget());
28918                 if(target) {
28919                         this.handleSelection(e);
28920                         var selNodes = this.getSelectedNodes();
28921             var dragData = {
28922                 source: this,
28923                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28924                 nodes: selNodes,
28925                 records: []
28926                         };
28927                         var selectedIndices = this.getSelectedIndexes();
28928                         for (var i = 0; i < selectedIndices.length; i++) {
28929                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28930                         }
28931                         if (selNodes.length == 1) {
28932                                 dragData.ddel = target.cloneNode(true); // the div element
28933                         } else {
28934                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28935                                 div.className = 'multi-proxy';
28936                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28937                                         div.appendChild(selNodes[i].cloneNode(true));
28938                                 }
28939                                 dragData.ddel = div;
28940                         }
28941             //console.log(dragData)
28942             //console.log(dragData.ddel.innerHTML)
28943                         return dragData;
28944                 }
28945         //console.log('nodragData')
28946                 return false;
28947     },
28948     
28949 /**     Specify to which ddGroup items in this DDView may be dragged. */
28950     setDraggable: function(ddGroup) {
28951         if (ddGroup instanceof Array) {
28952                 Roo.each(ddGroup, this.setDraggable, this);
28953                 return;
28954         }
28955         if (this.dragZone) {
28956                 this.dragZone.addToGroup(ddGroup);
28957         } else {
28958                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28959                                 containerScroll: true,
28960                                 ddGroup: ddGroup 
28961
28962                         });
28963 //                      Draggability implies selection. DragZone's mousedown selects the element.
28964                         if (!this.multiSelect) { this.singleSelect = true; }
28965
28966 //                      Wire the DragZone's handlers up to methods in *this*
28967                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28968                 }
28969     },
28970
28971 /**     Specify from which ddGroup this DDView accepts drops. */
28972     setDroppable: function(ddGroup) {
28973         if (ddGroup instanceof Array) {
28974                 Roo.each(ddGroup, this.setDroppable, this);
28975                 return;
28976         }
28977         if (this.dropZone) {
28978                 this.dropZone.addToGroup(ddGroup);
28979         } else {
28980                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28981                                 containerScroll: true,
28982                                 ddGroup: ddGroup
28983                         });
28984
28985 //                      Wire the DropZone's handlers up to methods in *this*
28986                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28987                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28988                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28989                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28990                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28991                 }
28992     },
28993
28994 /**     Decide whether to drop above or below a View node. */
28995     getDropPoint : function(e, n, dd){
28996         if (n == this.el.dom) { return "above"; }
28997                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28998                 var c = t + (b - t) / 2;
28999                 var y = Roo.lib.Event.getPageY(e);
29000                 if(y <= c) {
29001                         return "above";
29002                 }else{
29003                         return "below";
29004                 }
29005     },
29006
29007     onNodeEnter : function(n, dd, e, data){
29008                 return false;
29009     },
29010     
29011     onNodeOver : function(n, dd, e, data){
29012                 var pt = this.getDropPoint(e, n, dd);
29013                 // set the insert point style on the target node
29014                 var dragElClass = this.dropNotAllowed;
29015                 if (pt) {
29016                         var targetElClass;
29017                         if (pt == "above"){
29018                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29019                                 targetElClass = "x-view-drag-insert-above";
29020                         } else {
29021                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29022                                 targetElClass = "x-view-drag-insert-below";
29023                         }
29024                         if (this.lastInsertClass != targetElClass){
29025                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29026                                 this.lastInsertClass = targetElClass;
29027                         }
29028                 }
29029                 return dragElClass;
29030         },
29031
29032     onNodeOut : function(n, dd, e, data){
29033                 this.removeDropIndicators(n);
29034     },
29035
29036     onNodeDrop : function(n, dd, e, data){
29037         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29038                 return false;
29039         }
29040         var pt = this.getDropPoint(e, n, dd);
29041                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29042                 if (pt == "below") { insertAt++; }
29043                 for (var i = 0; i < data.records.length; i++) {
29044                         var r = data.records[i];
29045                         var dup = this.store.getById(r.id);
29046                         if (dup && (dd != this.dragZone)) {
29047                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29048                         } else {
29049                                 if (data.copy) {
29050                                         this.store.insert(insertAt++, r.copy());
29051                                 } else {
29052                                         data.source.isDirtyFlag = true;
29053                                         r.store.remove(r);
29054                                         this.store.insert(insertAt++, r);
29055                                 }
29056                                 this.isDirtyFlag = true;
29057                         }
29058                 }
29059                 this.dragZone.cachedTarget = null;
29060                 return true;
29061     },
29062
29063     removeDropIndicators : function(n){
29064                 if(n){
29065                         Roo.fly(n).removeClass([
29066                                 "x-view-drag-insert-above",
29067                                 "x-view-drag-insert-below"]);
29068                         this.lastInsertClass = "_noclass";
29069                 }
29070     },
29071
29072 /**
29073  *      Utility method. Add a delete option to the DDView's context menu.
29074  *      @param {String} imageUrl The URL of the "delete" icon image.
29075  */
29076         setDeletable: function(imageUrl) {
29077                 if (!this.singleSelect && !this.multiSelect) {
29078                         this.singleSelect = true;
29079                 }
29080                 var c = this.getContextMenu();
29081                 this.contextMenu.on("itemclick", function(item) {
29082                         switch (item.id) {
29083                                 case "delete":
29084                                         this.remove(this.getSelectedIndexes());
29085                                         break;
29086                         }
29087                 }, this);
29088                 this.contextMenu.add({
29089                         icon: imageUrl,
29090                         id: "delete",
29091                         text: 'Delete'
29092                 });
29093         },
29094         
29095 /**     Return the context menu for this DDView. */
29096         getContextMenu: function() {
29097                 if (!this.contextMenu) {
29098 //                      Create the View's context menu
29099                         this.contextMenu = new Roo.menu.Menu({
29100                                 id: this.id + "-contextmenu"
29101                         });
29102                         this.el.on("contextmenu", this.showContextMenu, this);
29103                 }
29104                 return this.contextMenu;
29105         },
29106         
29107         disableContextMenu: function() {
29108                 if (this.contextMenu) {
29109                         this.el.un("contextmenu", this.showContextMenu, this);
29110                 }
29111         },
29112
29113         showContextMenu: function(e, item) {
29114         item = this.findItemFromChild(e.getTarget());
29115                 if (item) {
29116                         e.stopEvent();
29117                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29118                         this.contextMenu.showAt(e.getXY());
29119             }
29120     },
29121
29122 /**
29123  *      Remove {@link Roo.data.Record}s at the specified indices.
29124  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29125  */
29126     remove: function(selectedIndices) {
29127                 selectedIndices = [].concat(selectedIndices);
29128                 for (var i = 0; i < selectedIndices.length; i++) {
29129                         var rec = this.store.getAt(selectedIndices[i]);
29130                         this.store.remove(rec);
29131                 }
29132     },
29133
29134 /**
29135  *      Double click fires the event, but also, if this is draggable, and there is only one other
29136  *      related DropZone, it transfers the selected node.
29137  */
29138     onDblClick : function(e){
29139         var item = this.findItemFromChild(e.getTarget());
29140         if(item){
29141             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29142                 return false;
29143             }
29144             if (this.dragGroup) {
29145                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29146                     while (targets.indexOf(this.dropZone) > -1) {
29147                             targets.remove(this.dropZone);
29148                                 }
29149                     if (targets.length == 1) {
29150                                         this.dragZone.cachedTarget = null;
29151                         var el = Roo.get(targets[0].getEl());
29152                         var box = el.getBox(true);
29153                         targets[0].onNodeDrop(el.dom, {
29154                                 target: el.dom,
29155                                 xy: [box.x, box.y + box.height - 1]
29156                         }, null, this.getDragData(e));
29157                     }
29158                 }
29159         }
29160     },
29161     
29162     handleSelection: function(e) {
29163                 this.dragZone.cachedTarget = null;
29164         var item = this.findItemFromChild(e.getTarget());
29165         if (!item) {
29166                 this.clearSelections(true);
29167                 return;
29168         }
29169                 if (item && (this.multiSelect || this.singleSelect)){
29170                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29171                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29172                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29173                                 this.unselect(item);
29174                         } else {
29175                                 this.select(item, this.multiSelect && e.ctrlKey);
29176                                 this.lastSelection = item;
29177                         }
29178                 }
29179     },
29180
29181     onItemClick : function(item, index, e){
29182                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29183                         return false;
29184                 }
29185                 return true;
29186     },
29187
29188     unselect : function(nodeInfo, suppressEvent){
29189                 var node = this.getNode(nodeInfo);
29190                 if(node && this.isSelected(node)){
29191                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29192                                 Roo.fly(node).removeClass(this.selectedClass);
29193                                 this.selections.remove(node);
29194                                 if(!suppressEvent){
29195                                         this.fireEvent("selectionchange", this, this.selections);
29196                                 }
29197                         }
29198                 }
29199     }
29200 });
29201 /*
29202  * Based on:
29203  * Ext JS Library 1.1.1
29204  * Copyright(c) 2006-2007, Ext JS, LLC.
29205  *
29206  * Originally Released Under LGPL - original licence link has changed is not relivant.
29207  *
29208  * Fork - LGPL
29209  * <script type="text/javascript">
29210  */
29211  
29212 /**
29213  * @class Roo.LayoutManager
29214  * @extends Roo.util.Observable
29215  * Base class for layout managers.
29216  */
29217 Roo.LayoutManager = function(container, config){
29218     Roo.LayoutManager.superclass.constructor.call(this);
29219     this.el = Roo.get(container);
29220     // ie scrollbar fix
29221     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29222         document.body.scroll = "no";
29223     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29224         this.el.position('relative');
29225     }
29226     this.id = this.el.id;
29227     this.el.addClass("x-layout-container");
29228     /** false to disable window resize monitoring @type Boolean */
29229     this.monitorWindowResize = true;
29230     this.regions = {};
29231     this.addEvents({
29232         /**
29233          * @event layout
29234          * Fires when a layout is performed. 
29235          * @param {Roo.LayoutManager} this
29236          */
29237         "layout" : true,
29238         /**
29239          * @event regionresized
29240          * Fires when the user resizes a region. 
29241          * @param {Roo.LayoutRegion} region The resized region
29242          * @param {Number} newSize The new size (width for east/west, height for north/south)
29243          */
29244         "regionresized" : true,
29245         /**
29246          * @event regioncollapsed
29247          * Fires when a region is collapsed. 
29248          * @param {Roo.LayoutRegion} region The collapsed region
29249          */
29250         "regioncollapsed" : true,
29251         /**
29252          * @event regionexpanded
29253          * Fires when a region is expanded.  
29254          * @param {Roo.LayoutRegion} region The expanded region
29255          */
29256         "regionexpanded" : true
29257     });
29258     this.updating = false;
29259     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29260 };
29261
29262 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29263     /**
29264      * Returns true if this layout is currently being updated
29265      * @return {Boolean}
29266      */
29267     isUpdating : function(){
29268         return this.updating; 
29269     },
29270     
29271     /**
29272      * Suspend the LayoutManager from doing auto-layouts while
29273      * making multiple add or remove calls
29274      */
29275     beginUpdate : function(){
29276         this.updating = true;    
29277     },
29278     
29279     /**
29280      * Restore auto-layouts and optionally disable the manager from performing a layout
29281      * @param {Boolean} noLayout true to disable a layout update 
29282      */
29283     endUpdate : function(noLayout){
29284         this.updating = false;
29285         if(!noLayout){
29286             this.layout();
29287         }    
29288     },
29289     
29290     layout: function(){
29291         
29292     },
29293     
29294     onRegionResized : function(region, newSize){
29295         this.fireEvent("regionresized", region, newSize);
29296         this.layout();
29297     },
29298     
29299     onRegionCollapsed : function(region){
29300         this.fireEvent("regioncollapsed", region);
29301     },
29302     
29303     onRegionExpanded : function(region){
29304         this.fireEvent("regionexpanded", region);
29305     },
29306         
29307     /**
29308      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29309      * performs box-model adjustments.
29310      * @return {Object} The size as an object {width: (the width), height: (the height)}
29311      */
29312     getViewSize : function(){
29313         var size;
29314         if(this.el.dom != document.body){
29315             size = this.el.getSize();
29316         }else{
29317             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29318         }
29319         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29320         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29321         return size;
29322     },
29323     
29324     /**
29325      * Returns the Element this layout is bound to.
29326      * @return {Roo.Element}
29327      */
29328     getEl : function(){
29329         return this.el;
29330     },
29331     
29332     /**
29333      * Returns the specified region.
29334      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29335      * @return {Roo.LayoutRegion}
29336      */
29337     getRegion : function(target){
29338         return this.regions[target.toLowerCase()];
29339     },
29340     
29341     onWindowResize : function(){
29342         if(this.monitorWindowResize){
29343             this.layout();
29344         }
29345     }
29346 });/*
29347  * Based on:
29348  * Ext JS Library 1.1.1
29349  * Copyright(c) 2006-2007, Ext JS, LLC.
29350  *
29351  * Originally Released Under LGPL - original licence link has changed is not relivant.
29352  *
29353  * Fork - LGPL
29354  * <script type="text/javascript">
29355  */
29356 /**
29357  * @class Roo.BorderLayout
29358  * @extends Roo.LayoutManager
29359  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29360  * please see: <br><br>
29361  * <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>
29362  * <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>
29363  * Example:
29364  <pre><code>
29365  var layout = new Roo.BorderLayout(document.body, {
29366     north: {
29367         initialSize: 25,
29368         titlebar: false
29369     },
29370     west: {
29371         split:true,
29372         initialSize: 200,
29373         minSize: 175,
29374         maxSize: 400,
29375         titlebar: true,
29376         collapsible: true
29377     },
29378     east: {
29379         split:true,
29380         initialSize: 202,
29381         minSize: 175,
29382         maxSize: 400,
29383         titlebar: true,
29384         collapsible: true
29385     },
29386     south: {
29387         split:true,
29388         initialSize: 100,
29389         minSize: 100,
29390         maxSize: 200,
29391         titlebar: true,
29392         collapsible: true
29393     },
29394     center: {
29395         titlebar: true,
29396         autoScroll:true,
29397         resizeTabs: true,
29398         minTabWidth: 50,
29399         preferredTabWidth: 150
29400     }
29401 });
29402
29403 // shorthand
29404 var CP = Roo.ContentPanel;
29405
29406 layout.beginUpdate();
29407 layout.add("north", new CP("north", "North"));
29408 layout.add("south", new CP("south", {title: "South", closable: true}));
29409 layout.add("west", new CP("west", {title: "West"}));
29410 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29411 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29412 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29413 layout.getRegion("center").showPanel("center1");
29414 layout.endUpdate();
29415 </code></pre>
29416
29417 <b>The container the layout is rendered into can be either the body element or any other element.
29418 If it is not the body element, the container needs to either be an absolute positioned element,
29419 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29420 the container size if it is not the body element.</b>
29421
29422 * @constructor
29423 * Create a new BorderLayout
29424 * @param {String/HTMLElement/Element} container The container this layout is bound to
29425 * @param {Object} config Configuration options
29426  */
29427 Roo.BorderLayout = function(container, config){
29428     config = config || {};
29429     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29430     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29431     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29432         var target = this.factory.validRegions[i];
29433         if(config[target]){
29434             this.addRegion(target, config[target]);
29435         }
29436     }
29437 };
29438
29439 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29440     /**
29441      * Creates and adds a new region if it doesn't already exist.
29442      * @param {String} target The target region key (north, south, east, west or center).
29443      * @param {Object} config The regions config object
29444      * @return {BorderLayoutRegion} The new region
29445      */
29446     addRegion : function(target, config){
29447         if(!this.regions[target]){
29448             var r = this.factory.create(target, this, config);
29449             this.bindRegion(target, r);
29450         }
29451         return this.regions[target];
29452     },
29453
29454     // private (kinda)
29455     bindRegion : function(name, r){
29456         this.regions[name] = r;
29457         r.on("visibilitychange", this.layout, this);
29458         r.on("paneladded", this.layout, this);
29459         r.on("panelremoved", this.layout, this);
29460         r.on("invalidated", this.layout, this);
29461         r.on("resized", this.onRegionResized, this);
29462         r.on("collapsed", this.onRegionCollapsed, this);
29463         r.on("expanded", this.onRegionExpanded, this);
29464     },
29465
29466     /**
29467      * Performs a layout update.
29468      */
29469     layout : function(){
29470         if(this.updating) {
29471             return;
29472         }
29473         var size = this.getViewSize();
29474         var w = size.width;
29475         var h = size.height;
29476         var centerW = w;
29477         var centerH = h;
29478         var centerY = 0;
29479         var centerX = 0;
29480         //var x = 0, y = 0;
29481
29482         var rs = this.regions;
29483         var north = rs["north"];
29484         var south = rs["south"]; 
29485         var west = rs["west"];
29486         var east = rs["east"];
29487         var center = rs["center"];
29488         //if(this.hideOnLayout){ // not supported anymore
29489             //c.el.setStyle("display", "none");
29490         //}
29491         if(north && north.isVisible()){
29492             var b = north.getBox();
29493             var m = north.getMargins();
29494             b.width = w - (m.left+m.right);
29495             b.x = m.left;
29496             b.y = m.top;
29497             centerY = b.height + b.y + m.bottom;
29498             centerH -= centerY;
29499             north.updateBox(this.safeBox(b));
29500         }
29501         if(south && south.isVisible()){
29502             var b = south.getBox();
29503             var m = south.getMargins();
29504             b.width = w - (m.left+m.right);
29505             b.x = m.left;
29506             var totalHeight = (b.height + m.top + m.bottom);
29507             b.y = h - totalHeight + m.top;
29508             centerH -= totalHeight;
29509             south.updateBox(this.safeBox(b));
29510         }
29511         if(west && west.isVisible()){
29512             var b = west.getBox();
29513             var m = west.getMargins();
29514             b.height = centerH - (m.top+m.bottom);
29515             b.x = m.left;
29516             b.y = centerY + m.top;
29517             var totalWidth = (b.width + m.left + m.right);
29518             centerX += totalWidth;
29519             centerW -= totalWidth;
29520             west.updateBox(this.safeBox(b));
29521         }
29522         if(east && east.isVisible()){
29523             var b = east.getBox();
29524             var m = east.getMargins();
29525             b.height = centerH - (m.top+m.bottom);
29526             var totalWidth = (b.width + m.left + m.right);
29527             b.x = w - totalWidth + m.left;
29528             b.y = centerY + m.top;
29529             centerW -= totalWidth;
29530             east.updateBox(this.safeBox(b));
29531         }
29532         if(center){
29533             var m = center.getMargins();
29534             var centerBox = {
29535                 x: centerX + m.left,
29536                 y: centerY + m.top,
29537                 width: centerW - (m.left+m.right),
29538                 height: centerH - (m.top+m.bottom)
29539             };
29540             //if(this.hideOnLayout){
29541                 //center.el.setStyle("display", "block");
29542             //}
29543             center.updateBox(this.safeBox(centerBox));
29544         }
29545         this.el.repaint();
29546         this.fireEvent("layout", this);
29547     },
29548
29549     // private
29550     safeBox : function(box){
29551         box.width = Math.max(0, box.width);
29552         box.height = Math.max(0, box.height);
29553         return box;
29554     },
29555
29556     /**
29557      * Adds a ContentPanel (or subclass) to this layout.
29558      * @param {String} target The target region key (north, south, east, west or center).
29559      * @param {Roo.ContentPanel} panel The panel to add
29560      * @return {Roo.ContentPanel} The added panel
29561      */
29562     add : function(target, panel){
29563          
29564         target = target.toLowerCase();
29565         return this.regions[target].add(panel);
29566     },
29567
29568     /**
29569      * Remove a ContentPanel (or subclass) to this layout.
29570      * @param {String} target The target region key (north, south, east, west or center).
29571      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29572      * @return {Roo.ContentPanel} The removed panel
29573      */
29574     remove : function(target, panel){
29575         target = target.toLowerCase();
29576         return this.regions[target].remove(panel);
29577     },
29578
29579     /**
29580      * Searches all regions for a panel with the specified id
29581      * @param {String} panelId
29582      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29583      */
29584     findPanel : function(panelId){
29585         var rs = this.regions;
29586         for(var target in rs){
29587             if(typeof rs[target] != "function"){
29588                 var p = rs[target].getPanel(panelId);
29589                 if(p){
29590                     return p;
29591                 }
29592             }
29593         }
29594         return null;
29595     },
29596
29597     /**
29598      * Searches all regions for a panel with the specified id and activates (shows) it.
29599      * @param {String/ContentPanel} panelId The panels id or the panel itself
29600      * @return {Roo.ContentPanel} The shown panel or null
29601      */
29602     showPanel : function(panelId) {
29603       var rs = this.regions;
29604       for(var target in rs){
29605          var r = rs[target];
29606          if(typeof r != "function"){
29607             if(r.hasPanel(panelId)){
29608                return r.showPanel(panelId);
29609             }
29610          }
29611       }
29612       return null;
29613    },
29614
29615    /**
29616      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29617      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29618      */
29619     restoreState : function(provider){
29620         if(!provider){
29621             provider = Roo.state.Manager;
29622         }
29623         var sm = new Roo.LayoutStateManager();
29624         sm.init(this, provider);
29625     },
29626
29627     /**
29628      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29629      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29630      * a valid ContentPanel config object.  Example:
29631      * <pre><code>
29632 // Create the main layout
29633 var layout = new Roo.BorderLayout('main-ct', {
29634     west: {
29635         split:true,
29636         minSize: 175,
29637         titlebar: true
29638     },
29639     center: {
29640         title:'Components'
29641     }
29642 }, 'main-ct');
29643
29644 // Create and add multiple ContentPanels at once via configs
29645 layout.batchAdd({
29646    west: {
29647        id: 'source-files',
29648        autoCreate:true,
29649        title:'Ext Source Files',
29650        autoScroll:true,
29651        fitToFrame:true
29652    },
29653    center : {
29654        el: cview,
29655        autoScroll:true,
29656        fitToFrame:true,
29657        toolbar: tb,
29658        resizeEl:'cbody'
29659    }
29660 });
29661 </code></pre>
29662      * @param {Object} regions An object containing ContentPanel configs by region name
29663      */
29664     batchAdd : function(regions){
29665         this.beginUpdate();
29666         for(var rname in regions){
29667             var lr = this.regions[rname];
29668             if(lr){
29669                 this.addTypedPanels(lr, regions[rname]);
29670             }
29671         }
29672         this.endUpdate();
29673     },
29674
29675     // private
29676     addTypedPanels : function(lr, ps){
29677         if(typeof ps == 'string'){
29678             lr.add(new Roo.ContentPanel(ps));
29679         }
29680         else if(ps instanceof Array){
29681             for(var i =0, len = ps.length; i < len; i++){
29682                 this.addTypedPanels(lr, ps[i]);
29683             }
29684         }
29685         else if(!ps.events){ // raw config?
29686             var el = ps.el;
29687             delete ps.el; // prevent conflict
29688             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29689         }
29690         else {  // panel object assumed!
29691             lr.add(ps);
29692         }
29693     },
29694     /**
29695      * Adds a xtype elements to the layout.
29696      * <pre><code>
29697
29698 layout.addxtype({
29699        xtype : 'ContentPanel',
29700        region: 'west',
29701        items: [ .... ]
29702    }
29703 );
29704
29705 layout.addxtype({
29706         xtype : 'NestedLayoutPanel',
29707         region: 'west',
29708         layout: {
29709            center: { },
29710            west: { }   
29711         },
29712         items : [ ... list of content panels or nested layout panels.. ]
29713    }
29714 );
29715 </code></pre>
29716      * @param {Object} cfg Xtype definition of item to add.
29717      */
29718     addxtype : function(cfg)
29719     {
29720         // basically accepts a pannel...
29721         // can accept a layout region..!?!?
29722         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29723         
29724         if (!cfg.xtype.match(/Panel$/)) {
29725             return false;
29726         }
29727         var ret = false;
29728         
29729         if (typeof(cfg.region) == 'undefined') {
29730             Roo.log("Failed to add Panel, region was not set");
29731             Roo.log(cfg);
29732             return false;
29733         }
29734         var region = cfg.region;
29735         delete cfg.region;
29736         
29737           
29738         var xitems = [];
29739         if (cfg.items) {
29740             xitems = cfg.items;
29741             delete cfg.items;
29742         }
29743         var nb = false;
29744         
29745         switch(cfg.xtype) 
29746         {
29747             case 'ContentPanel':  // ContentPanel (el, cfg)
29748             case 'ScrollPanel':  // ContentPanel (el, cfg)
29749             case 'ViewPanel': 
29750                 if(cfg.autoCreate) {
29751                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29752                 } else {
29753                     var el = this.el.createChild();
29754                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29755                 }
29756                 
29757                 this.add(region, ret);
29758                 break;
29759             
29760             
29761             case 'TreePanel': // our new panel!
29762                 cfg.el = this.el.createChild();
29763                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29764                 this.add(region, ret);
29765                 break;
29766             
29767             case 'NestedLayoutPanel': 
29768                 // create a new Layout (which is  a Border Layout...
29769                 var el = this.el.createChild();
29770                 var clayout = cfg.layout;
29771                 delete cfg.layout;
29772                 clayout.items   = clayout.items  || [];
29773                 // replace this exitems with the clayout ones..
29774                 xitems = clayout.items;
29775                  
29776                 
29777                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29778                     cfg.background = false;
29779                 }
29780                 var layout = new Roo.BorderLayout(el, clayout);
29781                 
29782                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29783                 //console.log('adding nested layout panel '  + cfg.toSource());
29784                 this.add(region, ret);
29785                 nb = {}; /// find first...
29786                 break;
29787                 
29788             case 'GridPanel': 
29789             
29790                 // needs grid and region
29791                 
29792                 //var el = this.getRegion(region).el.createChild();
29793                 var el = this.el.createChild();
29794                 // create the grid first...
29795                 
29796                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29797                 delete cfg.grid;
29798                 if (region == 'center' && this.active ) {
29799                     cfg.background = false;
29800                 }
29801                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29802                 
29803                 this.add(region, ret);
29804                 if (cfg.background) {
29805                     ret.on('activate', function(gp) {
29806                         if (!gp.grid.rendered) {
29807                             gp.grid.render();
29808                         }
29809                     });
29810                 } else {
29811                     grid.render();
29812                 }
29813                 break;
29814            
29815            
29816            
29817                 
29818                 
29819                 
29820             default:
29821                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29822                     
29823                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29824                     this.add(region, ret);
29825                 } else {
29826                 
29827                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29828                     return null;
29829                 }
29830                 
29831              // GridPanel (grid, cfg)
29832             
29833         }
29834         this.beginUpdate();
29835         // add children..
29836         var region = '';
29837         var abn = {};
29838         Roo.each(xitems, function(i)  {
29839             region = nb && i.region ? i.region : false;
29840             
29841             var add = ret.addxtype(i);
29842            
29843             if (region) {
29844                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29845                 if (!i.background) {
29846                     abn[region] = nb[region] ;
29847                 }
29848             }
29849             
29850         });
29851         this.endUpdate();
29852
29853         // make the last non-background panel active..
29854         //if (nb) { Roo.log(abn); }
29855         if (nb) {
29856             
29857             for(var r in abn) {
29858                 region = this.getRegion(r);
29859                 if (region) {
29860                     // tried using nb[r], but it does not work..
29861                      
29862                     region.showPanel(abn[r]);
29863                    
29864                 }
29865             }
29866         }
29867         return ret;
29868         
29869     }
29870 });
29871
29872 /**
29873  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29874  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29875  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29876  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29877  * <pre><code>
29878 // shorthand
29879 var CP = Roo.ContentPanel;
29880
29881 var layout = Roo.BorderLayout.create({
29882     north: {
29883         initialSize: 25,
29884         titlebar: false,
29885         panels: [new CP("north", "North")]
29886     },
29887     west: {
29888         split:true,
29889         initialSize: 200,
29890         minSize: 175,
29891         maxSize: 400,
29892         titlebar: true,
29893         collapsible: true,
29894         panels: [new CP("west", {title: "West"})]
29895     },
29896     east: {
29897         split:true,
29898         initialSize: 202,
29899         minSize: 175,
29900         maxSize: 400,
29901         titlebar: true,
29902         collapsible: true,
29903         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29904     },
29905     south: {
29906         split:true,
29907         initialSize: 100,
29908         minSize: 100,
29909         maxSize: 200,
29910         titlebar: true,
29911         collapsible: true,
29912         panels: [new CP("south", {title: "South", closable: true})]
29913     },
29914     center: {
29915         titlebar: true,
29916         autoScroll:true,
29917         resizeTabs: true,
29918         minTabWidth: 50,
29919         preferredTabWidth: 150,
29920         panels: [
29921             new CP("center1", {title: "Close Me", closable: true}),
29922             new CP("center2", {title: "Center Panel", closable: false})
29923         ]
29924     }
29925 }, document.body);
29926
29927 layout.getRegion("center").showPanel("center1");
29928 </code></pre>
29929  * @param config
29930  * @param targetEl
29931  */
29932 Roo.BorderLayout.create = function(config, targetEl){
29933     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29934     layout.beginUpdate();
29935     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29936     for(var j = 0, jlen = regions.length; j < jlen; j++){
29937         var lr = regions[j];
29938         if(layout.regions[lr] && config[lr].panels){
29939             var r = layout.regions[lr];
29940             var ps = config[lr].panels;
29941             layout.addTypedPanels(r, ps);
29942         }
29943     }
29944     layout.endUpdate();
29945     return layout;
29946 };
29947
29948 // private
29949 Roo.BorderLayout.RegionFactory = {
29950     // private
29951     validRegions : ["north","south","east","west","center"],
29952
29953     // private
29954     create : function(target, mgr, config){
29955         target = target.toLowerCase();
29956         if(config.lightweight || config.basic){
29957             return new Roo.BasicLayoutRegion(mgr, config, target);
29958         }
29959         switch(target){
29960             case "north":
29961                 return new Roo.NorthLayoutRegion(mgr, config);
29962             case "south":
29963                 return new Roo.SouthLayoutRegion(mgr, config);
29964             case "east":
29965                 return new Roo.EastLayoutRegion(mgr, config);
29966             case "west":
29967                 return new Roo.WestLayoutRegion(mgr, config);
29968             case "center":
29969                 return new Roo.CenterLayoutRegion(mgr, config);
29970         }
29971         throw 'Layout region "'+target+'" not supported.';
29972     }
29973 };/*
29974  * Based on:
29975  * Ext JS Library 1.1.1
29976  * Copyright(c) 2006-2007, Ext JS, LLC.
29977  *
29978  * Originally Released Under LGPL - original licence link has changed is not relivant.
29979  *
29980  * Fork - LGPL
29981  * <script type="text/javascript">
29982  */
29983  
29984 /**
29985  * @class Roo.BasicLayoutRegion
29986  * @extends Roo.util.Observable
29987  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29988  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29989  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29990  */
29991 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29992     this.mgr = mgr;
29993     this.position  = pos;
29994     this.events = {
29995         /**
29996          * @scope Roo.BasicLayoutRegion
29997          */
29998         
29999         /**
30000          * @event beforeremove
30001          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30002          * @param {Roo.LayoutRegion} this
30003          * @param {Roo.ContentPanel} panel The panel
30004          * @param {Object} e The cancel event object
30005          */
30006         "beforeremove" : true,
30007         /**
30008          * @event invalidated
30009          * Fires when the layout for this region is changed.
30010          * @param {Roo.LayoutRegion} this
30011          */
30012         "invalidated" : true,
30013         /**
30014          * @event visibilitychange
30015          * Fires when this region is shown or hidden 
30016          * @param {Roo.LayoutRegion} this
30017          * @param {Boolean} visibility true or false
30018          */
30019         "visibilitychange" : true,
30020         /**
30021          * @event paneladded
30022          * Fires when a panel is added. 
30023          * @param {Roo.LayoutRegion} this
30024          * @param {Roo.ContentPanel} panel The panel
30025          */
30026         "paneladded" : true,
30027         /**
30028          * @event panelremoved
30029          * Fires when a panel is removed. 
30030          * @param {Roo.LayoutRegion} this
30031          * @param {Roo.ContentPanel} panel The panel
30032          */
30033         "panelremoved" : true,
30034         /**
30035          * @event beforecollapse
30036          * Fires when this region before collapse.
30037          * @param {Roo.LayoutRegion} this
30038          */
30039         "beforecollapse" : true,
30040         /**
30041          * @event collapsed
30042          * Fires when this region is collapsed.
30043          * @param {Roo.LayoutRegion} this
30044          */
30045         "collapsed" : true,
30046         /**
30047          * @event expanded
30048          * Fires when this region is expanded.
30049          * @param {Roo.LayoutRegion} this
30050          */
30051         "expanded" : true,
30052         /**
30053          * @event slideshow
30054          * Fires when this region is slid into view.
30055          * @param {Roo.LayoutRegion} this
30056          */
30057         "slideshow" : true,
30058         /**
30059          * @event slidehide
30060          * Fires when this region slides out of view. 
30061          * @param {Roo.LayoutRegion} this
30062          */
30063         "slidehide" : true,
30064         /**
30065          * @event panelactivated
30066          * Fires when a panel is activated. 
30067          * @param {Roo.LayoutRegion} this
30068          * @param {Roo.ContentPanel} panel The activated panel
30069          */
30070         "panelactivated" : true,
30071         /**
30072          * @event resized
30073          * Fires when the user resizes this region. 
30074          * @param {Roo.LayoutRegion} this
30075          * @param {Number} newSize The new size (width for east/west, height for north/south)
30076          */
30077         "resized" : true
30078     };
30079     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30080     this.panels = new Roo.util.MixedCollection();
30081     this.panels.getKey = this.getPanelId.createDelegate(this);
30082     this.box = null;
30083     this.activePanel = null;
30084     // ensure listeners are added...
30085     
30086     if (config.listeners || config.events) {
30087         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30088             listeners : config.listeners || {},
30089             events : config.events || {}
30090         });
30091     }
30092     
30093     if(skipConfig !== true){
30094         this.applyConfig(config);
30095     }
30096 };
30097
30098 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30099     getPanelId : function(p){
30100         return p.getId();
30101     },
30102     
30103     applyConfig : function(config){
30104         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30105         this.config = config;
30106         
30107     },
30108     
30109     /**
30110      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30111      * the width, for horizontal (north, south) the height.
30112      * @param {Number} newSize The new width or height
30113      */
30114     resizeTo : function(newSize){
30115         var el = this.el ? this.el :
30116                  (this.activePanel ? this.activePanel.getEl() : null);
30117         if(el){
30118             switch(this.position){
30119                 case "east":
30120                 case "west":
30121                     el.setWidth(newSize);
30122                     this.fireEvent("resized", this, newSize);
30123                 break;
30124                 case "north":
30125                 case "south":
30126                     el.setHeight(newSize);
30127                     this.fireEvent("resized", this, newSize);
30128                 break;                
30129             }
30130         }
30131     },
30132     
30133     getBox : function(){
30134         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30135     },
30136     
30137     getMargins : function(){
30138         return this.margins;
30139     },
30140     
30141     updateBox : function(box){
30142         this.box = box;
30143         var el = this.activePanel.getEl();
30144         el.dom.style.left = box.x + "px";
30145         el.dom.style.top = box.y + "px";
30146         this.activePanel.setSize(box.width, box.height);
30147     },
30148     
30149     /**
30150      * Returns the container element for this region.
30151      * @return {Roo.Element}
30152      */
30153     getEl : function(){
30154         return this.activePanel;
30155     },
30156     
30157     /**
30158      * Returns true if this region is currently visible.
30159      * @return {Boolean}
30160      */
30161     isVisible : function(){
30162         return this.activePanel ? true : false;
30163     },
30164     
30165     setActivePanel : function(panel){
30166         panel = this.getPanel(panel);
30167         if(this.activePanel && this.activePanel != panel){
30168             this.activePanel.setActiveState(false);
30169             this.activePanel.getEl().setLeftTop(-10000,-10000);
30170         }
30171         this.activePanel = panel;
30172         panel.setActiveState(true);
30173         if(this.box){
30174             panel.setSize(this.box.width, this.box.height);
30175         }
30176         this.fireEvent("panelactivated", this, panel);
30177         this.fireEvent("invalidated");
30178     },
30179     
30180     /**
30181      * Show the specified panel.
30182      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30183      * @return {Roo.ContentPanel} The shown panel or null
30184      */
30185     showPanel : function(panel){
30186         if(panel = this.getPanel(panel)){
30187             this.setActivePanel(panel);
30188         }
30189         return panel;
30190     },
30191     
30192     /**
30193      * Get the active panel for this region.
30194      * @return {Roo.ContentPanel} The active panel or null
30195      */
30196     getActivePanel : function(){
30197         return this.activePanel;
30198     },
30199     
30200     /**
30201      * Add the passed ContentPanel(s)
30202      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30203      * @return {Roo.ContentPanel} The panel added (if only one was added)
30204      */
30205     add : function(panel){
30206         if(arguments.length > 1){
30207             for(var i = 0, len = arguments.length; i < len; i++) {
30208                 this.add(arguments[i]);
30209             }
30210             return null;
30211         }
30212         if(this.hasPanel(panel)){
30213             this.showPanel(panel);
30214             return panel;
30215         }
30216         var el = panel.getEl();
30217         if(el.dom.parentNode != this.mgr.el.dom){
30218             this.mgr.el.dom.appendChild(el.dom);
30219         }
30220         if(panel.setRegion){
30221             panel.setRegion(this);
30222         }
30223         this.panels.add(panel);
30224         el.setStyle("position", "absolute");
30225         if(!panel.background){
30226             this.setActivePanel(panel);
30227             if(this.config.initialSize && this.panels.getCount()==1){
30228                 this.resizeTo(this.config.initialSize);
30229             }
30230         }
30231         this.fireEvent("paneladded", this, panel);
30232         return panel;
30233     },
30234     
30235     /**
30236      * Returns true if the panel is in this region.
30237      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30238      * @return {Boolean}
30239      */
30240     hasPanel : function(panel){
30241         if(typeof panel == "object"){ // must be panel obj
30242             panel = panel.getId();
30243         }
30244         return this.getPanel(panel) ? true : false;
30245     },
30246     
30247     /**
30248      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30249      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30250      * @param {Boolean} preservePanel Overrides the config preservePanel option
30251      * @return {Roo.ContentPanel} The panel that was removed
30252      */
30253     remove : function(panel, preservePanel){
30254         panel = this.getPanel(panel);
30255         if(!panel){
30256             return null;
30257         }
30258         var e = {};
30259         this.fireEvent("beforeremove", this, panel, e);
30260         if(e.cancel === true){
30261             return null;
30262         }
30263         var panelId = panel.getId();
30264         this.panels.removeKey(panelId);
30265         return panel;
30266     },
30267     
30268     /**
30269      * Returns the panel specified or null if it's not in this region.
30270      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30271      * @return {Roo.ContentPanel}
30272      */
30273     getPanel : function(id){
30274         if(typeof id == "object"){ // must be panel obj
30275             return id;
30276         }
30277         return this.panels.get(id);
30278     },
30279     
30280     /**
30281      * Returns this regions position (north/south/east/west/center).
30282      * @return {String} 
30283      */
30284     getPosition: function(){
30285         return this.position;    
30286     }
30287 });/*
30288  * Based on:
30289  * Ext JS Library 1.1.1
30290  * Copyright(c) 2006-2007, Ext JS, LLC.
30291  *
30292  * Originally Released Under LGPL - original licence link has changed is not relivant.
30293  *
30294  * Fork - LGPL
30295  * <script type="text/javascript">
30296  */
30297  
30298 /**
30299  * @class Roo.LayoutRegion
30300  * @extends Roo.BasicLayoutRegion
30301  * This class represents a region in a layout manager.
30302  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30303  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30304  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30305  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30306  * @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})
30307  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30308  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30309  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30310  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30311  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30312  * @cfg {String}    title           The title for the region (overrides panel titles)
30313  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30314  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30315  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30316  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30317  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30318  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30319  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30320  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30321  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30322  * @cfg {Boolean}   showPin         True to show a pin button
30323  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30324  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30325  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30326  * @cfg {Number}    width           For East/West panels
30327  * @cfg {Number}    height          For North/South panels
30328  * @cfg {Boolean}   split           To show the splitter
30329  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30330  */
30331 Roo.LayoutRegion = function(mgr, config, pos){
30332     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30333     var dh = Roo.DomHelper;
30334     /** This region's container element 
30335     * @type Roo.Element */
30336     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30337     /** This region's title element 
30338     * @type Roo.Element */
30339
30340     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30341         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30342         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30343     ]}, true);
30344     this.titleEl.enableDisplayMode();
30345     /** This region's title text element 
30346     * @type HTMLElement */
30347     this.titleTextEl = this.titleEl.dom.firstChild;
30348     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30349     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30350     this.closeBtn.enableDisplayMode();
30351     this.closeBtn.on("click", this.closeClicked, this);
30352     this.closeBtn.hide();
30353
30354     this.createBody(config);
30355     this.visible = true;
30356     this.collapsed = false;
30357
30358     if(config.hideWhenEmpty){
30359         this.hide();
30360         this.on("paneladded", this.validateVisibility, this);
30361         this.on("panelremoved", this.validateVisibility, this);
30362     }
30363     this.applyConfig(config);
30364 };
30365
30366 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30367
30368     createBody : function(){
30369         /** This region's body element 
30370         * @type Roo.Element */
30371         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30372     },
30373
30374     applyConfig : function(c){
30375         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30376             var dh = Roo.DomHelper;
30377             if(c.titlebar !== false){
30378                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30379                 this.collapseBtn.on("click", this.collapse, this);
30380                 this.collapseBtn.enableDisplayMode();
30381
30382                 if(c.showPin === true || this.showPin){
30383                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30384                     this.stickBtn.enableDisplayMode();
30385                     this.stickBtn.on("click", this.expand, this);
30386                     this.stickBtn.hide();
30387                 }
30388             }
30389             /** This region's collapsed element
30390             * @type Roo.Element */
30391             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30392                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30393             ]}, true);
30394             if(c.floatable !== false){
30395                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30396                this.collapsedEl.on("click", this.collapseClick, this);
30397             }
30398
30399             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30400                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30401                    id: "message", unselectable: "on", style:{"float":"left"}});
30402                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30403              }
30404             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30405             this.expandBtn.on("click", this.expand, this);
30406         }
30407         if(this.collapseBtn){
30408             this.collapseBtn.setVisible(c.collapsible == true);
30409         }
30410         this.cmargins = c.cmargins || this.cmargins ||
30411                          (this.position == "west" || this.position == "east" ?
30412                              {top: 0, left: 2, right:2, bottom: 0} :
30413                              {top: 2, left: 0, right:0, bottom: 2});
30414         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30415         this.bottomTabs = c.tabPosition != "top";
30416         this.autoScroll = c.autoScroll || false;
30417         if(this.autoScroll){
30418             this.bodyEl.setStyle("overflow", "auto");
30419         }else{
30420             this.bodyEl.setStyle("overflow", "hidden");
30421         }
30422         //if(c.titlebar !== false){
30423             if((!c.titlebar && !c.title) || c.titlebar === false){
30424                 this.titleEl.hide();
30425             }else{
30426                 this.titleEl.show();
30427                 if(c.title){
30428                     this.titleTextEl.innerHTML = c.title;
30429                 }
30430             }
30431         //}
30432         this.duration = c.duration || .30;
30433         this.slideDuration = c.slideDuration || .45;
30434         this.config = c;
30435         if(c.collapsed){
30436             this.collapse(true);
30437         }
30438         if(c.hidden){
30439             this.hide();
30440         }
30441     },
30442     /**
30443      * Returns true if this region is currently visible.
30444      * @return {Boolean}
30445      */
30446     isVisible : function(){
30447         return this.visible;
30448     },
30449
30450     /**
30451      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30452      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30453      */
30454     setCollapsedTitle : function(title){
30455         title = title || "&#160;";
30456         if(this.collapsedTitleTextEl){
30457             this.collapsedTitleTextEl.innerHTML = title;
30458         }
30459     },
30460
30461     getBox : function(){
30462         var b;
30463         if(!this.collapsed){
30464             b = this.el.getBox(false, true);
30465         }else{
30466             b = this.collapsedEl.getBox(false, true);
30467         }
30468         return b;
30469     },
30470
30471     getMargins : function(){
30472         return this.collapsed ? this.cmargins : this.margins;
30473     },
30474
30475     highlight : function(){
30476         this.el.addClass("x-layout-panel-dragover");
30477     },
30478
30479     unhighlight : function(){
30480         this.el.removeClass("x-layout-panel-dragover");
30481     },
30482
30483     updateBox : function(box){
30484         this.box = box;
30485         if(!this.collapsed){
30486             this.el.dom.style.left = box.x + "px";
30487             this.el.dom.style.top = box.y + "px";
30488             this.updateBody(box.width, box.height);
30489         }else{
30490             this.collapsedEl.dom.style.left = box.x + "px";
30491             this.collapsedEl.dom.style.top = box.y + "px";
30492             this.collapsedEl.setSize(box.width, box.height);
30493         }
30494         if(this.tabs){
30495             this.tabs.autoSizeTabs();
30496         }
30497     },
30498
30499     updateBody : function(w, h){
30500         if(w !== null){
30501             this.el.setWidth(w);
30502             w -= this.el.getBorderWidth("rl");
30503             if(this.config.adjustments){
30504                 w += this.config.adjustments[0];
30505             }
30506         }
30507         if(h !== null){
30508             this.el.setHeight(h);
30509             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30510             h -= this.el.getBorderWidth("tb");
30511             if(this.config.adjustments){
30512                 h += this.config.adjustments[1];
30513             }
30514             this.bodyEl.setHeight(h);
30515             if(this.tabs){
30516                 h = this.tabs.syncHeight(h);
30517             }
30518         }
30519         if(this.panelSize){
30520             w = w !== null ? w : this.panelSize.width;
30521             h = h !== null ? h : this.panelSize.height;
30522         }
30523         if(this.activePanel){
30524             var el = this.activePanel.getEl();
30525             w = w !== null ? w : el.getWidth();
30526             h = h !== null ? h : el.getHeight();
30527             this.panelSize = {width: w, height: h};
30528             this.activePanel.setSize(w, h);
30529         }
30530         if(Roo.isIE && this.tabs){
30531             this.tabs.el.repaint();
30532         }
30533     },
30534
30535     /**
30536      * Returns the container element for this region.
30537      * @return {Roo.Element}
30538      */
30539     getEl : function(){
30540         return this.el;
30541     },
30542
30543     /**
30544      * Hides this region.
30545      */
30546     hide : function(){
30547         if(!this.collapsed){
30548             this.el.dom.style.left = "-2000px";
30549             this.el.hide();
30550         }else{
30551             this.collapsedEl.dom.style.left = "-2000px";
30552             this.collapsedEl.hide();
30553         }
30554         this.visible = false;
30555         this.fireEvent("visibilitychange", this, false);
30556     },
30557
30558     /**
30559      * Shows this region if it was previously hidden.
30560      */
30561     show : function(){
30562         if(!this.collapsed){
30563             this.el.show();
30564         }else{
30565             this.collapsedEl.show();
30566         }
30567         this.visible = true;
30568         this.fireEvent("visibilitychange", this, true);
30569     },
30570
30571     closeClicked : function(){
30572         if(this.activePanel){
30573             this.remove(this.activePanel);
30574         }
30575     },
30576
30577     collapseClick : function(e){
30578         if(this.isSlid){
30579            e.stopPropagation();
30580            this.slideIn();
30581         }else{
30582            e.stopPropagation();
30583            this.slideOut();
30584         }
30585     },
30586
30587     /**
30588      * Collapses this region.
30589      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30590      */
30591     collapse : function(skipAnim, skipCheck){
30592         if(this.collapsed) {
30593             return;
30594         }
30595         
30596         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30597             
30598             this.collapsed = true;
30599             if(this.split){
30600                 this.split.el.hide();
30601             }
30602             if(this.config.animate && skipAnim !== true){
30603                 this.fireEvent("invalidated", this);
30604                 this.animateCollapse();
30605             }else{
30606                 this.el.setLocation(-20000,-20000);
30607                 this.el.hide();
30608                 this.collapsedEl.show();
30609                 this.fireEvent("collapsed", this);
30610                 this.fireEvent("invalidated", this);
30611             }
30612         }
30613         
30614     },
30615
30616     animateCollapse : function(){
30617         // overridden
30618     },
30619
30620     /**
30621      * Expands this region if it was previously collapsed.
30622      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30623      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30624      */
30625     expand : function(e, skipAnim){
30626         if(e) {
30627             e.stopPropagation();
30628         }
30629         if(!this.collapsed || this.el.hasActiveFx()) {
30630             return;
30631         }
30632         if(this.isSlid){
30633             this.afterSlideIn();
30634             skipAnim = true;
30635         }
30636         this.collapsed = false;
30637         if(this.config.animate && skipAnim !== true){
30638             this.animateExpand();
30639         }else{
30640             this.el.show();
30641             if(this.split){
30642                 this.split.el.show();
30643             }
30644             this.collapsedEl.setLocation(-2000,-2000);
30645             this.collapsedEl.hide();
30646             this.fireEvent("invalidated", this);
30647             this.fireEvent("expanded", this);
30648         }
30649     },
30650
30651     animateExpand : function(){
30652         // overridden
30653     },
30654
30655     initTabs : function()
30656     {
30657         this.bodyEl.setStyle("overflow", "hidden");
30658         var ts = new Roo.TabPanel(
30659                 this.bodyEl.dom,
30660                 {
30661                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30662                     disableTooltips: this.config.disableTabTips,
30663                     toolbar : this.config.toolbar
30664                 }
30665         );
30666         if(this.config.hideTabs){
30667             ts.stripWrap.setDisplayed(false);
30668         }
30669         this.tabs = ts;
30670         ts.resizeTabs = this.config.resizeTabs === true;
30671         ts.minTabWidth = this.config.minTabWidth || 40;
30672         ts.maxTabWidth = this.config.maxTabWidth || 250;
30673         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30674         ts.monitorResize = false;
30675         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30676         ts.bodyEl.addClass('x-layout-tabs-body');
30677         this.panels.each(this.initPanelAsTab, this);
30678     },
30679
30680     initPanelAsTab : function(panel){
30681         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30682                     this.config.closeOnTab && panel.isClosable());
30683         if(panel.tabTip !== undefined){
30684             ti.setTooltip(panel.tabTip);
30685         }
30686         ti.on("activate", function(){
30687               this.setActivePanel(panel);
30688         }, this);
30689         if(this.config.closeOnTab){
30690             ti.on("beforeclose", function(t, e){
30691                 e.cancel = true;
30692                 this.remove(panel);
30693             }, this);
30694         }
30695         return ti;
30696     },
30697
30698     updatePanelTitle : function(panel, title){
30699         if(this.activePanel == panel){
30700             this.updateTitle(title);
30701         }
30702         if(this.tabs){
30703             var ti = this.tabs.getTab(panel.getEl().id);
30704             ti.setText(title);
30705             if(panel.tabTip !== undefined){
30706                 ti.setTooltip(panel.tabTip);
30707             }
30708         }
30709     },
30710
30711     updateTitle : function(title){
30712         if(this.titleTextEl && !this.config.title){
30713             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30714         }
30715     },
30716
30717     setActivePanel : function(panel){
30718         panel = this.getPanel(panel);
30719         if(this.activePanel && this.activePanel != panel){
30720             this.activePanel.setActiveState(false);
30721         }
30722         this.activePanel = panel;
30723         panel.setActiveState(true);
30724         if(this.panelSize){
30725             panel.setSize(this.panelSize.width, this.panelSize.height);
30726         }
30727         if(this.closeBtn){
30728             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30729         }
30730         this.updateTitle(panel.getTitle());
30731         if(this.tabs){
30732             this.fireEvent("invalidated", this);
30733         }
30734         this.fireEvent("panelactivated", this, panel);
30735     },
30736
30737     /**
30738      * Shows the specified panel.
30739      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30740      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30741      */
30742     showPanel : function(panel)
30743     {
30744         panel = this.getPanel(panel);
30745         if(panel){
30746             if(this.tabs){
30747                 var tab = this.tabs.getTab(panel.getEl().id);
30748                 if(tab.isHidden()){
30749                     this.tabs.unhideTab(tab.id);
30750                 }
30751                 tab.activate();
30752             }else{
30753                 this.setActivePanel(panel);
30754             }
30755         }
30756         return panel;
30757     },
30758
30759     /**
30760      * Get the active panel for this region.
30761      * @return {Roo.ContentPanel} The active panel or null
30762      */
30763     getActivePanel : function(){
30764         return this.activePanel;
30765     },
30766
30767     validateVisibility : function(){
30768         if(this.panels.getCount() < 1){
30769             this.updateTitle("&#160;");
30770             this.closeBtn.hide();
30771             this.hide();
30772         }else{
30773             if(!this.isVisible()){
30774                 this.show();
30775             }
30776         }
30777     },
30778
30779     /**
30780      * Adds the passed ContentPanel(s) to this region.
30781      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30782      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30783      */
30784     add : function(panel){
30785         if(arguments.length > 1){
30786             for(var i = 0, len = arguments.length; i < len; i++) {
30787                 this.add(arguments[i]);
30788             }
30789             return null;
30790         }
30791         if(this.hasPanel(panel)){
30792             this.showPanel(panel);
30793             return panel;
30794         }
30795         panel.setRegion(this);
30796         this.panels.add(panel);
30797         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30798             this.bodyEl.dom.appendChild(panel.getEl().dom);
30799             if(panel.background !== true){
30800                 this.setActivePanel(panel);
30801             }
30802             this.fireEvent("paneladded", this, panel);
30803             return panel;
30804         }
30805         if(!this.tabs){
30806             this.initTabs();
30807         }else{
30808             this.initPanelAsTab(panel);
30809         }
30810         if(panel.background !== true){
30811             this.tabs.activate(panel.getEl().id);
30812         }
30813         this.fireEvent("paneladded", this, panel);
30814         return panel;
30815     },
30816
30817     /**
30818      * Hides the tab for the specified panel.
30819      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30820      */
30821     hidePanel : function(panel){
30822         if(this.tabs && (panel = this.getPanel(panel))){
30823             this.tabs.hideTab(panel.getEl().id);
30824         }
30825     },
30826
30827     /**
30828      * Unhides the tab for a previously hidden panel.
30829      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30830      */
30831     unhidePanel : function(panel){
30832         if(this.tabs && (panel = this.getPanel(panel))){
30833             this.tabs.unhideTab(panel.getEl().id);
30834         }
30835     },
30836
30837     clearPanels : function(){
30838         while(this.panels.getCount() > 0){
30839              this.remove(this.panels.first());
30840         }
30841     },
30842
30843     /**
30844      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30845      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30846      * @param {Boolean} preservePanel Overrides the config preservePanel option
30847      * @return {Roo.ContentPanel} The panel that was removed
30848      */
30849     remove : function(panel, preservePanel){
30850         panel = this.getPanel(panel);
30851         if(!panel){
30852             return null;
30853         }
30854         var e = {};
30855         this.fireEvent("beforeremove", this, panel, e);
30856         if(e.cancel === true){
30857             return null;
30858         }
30859         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30860         var panelId = panel.getId();
30861         this.panels.removeKey(panelId);
30862         if(preservePanel){
30863             document.body.appendChild(panel.getEl().dom);
30864         }
30865         if(this.tabs){
30866             this.tabs.removeTab(panel.getEl().id);
30867         }else if (!preservePanel){
30868             this.bodyEl.dom.removeChild(panel.getEl().dom);
30869         }
30870         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30871             var p = this.panels.first();
30872             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30873             tempEl.appendChild(p.getEl().dom);
30874             this.bodyEl.update("");
30875             this.bodyEl.dom.appendChild(p.getEl().dom);
30876             tempEl = null;
30877             this.updateTitle(p.getTitle());
30878             this.tabs = null;
30879             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30880             this.setActivePanel(p);
30881         }
30882         panel.setRegion(null);
30883         if(this.activePanel == panel){
30884             this.activePanel = null;
30885         }
30886         if(this.config.autoDestroy !== false && preservePanel !== true){
30887             try{panel.destroy();}catch(e){}
30888         }
30889         this.fireEvent("panelremoved", this, panel);
30890         return panel;
30891     },
30892
30893     /**
30894      * Returns the TabPanel component used by this region
30895      * @return {Roo.TabPanel}
30896      */
30897     getTabs : function(){
30898         return this.tabs;
30899     },
30900
30901     createTool : function(parentEl, className){
30902         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30903             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30904         btn.addClassOnOver("x-layout-tools-button-over");
30905         return btn;
30906     }
30907 });/*
30908  * Based on:
30909  * Ext JS Library 1.1.1
30910  * Copyright(c) 2006-2007, Ext JS, LLC.
30911  *
30912  * Originally Released Under LGPL - original licence link has changed is not relivant.
30913  *
30914  * Fork - LGPL
30915  * <script type="text/javascript">
30916  */
30917  
30918
30919
30920 /**
30921  * @class Roo.SplitLayoutRegion
30922  * @extends Roo.LayoutRegion
30923  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30924  */
30925 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30926     this.cursor = cursor;
30927     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30928 };
30929
30930 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30931     splitTip : "Drag to resize.",
30932     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30933     useSplitTips : false,
30934
30935     applyConfig : function(config){
30936         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30937         if(config.split){
30938             if(!this.split){
30939                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30940                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30941                 /** The SplitBar for this region 
30942                 * @type Roo.SplitBar */
30943                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30944                 this.split.on("moved", this.onSplitMove, this);
30945                 this.split.useShim = config.useShim === true;
30946                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30947                 if(this.useSplitTips){
30948                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30949                 }
30950                 if(config.collapsible){
30951                     this.split.el.on("dblclick", this.collapse,  this);
30952                 }
30953             }
30954             if(typeof config.minSize != "undefined"){
30955                 this.split.minSize = config.minSize;
30956             }
30957             if(typeof config.maxSize != "undefined"){
30958                 this.split.maxSize = config.maxSize;
30959             }
30960             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30961                 this.hideSplitter();
30962             }
30963         }
30964     },
30965
30966     getHMaxSize : function(){
30967          var cmax = this.config.maxSize || 10000;
30968          var center = this.mgr.getRegion("center");
30969          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30970     },
30971
30972     getVMaxSize : function(){
30973          var cmax = this.config.maxSize || 10000;
30974          var center = this.mgr.getRegion("center");
30975          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30976     },
30977
30978     onSplitMove : function(split, newSize){
30979         this.fireEvent("resized", this, newSize);
30980     },
30981     
30982     /** 
30983      * Returns the {@link Roo.SplitBar} for this region.
30984      * @return {Roo.SplitBar}
30985      */
30986     getSplitBar : function(){
30987         return this.split;
30988     },
30989     
30990     hide : function(){
30991         this.hideSplitter();
30992         Roo.SplitLayoutRegion.superclass.hide.call(this);
30993     },
30994
30995     hideSplitter : function(){
30996         if(this.split){
30997             this.split.el.setLocation(-2000,-2000);
30998             this.split.el.hide();
30999         }
31000     },
31001
31002     show : function(){
31003         if(this.split){
31004             this.split.el.show();
31005         }
31006         Roo.SplitLayoutRegion.superclass.show.call(this);
31007     },
31008     
31009     beforeSlide: function(){
31010         if(Roo.isGecko){// firefox overflow auto bug workaround
31011             this.bodyEl.clip();
31012             if(this.tabs) {
31013                 this.tabs.bodyEl.clip();
31014             }
31015             if(this.activePanel){
31016                 this.activePanel.getEl().clip();
31017                 
31018                 if(this.activePanel.beforeSlide){
31019                     this.activePanel.beforeSlide();
31020                 }
31021             }
31022         }
31023     },
31024     
31025     afterSlide : function(){
31026         if(Roo.isGecko){// firefox overflow auto bug workaround
31027             this.bodyEl.unclip();
31028             if(this.tabs) {
31029                 this.tabs.bodyEl.unclip();
31030             }
31031             if(this.activePanel){
31032                 this.activePanel.getEl().unclip();
31033                 if(this.activePanel.afterSlide){
31034                     this.activePanel.afterSlide();
31035                 }
31036             }
31037         }
31038     },
31039
31040     initAutoHide : function(){
31041         if(this.autoHide !== false){
31042             if(!this.autoHideHd){
31043                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31044                 this.autoHideHd = {
31045                     "mouseout": function(e){
31046                         if(!e.within(this.el, true)){
31047                             st.delay(500);
31048                         }
31049                     },
31050                     "mouseover" : function(e){
31051                         st.cancel();
31052                     },
31053                     scope : this
31054                 };
31055             }
31056             this.el.on(this.autoHideHd);
31057         }
31058     },
31059
31060     clearAutoHide : function(){
31061         if(this.autoHide !== false){
31062             this.el.un("mouseout", this.autoHideHd.mouseout);
31063             this.el.un("mouseover", this.autoHideHd.mouseover);
31064         }
31065     },
31066
31067     clearMonitor : function(){
31068         Roo.get(document).un("click", this.slideInIf, this);
31069     },
31070
31071     // these names are backwards but not changed for compat
31072     slideOut : function(){
31073         if(this.isSlid || this.el.hasActiveFx()){
31074             return;
31075         }
31076         this.isSlid = true;
31077         if(this.collapseBtn){
31078             this.collapseBtn.hide();
31079         }
31080         this.closeBtnState = this.closeBtn.getStyle('display');
31081         this.closeBtn.hide();
31082         if(this.stickBtn){
31083             this.stickBtn.show();
31084         }
31085         this.el.show();
31086         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31087         this.beforeSlide();
31088         this.el.setStyle("z-index", 10001);
31089         this.el.slideIn(this.getSlideAnchor(), {
31090             callback: function(){
31091                 this.afterSlide();
31092                 this.initAutoHide();
31093                 Roo.get(document).on("click", this.slideInIf, this);
31094                 this.fireEvent("slideshow", this);
31095             },
31096             scope: this,
31097             block: true
31098         });
31099     },
31100
31101     afterSlideIn : function(){
31102         this.clearAutoHide();
31103         this.isSlid = false;
31104         this.clearMonitor();
31105         this.el.setStyle("z-index", "");
31106         if(this.collapseBtn){
31107             this.collapseBtn.show();
31108         }
31109         this.closeBtn.setStyle('display', this.closeBtnState);
31110         if(this.stickBtn){
31111             this.stickBtn.hide();
31112         }
31113         this.fireEvent("slidehide", this);
31114     },
31115
31116     slideIn : function(cb){
31117         if(!this.isSlid || this.el.hasActiveFx()){
31118             Roo.callback(cb);
31119             return;
31120         }
31121         this.isSlid = false;
31122         this.beforeSlide();
31123         this.el.slideOut(this.getSlideAnchor(), {
31124             callback: function(){
31125                 this.el.setLeftTop(-10000, -10000);
31126                 this.afterSlide();
31127                 this.afterSlideIn();
31128                 Roo.callback(cb);
31129             },
31130             scope: this,
31131             block: true
31132         });
31133     },
31134     
31135     slideInIf : function(e){
31136         if(!e.within(this.el)){
31137             this.slideIn();
31138         }
31139     },
31140
31141     animateCollapse : function(){
31142         this.beforeSlide();
31143         this.el.setStyle("z-index", 20000);
31144         var anchor = this.getSlideAnchor();
31145         this.el.slideOut(anchor, {
31146             callback : function(){
31147                 this.el.setStyle("z-index", "");
31148                 this.collapsedEl.slideIn(anchor, {duration:.3});
31149                 this.afterSlide();
31150                 this.el.setLocation(-10000,-10000);
31151                 this.el.hide();
31152                 this.fireEvent("collapsed", this);
31153             },
31154             scope: this,
31155             block: true
31156         });
31157     },
31158
31159     animateExpand : function(){
31160         this.beforeSlide();
31161         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31162         this.el.setStyle("z-index", 20000);
31163         this.collapsedEl.hide({
31164             duration:.1
31165         });
31166         this.el.slideIn(this.getSlideAnchor(), {
31167             callback : function(){
31168                 this.el.setStyle("z-index", "");
31169                 this.afterSlide();
31170                 if(this.split){
31171                     this.split.el.show();
31172                 }
31173                 this.fireEvent("invalidated", this);
31174                 this.fireEvent("expanded", this);
31175             },
31176             scope: this,
31177             block: true
31178         });
31179     },
31180
31181     anchors : {
31182         "west" : "left",
31183         "east" : "right",
31184         "north" : "top",
31185         "south" : "bottom"
31186     },
31187
31188     sanchors : {
31189         "west" : "l",
31190         "east" : "r",
31191         "north" : "t",
31192         "south" : "b"
31193     },
31194
31195     canchors : {
31196         "west" : "tl-tr",
31197         "east" : "tr-tl",
31198         "north" : "tl-bl",
31199         "south" : "bl-tl"
31200     },
31201
31202     getAnchor : function(){
31203         return this.anchors[this.position];
31204     },
31205
31206     getCollapseAnchor : function(){
31207         return this.canchors[this.position];
31208     },
31209
31210     getSlideAnchor : function(){
31211         return this.sanchors[this.position];
31212     },
31213
31214     getAlignAdj : function(){
31215         var cm = this.cmargins;
31216         switch(this.position){
31217             case "west":
31218                 return [0, 0];
31219             break;
31220             case "east":
31221                 return [0, 0];
31222             break;
31223             case "north":
31224                 return [0, 0];
31225             break;
31226             case "south":
31227                 return [0, 0];
31228             break;
31229         }
31230     },
31231
31232     getExpandAdj : function(){
31233         var c = this.collapsedEl, cm = this.cmargins;
31234         switch(this.position){
31235             case "west":
31236                 return [-(cm.right+c.getWidth()+cm.left), 0];
31237             break;
31238             case "east":
31239                 return [cm.right+c.getWidth()+cm.left, 0];
31240             break;
31241             case "north":
31242                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31243             break;
31244             case "south":
31245                 return [0, cm.top+cm.bottom+c.getHeight()];
31246             break;
31247         }
31248     }
31249 });/*
31250  * Based on:
31251  * Ext JS Library 1.1.1
31252  * Copyright(c) 2006-2007, Ext JS, LLC.
31253  *
31254  * Originally Released Under LGPL - original licence link has changed is not relivant.
31255  *
31256  * Fork - LGPL
31257  * <script type="text/javascript">
31258  */
31259 /*
31260  * These classes are private internal classes
31261  */
31262 Roo.CenterLayoutRegion = function(mgr, config){
31263     Roo.LayoutRegion.call(this, mgr, config, "center");
31264     this.visible = true;
31265     this.minWidth = config.minWidth || 20;
31266     this.minHeight = config.minHeight || 20;
31267 };
31268
31269 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31270     hide : function(){
31271         // center panel can't be hidden
31272     },
31273     
31274     show : function(){
31275         // center panel can't be hidden
31276     },
31277     
31278     getMinWidth: function(){
31279         return this.minWidth;
31280     },
31281     
31282     getMinHeight: function(){
31283         return this.minHeight;
31284     }
31285 });
31286
31287
31288 Roo.NorthLayoutRegion = function(mgr, config){
31289     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31290     if(this.split){
31291         this.split.placement = Roo.SplitBar.TOP;
31292         this.split.orientation = Roo.SplitBar.VERTICAL;
31293         this.split.el.addClass("x-layout-split-v");
31294     }
31295     var size = config.initialSize || config.height;
31296     if(typeof size != "undefined"){
31297         this.el.setHeight(size);
31298     }
31299 };
31300 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31301     orientation: Roo.SplitBar.VERTICAL,
31302     getBox : function(){
31303         if(this.collapsed){
31304             return this.collapsedEl.getBox();
31305         }
31306         var box = this.el.getBox();
31307         if(this.split){
31308             box.height += this.split.el.getHeight();
31309         }
31310         return box;
31311     },
31312     
31313     updateBox : function(box){
31314         if(this.split && !this.collapsed){
31315             box.height -= this.split.el.getHeight();
31316             this.split.el.setLeft(box.x);
31317             this.split.el.setTop(box.y+box.height);
31318             this.split.el.setWidth(box.width);
31319         }
31320         if(this.collapsed){
31321             this.updateBody(box.width, null);
31322         }
31323         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31324     }
31325 });
31326
31327 Roo.SouthLayoutRegion = function(mgr, config){
31328     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31329     if(this.split){
31330         this.split.placement = Roo.SplitBar.BOTTOM;
31331         this.split.orientation = Roo.SplitBar.VERTICAL;
31332         this.split.el.addClass("x-layout-split-v");
31333     }
31334     var size = config.initialSize || config.height;
31335     if(typeof size != "undefined"){
31336         this.el.setHeight(size);
31337     }
31338 };
31339 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31340     orientation: Roo.SplitBar.VERTICAL,
31341     getBox : function(){
31342         if(this.collapsed){
31343             return this.collapsedEl.getBox();
31344         }
31345         var box = this.el.getBox();
31346         if(this.split){
31347             var sh = this.split.el.getHeight();
31348             box.height += sh;
31349             box.y -= sh;
31350         }
31351         return box;
31352     },
31353     
31354     updateBox : function(box){
31355         if(this.split && !this.collapsed){
31356             var sh = this.split.el.getHeight();
31357             box.height -= sh;
31358             box.y += sh;
31359             this.split.el.setLeft(box.x);
31360             this.split.el.setTop(box.y-sh);
31361             this.split.el.setWidth(box.width);
31362         }
31363         if(this.collapsed){
31364             this.updateBody(box.width, null);
31365         }
31366         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31367     }
31368 });
31369
31370 Roo.EastLayoutRegion = function(mgr, config){
31371     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31372     if(this.split){
31373         this.split.placement = Roo.SplitBar.RIGHT;
31374         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31375         this.split.el.addClass("x-layout-split-h");
31376     }
31377     var size = config.initialSize || config.width;
31378     if(typeof size != "undefined"){
31379         this.el.setWidth(size);
31380     }
31381 };
31382 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31383     orientation: Roo.SplitBar.HORIZONTAL,
31384     getBox : function(){
31385         if(this.collapsed){
31386             return this.collapsedEl.getBox();
31387         }
31388         var box = this.el.getBox();
31389         if(this.split){
31390             var sw = this.split.el.getWidth();
31391             box.width += sw;
31392             box.x -= sw;
31393         }
31394         return box;
31395     },
31396
31397     updateBox : function(box){
31398         if(this.split && !this.collapsed){
31399             var sw = this.split.el.getWidth();
31400             box.width -= sw;
31401             this.split.el.setLeft(box.x);
31402             this.split.el.setTop(box.y);
31403             this.split.el.setHeight(box.height);
31404             box.x += sw;
31405         }
31406         if(this.collapsed){
31407             this.updateBody(null, box.height);
31408         }
31409         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31410     }
31411 });
31412
31413 Roo.WestLayoutRegion = function(mgr, config){
31414     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31415     if(this.split){
31416         this.split.placement = Roo.SplitBar.LEFT;
31417         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31418         this.split.el.addClass("x-layout-split-h");
31419     }
31420     var size = config.initialSize || config.width;
31421     if(typeof size != "undefined"){
31422         this.el.setWidth(size);
31423     }
31424 };
31425 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31426     orientation: Roo.SplitBar.HORIZONTAL,
31427     getBox : function(){
31428         if(this.collapsed){
31429             return this.collapsedEl.getBox();
31430         }
31431         var box = this.el.getBox();
31432         if(this.split){
31433             box.width += this.split.el.getWidth();
31434         }
31435         return box;
31436     },
31437     
31438     updateBox : function(box){
31439         if(this.split && !this.collapsed){
31440             var sw = this.split.el.getWidth();
31441             box.width -= sw;
31442             this.split.el.setLeft(box.x+box.width);
31443             this.split.el.setTop(box.y);
31444             this.split.el.setHeight(box.height);
31445         }
31446         if(this.collapsed){
31447             this.updateBody(null, box.height);
31448         }
31449         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31450     }
31451 });
31452 /*
31453  * Based on:
31454  * Ext JS Library 1.1.1
31455  * Copyright(c) 2006-2007, Ext JS, LLC.
31456  *
31457  * Originally Released Under LGPL - original licence link has changed is not relivant.
31458  *
31459  * Fork - LGPL
31460  * <script type="text/javascript">
31461  */
31462  
31463  
31464 /*
31465  * Private internal class for reading and applying state
31466  */
31467 Roo.LayoutStateManager = function(layout){
31468      // default empty state
31469      this.state = {
31470         north: {},
31471         south: {},
31472         east: {},
31473         west: {}       
31474     };
31475 };
31476
31477 Roo.LayoutStateManager.prototype = {
31478     init : function(layout, provider){
31479         this.provider = provider;
31480         var state = provider.get(layout.id+"-layout-state");
31481         if(state){
31482             var wasUpdating = layout.isUpdating();
31483             if(!wasUpdating){
31484                 layout.beginUpdate();
31485             }
31486             for(var key in state){
31487                 if(typeof state[key] != "function"){
31488                     var rstate = state[key];
31489                     var r = layout.getRegion(key);
31490                     if(r && rstate){
31491                         if(rstate.size){
31492                             r.resizeTo(rstate.size);
31493                         }
31494                         if(rstate.collapsed == true){
31495                             r.collapse(true);
31496                         }else{
31497                             r.expand(null, true);
31498                         }
31499                     }
31500                 }
31501             }
31502             if(!wasUpdating){
31503                 layout.endUpdate();
31504             }
31505             this.state = state; 
31506         }
31507         this.layout = layout;
31508         layout.on("regionresized", this.onRegionResized, this);
31509         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31510         layout.on("regionexpanded", this.onRegionExpanded, this);
31511     },
31512     
31513     storeState : function(){
31514         this.provider.set(this.layout.id+"-layout-state", this.state);
31515     },
31516     
31517     onRegionResized : function(region, newSize){
31518         this.state[region.getPosition()].size = newSize;
31519         this.storeState();
31520     },
31521     
31522     onRegionCollapsed : function(region){
31523         this.state[region.getPosition()].collapsed = true;
31524         this.storeState();
31525     },
31526     
31527     onRegionExpanded : function(region){
31528         this.state[region.getPosition()].collapsed = false;
31529         this.storeState();
31530     }
31531 };/*
31532  * Based on:
31533  * Ext JS Library 1.1.1
31534  * Copyright(c) 2006-2007, Ext JS, LLC.
31535  *
31536  * Originally Released Under LGPL - original licence link has changed is not relivant.
31537  *
31538  * Fork - LGPL
31539  * <script type="text/javascript">
31540  */
31541 /**
31542  * @class Roo.ContentPanel
31543  * @extends Roo.util.Observable
31544  * A basic ContentPanel element.
31545  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31546  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31547  * @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
31548  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31549  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31550  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31551  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31552  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31553  * @cfg {String} title          The title for this panel
31554  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31555  * @cfg {String} url            Calls {@link #setUrl} with this value
31556  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31557  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31558  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31559  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31560
31561  * @constructor
31562  * Create a new ContentPanel.
31563  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31564  * @param {String/Object} config A string to set only the title or a config object
31565  * @param {String} content (optional) Set the HTML content for this panel
31566  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31567  */
31568 Roo.ContentPanel = function(el, config, content){
31569     
31570      
31571     /*
31572     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31573         config = el;
31574         el = Roo.id();
31575     }
31576     if (config && config.parentLayout) { 
31577         el = config.parentLayout.el.createChild(); 
31578     }
31579     */
31580     if(el.autoCreate){ // xtype is available if this is called from factory
31581         config = el;
31582         el = Roo.id();
31583     }
31584     this.el = Roo.get(el);
31585     if(!this.el && config && config.autoCreate){
31586         if(typeof config.autoCreate == "object"){
31587             if(!config.autoCreate.id){
31588                 config.autoCreate.id = config.id||el;
31589             }
31590             this.el = Roo.DomHelper.append(document.body,
31591                         config.autoCreate, true);
31592         }else{
31593             this.el = Roo.DomHelper.append(document.body,
31594                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31595         }
31596     }
31597     this.closable = false;
31598     this.loaded = false;
31599     this.active = false;
31600     if(typeof config == "string"){
31601         this.title = config;
31602     }else{
31603         Roo.apply(this, config);
31604     }
31605     
31606     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31607         this.wrapEl = this.el.wrap();
31608         this.toolbar.container = this.el.insertSibling(false, 'before');
31609         this.toolbar = new Roo.Toolbar(this.toolbar);
31610     }
31611     
31612     // xtype created footer. - not sure if will work as we normally have to render first..
31613     if (this.footer && !this.footer.el && this.footer.xtype) {
31614         if (!this.wrapEl) {
31615             this.wrapEl = this.el.wrap();
31616         }
31617     
31618         this.footer.container = this.wrapEl.createChild();
31619          
31620         this.footer = Roo.factory(this.footer, Roo);
31621         
31622     }
31623     
31624     if(this.resizeEl){
31625         this.resizeEl = Roo.get(this.resizeEl, true);
31626     }else{
31627         this.resizeEl = this.el;
31628     }
31629     // handle view.xtype
31630     
31631  
31632     
31633     
31634     this.addEvents({
31635         /**
31636          * @event activate
31637          * Fires when this panel is activated. 
31638          * @param {Roo.ContentPanel} this
31639          */
31640         "activate" : true,
31641         /**
31642          * @event deactivate
31643          * Fires when this panel is activated. 
31644          * @param {Roo.ContentPanel} this
31645          */
31646         "deactivate" : true,
31647
31648         /**
31649          * @event resize
31650          * Fires when this panel is resized if fitToFrame is true.
31651          * @param {Roo.ContentPanel} this
31652          * @param {Number} width The width after any component adjustments
31653          * @param {Number} height The height after any component adjustments
31654          */
31655         "resize" : true,
31656         
31657          /**
31658          * @event render
31659          * Fires when this tab is created
31660          * @param {Roo.ContentPanel} this
31661          */
31662         "render" : true
31663          
31664         
31665     });
31666     
31667
31668     
31669     
31670     if(this.autoScroll){
31671         this.resizeEl.setStyle("overflow", "auto");
31672     } else {
31673         // fix randome scrolling
31674         this.el.on('scroll', function() {
31675             Roo.log('fix random scolling');
31676             this.scrollTo('top',0); 
31677         });
31678     }
31679     content = content || this.content;
31680     if(content){
31681         this.setContent(content);
31682     }
31683     if(config && config.url){
31684         this.setUrl(this.url, this.params, this.loadOnce);
31685     }
31686     
31687     
31688     
31689     Roo.ContentPanel.superclass.constructor.call(this);
31690     
31691     if (this.view && typeof(this.view.xtype) != 'undefined') {
31692         this.view.el = this.el.appendChild(document.createElement("div"));
31693         this.view = Roo.factory(this.view); 
31694         this.view.render  &&  this.view.render(false, '');  
31695     }
31696     
31697     
31698     this.fireEvent('render', this);
31699 };
31700
31701 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31702     tabTip:'',
31703     setRegion : function(region){
31704         this.region = region;
31705         if(region){
31706            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31707         }else{
31708            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31709         } 
31710     },
31711     
31712     /**
31713      * Returns the toolbar for this Panel if one was configured. 
31714      * @return {Roo.Toolbar} 
31715      */
31716     getToolbar : function(){
31717         return this.toolbar;
31718     },
31719     
31720     setActiveState : function(active){
31721         this.active = active;
31722         if(!active){
31723             this.fireEvent("deactivate", this);
31724         }else{
31725             this.fireEvent("activate", this);
31726         }
31727     },
31728     /**
31729      * Updates this panel's element
31730      * @param {String} content The new content
31731      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31732     */
31733     setContent : function(content, loadScripts){
31734         this.el.update(content, loadScripts);
31735     },
31736
31737     ignoreResize : function(w, h){
31738         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31739             return true;
31740         }else{
31741             this.lastSize = {width: w, height: h};
31742             return false;
31743         }
31744     },
31745     /**
31746      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31747      * @return {Roo.UpdateManager} The UpdateManager
31748      */
31749     getUpdateManager : function(){
31750         return this.el.getUpdateManager();
31751     },
31752      /**
31753      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31754      * @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:
31755 <pre><code>
31756 panel.load({
31757     url: "your-url.php",
31758     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31759     callback: yourFunction,
31760     scope: yourObject, //(optional scope)
31761     discardUrl: false,
31762     nocache: false,
31763     text: "Loading...",
31764     timeout: 30,
31765     scripts: false
31766 });
31767 </code></pre>
31768      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31769      * 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.
31770      * @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}
31771      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31772      * @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.
31773      * @return {Roo.ContentPanel} this
31774      */
31775     load : function(){
31776         var um = this.el.getUpdateManager();
31777         um.update.apply(um, arguments);
31778         return this;
31779     },
31780
31781
31782     /**
31783      * 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.
31784      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31785      * @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)
31786      * @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)
31787      * @return {Roo.UpdateManager} The UpdateManager
31788      */
31789     setUrl : function(url, params, loadOnce){
31790         if(this.refreshDelegate){
31791             this.removeListener("activate", this.refreshDelegate);
31792         }
31793         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31794         this.on("activate", this.refreshDelegate);
31795         return this.el.getUpdateManager();
31796     },
31797     
31798     _handleRefresh : function(url, params, loadOnce){
31799         if(!loadOnce || !this.loaded){
31800             var updater = this.el.getUpdateManager();
31801             updater.update(url, params, this._setLoaded.createDelegate(this));
31802         }
31803     },
31804     
31805     _setLoaded : function(){
31806         this.loaded = true;
31807     }, 
31808     
31809     /**
31810      * Returns this panel's id
31811      * @return {String} 
31812      */
31813     getId : function(){
31814         return this.el.id;
31815     },
31816     
31817     /** 
31818      * Returns this panel's element - used by regiosn to add.
31819      * @return {Roo.Element} 
31820      */
31821     getEl : function(){
31822         return this.wrapEl || this.el;
31823     },
31824     
31825     adjustForComponents : function(width, height)
31826     {
31827         //Roo.log('adjustForComponents ');
31828         if(this.resizeEl != this.el){
31829             width -= this.el.getFrameWidth('lr');
31830             height -= this.el.getFrameWidth('tb');
31831         }
31832         if(this.toolbar){
31833             var te = this.toolbar.getEl();
31834             height -= te.getHeight();
31835             te.setWidth(width);
31836         }
31837         if(this.footer){
31838             var te = this.footer.getEl();
31839             //Roo.log("footer:" + te.getHeight());
31840             
31841             height -= te.getHeight();
31842             te.setWidth(width);
31843         }
31844         
31845         
31846         if(this.adjustments){
31847             width += this.adjustments[0];
31848             height += this.adjustments[1];
31849         }
31850         return {"width": width, "height": height};
31851     },
31852     
31853     setSize : function(width, height){
31854         if(this.fitToFrame && !this.ignoreResize(width, height)){
31855             if(this.fitContainer && this.resizeEl != this.el){
31856                 this.el.setSize(width, height);
31857             }
31858             var size = this.adjustForComponents(width, height);
31859             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31860             this.fireEvent('resize', this, size.width, size.height);
31861         }
31862     },
31863     
31864     /**
31865      * Returns this panel's title
31866      * @return {String} 
31867      */
31868     getTitle : function(){
31869         return this.title;
31870     },
31871     
31872     /**
31873      * Set this panel's title
31874      * @param {String} title
31875      */
31876     setTitle : function(title){
31877         this.title = title;
31878         if(this.region){
31879             this.region.updatePanelTitle(this, title);
31880         }
31881     },
31882     
31883     /**
31884      * Returns true is this panel was configured to be closable
31885      * @return {Boolean} 
31886      */
31887     isClosable : function(){
31888         return this.closable;
31889     },
31890     
31891     beforeSlide : function(){
31892         this.el.clip();
31893         this.resizeEl.clip();
31894     },
31895     
31896     afterSlide : function(){
31897         this.el.unclip();
31898         this.resizeEl.unclip();
31899     },
31900     
31901     /**
31902      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31903      *   Will fail silently if the {@link #setUrl} method has not been called.
31904      *   This does not activate the panel, just updates its content.
31905      */
31906     refresh : function(){
31907         if(this.refreshDelegate){
31908            this.loaded = false;
31909            this.refreshDelegate();
31910         }
31911     },
31912     
31913     /**
31914      * Destroys this panel
31915      */
31916     destroy : function(){
31917         this.el.removeAllListeners();
31918         var tempEl = document.createElement("span");
31919         tempEl.appendChild(this.el.dom);
31920         tempEl.innerHTML = "";
31921         this.el.remove();
31922         this.el = null;
31923     },
31924     
31925     /**
31926      * form - if the content panel contains a form - this is a reference to it.
31927      * @type {Roo.form.Form}
31928      */
31929     form : false,
31930     /**
31931      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31932      *    This contains a reference to it.
31933      * @type {Roo.View}
31934      */
31935     view : false,
31936     
31937       /**
31938      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31939      * <pre><code>
31940
31941 layout.addxtype({
31942        xtype : 'Form',
31943        items: [ .... ]
31944    }
31945 );
31946
31947 </code></pre>
31948      * @param {Object} cfg Xtype definition of item to add.
31949      */
31950     
31951     addxtype : function(cfg) {
31952         // add form..
31953         if (cfg.xtype.match(/^Form$/)) {
31954             
31955             var el;
31956             //if (this.footer) {
31957             //    el = this.footer.container.insertSibling(false, 'before');
31958             //} else {
31959                 el = this.el.createChild();
31960             //}
31961
31962             this.form = new  Roo.form.Form(cfg);
31963             
31964             
31965             if ( this.form.allItems.length) {
31966                 this.form.render(el.dom);
31967             }
31968             return this.form;
31969         }
31970         // should only have one of theses..
31971         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31972             // views.. should not be just added - used named prop 'view''
31973             
31974             cfg.el = this.el.appendChild(document.createElement("div"));
31975             // factory?
31976             
31977             var ret = new Roo.factory(cfg);
31978              
31979              ret.render && ret.render(false, ''); // render blank..
31980             this.view = ret;
31981             return ret;
31982         }
31983         return false;
31984     }
31985 });
31986
31987 /**
31988  * @class Roo.GridPanel
31989  * @extends Roo.ContentPanel
31990  * @constructor
31991  * Create a new GridPanel.
31992  * @param {Roo.grid.Grid} grid The grid for this panel
31993  * @param {String/Object} config A string to set only the panel's title, or a config object
31994  */
31995 Roo.GridPanel = function(grid, config){
31996     
31997   
31998     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31999         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32000         
32001     this.wrapper.dom.appendChild(grid.getGridEl().dom);
32002     
32003     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32004     
32005     if(this.toolbar){
32006         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32007     }
32008     // xtype created footer. - not sure if will work as we normally have to render first..
32009     if (this.footer && !this.footer.el && this.footer.xtype) {
32010         
32011         this.footer.container = this.grid.getView().getFooterPanel(true);
32012         this.footer.dataSource = this.grid.dataSource;
32013         this.footer = Roo.factory(this.footer, Roo);
32014         
32015     }
32016     
32017     grid.monitorWindowResize = false; // turn off autosizing
32018     grid.autoHeight = false;
32019     grid.autoWidth = false;
32020     this.grid = grid;
32021     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32022 };
32023
32024 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32025     getId : function(){
32026         return this.grid.id;
32027     },
32028     
32029     /**
32030      * Returns the grid for this panel
32031      * @return {Roo.grid.Grid} 
32032      */
32033     getGrid : function(){
32034         return this.grid;    
32035     },
32036     
32037     setSize : function(width, height){
32038         if(!this.ignoreResize(width, height)){
32039             var grid = this.grid;
32040             var size = this.adjustForComponents(width, height);
32041             grid.getGridEl().setSize(size.width, size.height);
32042             grid.autoSize();
32043         }
32044     },
32045     
32046     beforeSlide : function(){
32047         this.grid.getView().scroller.clip();
32048     },
32049     
32050     afterSlide : function(){
32051         this.grid.getView().scroller.unclip();
32052     },
32053     
32054     destroy : function(){
32055         this.grid.destroy();
32056         delete this.grid;
32057         Roo.GridPanel.superclass.destroy.call(this); 
32058     }
32059 });
32060
32061
32062 /**
32063  * @class Roo.NestedLayoutPanel
32064  * @extends Roo.ContentPanel
32065  * @constructor
32066  * Create a new NestedLayoutPanel.
32067  * 
32068  * 
32069  * @param {Roo.BorderLayout} layout The layout for this panel
32070  * @param {String/Object} config A string to set only the title or a config object
32071  */
32072 Roo.NestedLayoutPanel = function(layout, config)
32073 {
32074     // construct with only one argument..
32075     /* FIXME - implement nicer consturctors
32076     if (layout.layout) {
32077         config = layout;
32078         layout = config.layout;
32079         delete config.layout;
32080     }
32081     if (layout.xtype && !layout.getEl) {
32082         // then layout needs constructing..
32083         layout = Roo.factory(layout, Roo);
32084     }
32085     */
32086     
32087     
32088     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32089     
32090     layout.monitorWindowResize = false; // turn off autosizing
32091     this.layout = layout;
32092     this.layout.getEl().addClass("x-layout-nested-layout");
32093     
32094     
32095     
32096     
32097 };
32098
32099 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32100
32101     setSize : function(width, height){
32102         if(!this.ignoreResize(width, height)){
32103             var size = this.adjustForComponents(width, height);
32104             var el = this.layout.getEl();
32105             el.setSize(size.width, size.height);
32106             var touch = el.dom.offsetWidth;
32107             this.layout.layout();
32108             // ie requires a double layout on the first pass
32109             if(Roo.isIE && !this.initialized){
32110                 this.initialized = true;
32111                 this.layout.layout();
32112             }
32113         }
32114     },
32115     
32116     // activate all subpanels if not currently active..
32117     
32118     setActiveState : function(active){
32119         this.active = active;
32120         if(!active){
32121             this.fireEvent("deactivate", this);
32122             return;
32123         }
32124         
32125         this.fireEvent("activate", this);
32126         // not sure if this should happen before or after..
32127         if (!this.layout) {
32128             return; // should not happen..
32129         }
32130         var reg = false;
32131         for (var r in this.layout.regions) {
32132             reg = this.layout.getRegion(r);
32133             if (reg.getActivePanel()) {
32134                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32135                 reg.setActivePanel(reg.getActivePanel());
32136                 continue;
32137             }
32138             if (!reg.panels.length) {
32139                 continue;
32140             }
32141             reg.showPanel(reg.getPanel(0));
32142         }
32143         
32144         
32145         
32146         
32147     },
32148     
32149     /**
32150      * Returns the nested BorderLayout for this panel
32151      * @return {Roo.BorderLayout} 
32152      */
32153     getLayout : function(){
32154         return this.layout;
32155     },
32156     
32157      /**
32158      * Adds a xtype elements to the layout of the nested panel
32159      * <pre><code>
32160
32161 panel.addxtype({
32162        xtype : 'ContentPanel',
32163        region: 'west',
32164        items: [ .... ]
32165    }
32166 );
32167
32168 panel.addxtype({
32169         xtype : 'NestedLayoutPanel',
32170         region: 'west',
32171         layout: {
32172            center: { },
32173            west: { }   
32174         },
32175         items : [ ... list of content panels or nested layout panels.. ]
32176    }
32177 );
32178 </code></pre>
32179      * @param {Object} cfg Xtype definition of item to add.
32180      */
32181     addxtype : function(cfg) {
32182         return this.layout.addxtype(cfg);
32183     
32184     }
32185 });
32186
32187 Roo.ScrollPanel = function(el, config, content){
32188     config = config || {};
32189     config.fitToFrame = true;
32190     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32191     
32192     this.el.dom.style.overflow = "hidden";
32193     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32194     this.el.removeClass("x-layout-inactive-content");
32195     this.el.on("mousewheel", this.onWheel, this);
32196
32197     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32198     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32199     up.unselectable(); down.unselectable();
32200     up.on("click", this.scrollUp, this);
32201     down.on("click", this.scrollDown, this);
32202     up.addClassOnOver("x-scroller-btn-over");
32203     down.addClassOnOver("x-scroller-btn-over");
32204     up.addClassOnClick("x-scroller-btn-click");
32205     down.addClassOnClick("x-scroller-btn-click");
32206     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32207
32208     this.resizeEl = this.el;
32209     this.el = wrap; this.up = up; this.down = down;
32210 };
32211
32212 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32213     increment : 100,
32214     wheelIncrement : 5,
32215     scrollUp : function(){
32216         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32217     },
32218
32219     scrollDown : function(){
32220         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32221     },
32222
32223     afterScroll : function(){
32224         var el = this.resizeEl;
32225         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32226         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32227         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32228     },
32229
32230     setSize : function(){
32231         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32232         this.afterScroll();
32233     },
32234
32235     onWheel : function(e){
32236         var d = e.getWheelDelta();
32237         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32238         this.afterScroll();
32239         e.stopEvent();
32240     },
32241
32242     setContent : function(content, loadScripts){
32243         this.resizeEl.update(content, loadScripts);
32244     }
32245
32246 });
32247
32248
32249
32250
32251
32252
32253
32254
32255
32256 /**
32257  * @class Roo.TreePanel
32258  * @extends Roo.ContentPanel
32259  * @constructor
32260  * Create a new TreePanel. - defaults to fit/scoll contents.
32261  * @param {String/Object} config A string to set only the panel's title, or a config object
32262  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32263  */
32264 Roo.TreePanel = function(config){
32265     var el = config.el;
32266     var tree = config.tree;
32267     delete config.tree; 
32268     delete config.el; // hopefull!
32269     
32270     // wrapper for IE7 strict & safari scroll issue
32271     
32272     var treeEl = el.createChild();
32273     config.resizeEl = treeEl;
32274     
32275     
32276     
32277     Roo.TreePanel.superclass.constructor.call(this, el, config);
32278  
32279  
32280     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32281     //console.log(tree);
32282     this.on('activate', function()
32283     {
32284         if (this.tree.rendered) {
32285             return;
32286         }
32287         //console.log('render tree');
32288         this.tree.render();
32289     });
32290     // this should not be needed.. - it's actually the 'el' that resizes?
32291     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32292     
32293     //this.on('resize',  function (cp, w, h) {
32294     //        this.tree.innerCt.setWidth(w);
32295     //        this.tree.innerCt.setHeight(h);
32296     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32297     //});
32298
32299         
32300     
32301 };
32302
32303 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32304     fitToFrame : true,
32305     autoScroll : true
32306 });
32307
32308
32309
32310
32311
32312
32313
32314
32315
32316
32317
32318 /*
32319  * Based on:
32320  * Ext JS Library 1.1.1
32321  * Copyright(c) 2006-2007, Ext JS, LLC.
32322  *
32323  * Originally Released Under LGPL - original licence link has changed is not relivant.
32324  *
32325  * Fork - LGPL
32326  * <script type="text/javascript">
32327  */
32328  
32329
32330 /**
32331  * @class Roo.ReaderLayout
32332  * @extends Roo.BorderLayout
32333  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32334  * center region containing two nested regions (a top one for a list view and one for item preview below),
32335  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32336  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32337  * expedites the setup of the overall layout and regions for this common application style.
32338  * Example:
32339  <pre><code>
32340 var reader = new Roo.ReaderLayout();
32341 var CP = Roo.ContentPanel;  // shortcut for adding
32342
32343 reader.beginUpdate();
32344 reader.add("north", new CP("north", "North"));
32345 reader.add("west", new CP("west", {title: "West"}));
32346 reader.add("east", new CP("east", {title: "East"}));
32347
32348 reader.regions.listView.add(new CP("listView", "List"));
32349 reader.regions.preview.add(new CP("preview", "Preview"));
32350 reader.endUpdate();
32351 </code></pre>
32352 * @constructor
32353 * Create a new ReaderLayout
32354 * @param {Object} config Configuration options
32355 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32356 * document.body if omitted)
32357 */
32358 Roo.ReaderLayout = function(config, renderTo){
32359     var c = config || {size:{}};
32360     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32361         north: c.north !== false ? Roo.apply({
32362             split:false,
32363             initialSize: 32,
32364             titlebar: false
32365         }, c.north) : false,
32366         west: c.west !== false ? Roo.apply({
32367             split:true,
32368             initialSize: 200,
32369             minSize: 175,
32370             maxSize: 400,
32371             titlebar: true,
32372             collapsible: true,
32373             animate: true,
32374             margins:{left:5,right:0,bottom:5,top:5},
32375             cmargins:{left:5,right:5,bottom:5,top:5}
32376         }, c.west) : false,
32377         east: c.east !== false ? Roo.apply({
32378             split:true,
32379             initialSize: 200,
32380             minSize: 175,
32381             maxSize: 400,
32382             titlebar: true,
32383             collapsible: true,
32384             animate: true,
32385             margins:{left:0,right:5,bottom:5,top:5},
32386             cmargins:{left:5,right:5,bottom:5,top:5}
32387         }, c.east) : false,
32388         center: Roo.apply({
32389             tabPosition: 'top',
32390             autoScroll:false,
32391             closeOnTab: true,
32392             titlebar:false,
32393             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32394         }, c.center)
32395     });
32396
32397     this.el.addClass('x-reader');
32398
32399     this.beginUpdate();
32400
32401     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32402         south: c.preview !== false ? Roo.apply({
32403             split:true,
32404             initialSize: 200,
32405             minSize: 100,
32406             autoScroll:true,
32407             collapsible:true,
32408             titlebar: true,
32409             cmargins:{top:5,left:0, right:0, bottom:0}
32410         }, c.preview) : false,
32411         center: Roo.apply({
32412             autoScroll:false,
32413             titlebar:false,
32414             minHeight:200
32415         }, c.listView)
32416     });
32417     this.add('center', new Roo.NestedLayoutPanel(inner,
32418             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32419
32420     this.endUpdate();
32421
32422     this.regions.preview = inner.getRegion('south');
32423     this.regions.listView = inner.getRegion('center');
32424 };
32425
32426 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32427  * Based on:
32428  * Ext JS Library 1.1.1
32429  * Copyright(c) 2006-2007, Ext JS, LLC.
32430  *
32431  * Originally Released Under LGPL - original licence link has changed is not relivant.
32432  *
32433  * Fork - LGPL
32434  * <script type="text/javascript">
32435  */
32436  
32437 /**
32438  * @class Roo.grid.Grid
32439  * @extends Roo.util.Observable
32440  * This class represents the primary interface of a component based grid control.
32441  * <br><br>Usage:<pre><code>
32442  var grid = new Roo.grid.Grid("my-container-id", {
32443      ds: myDataStore,
32444      cm: myColModel,
32445      selModel: mySelectionModel,
32446      autoSizeColumns: true,
32447      monitorWindowResize: false,
32448      trackMouseOver: true
32449  });
32450  // set any options
32451  grid.render();
32452  * </code></pre>
32453  * <b>Common Problems:</b><br/>
32454  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32455  * element will correct this<br/>
32456  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32457  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32458  * are unpredictable.<br/>
32459  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32460  * grid to calculate dimensions/offsets.<br/>
32461   * @constructor
32462  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32463  * The container MUST have some type of size defined for the grid to fill. The container will be
32464  * automatically set to position relative if it isn't already.
32465  * @param {Object} config A config object that sets properties on this grid.
32466  */
32467 Roo.grid.Grid = function(container, config){
32468         // initialize the container
32469         this.container = Roo.get(container);
32470         this.container.update("");
32471         this.container.setStyle("overflow", "hidden");
32472     this.container.addClass('x-grid-container');
32473
32474     this.id = this.container.id;
32475
32476     Roo.apply(this, config);
32477     // check and correct shorthanded configs
32478     if(this.ds){
32479         this.dataSource = this.ds;
32480         delete this.ds;
32481     }
32482     if(this.cm){
32483         this.colModel = this.cm;
32484         delete this.cm;
32485     }
32486     if(this.sm){
32487         this.selModel = this.sm;
32488         delete this.sm;
32489     }
32490
32491     if (this.selModel) {
32492         this.selModel = Roo.factory(this.selModel, Roo.grid);
32493         this.sm = this.selModel;
32494         this.sm.xmodule = this.xmodule || false;
32495     }
32496     if (typeof(this.colModel.config) == 'undefined') {
32497         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32498         this.cm = this.colModel;
32499         this.cm.xmodule = this.xmodule || false;
32500     }
32501     if (this.dataSource) {
32502         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32503         this.ds = this.dataSource;
32504         this.ds.xmodule = this.xmodule || false;
32505          
32506     }
32507     
32508     
32509     
32510     if(this.width){
32511         this.container.setWidth(this.width);
32512     }
32513
32514     if(this.height){
32515         this.container.setHeight(this.height);
32516     }
32517     /** @private */
32518         this.addEvents({
32519         // raw events
32520         /**
32521          * @event click
32522          * The raw click event for the entire grid.
32523          * @param {Roo.EventObject} e
32524          */
32525         "click" : true,
32526         /**
32527          * @event dblclick
32528          * The raw dblclick event for the entire grid.
32529          * @param {Roo.EventObject} e
32530          */
32531         "dblclick" : true,
32532         /**
32533          * @event contextmenu
32534          * The raw contextmenu event for the entire grid.
32535          * @param {Roo.EventObject} e
32536          */
32537         "contextmenu" : true,
32538         /**
32539          * @event mousedown
32540          * The raw mousedown event for the entire grid.
32541          * @param {Roo.EventObject} e
32542          */
32543         "mousedown" : true,
32544         /**
32545          * @event mouseup
32546          * The raw mouseup event for the entire grid.
32547          * @param {Roo.EventObject} e
32548          */
32549         "mouseup" : true,
32550         /**
32551          * @event mouseover
32552          * The raw mouseover event for the entire grid.
32553          * @param {Roo.EventObject} e
32554          */
32555         "mouseover" : true,
32556         /**
32557          * @event mouseout
32558          * The raw mouseout event for the entire grid.
32559          * @param {Roo.EventObject} e
32560          */
32561         "mouseout" : true,
32562         /**
32563          * @event keypress
32564          * The raw keypress event for the entire grid.
32565          * @param {Roo.EventObject} e
32566          */
32567         "keypress" : true,
32568         /**
32569          * @event keydown
32570          * The raw keydown event for the entire grid.
32571          * @param {Roo.EventObject} e
32572          */
32573         "keydown" : true,
32574
32575         // custom events
32576
32577         /**
32578          * @event cellclick
32579          * Fires when a cell is clicked
32580          * @param {Grid} this
32581          * @param {Number} rowIndex
32582          * @param {Number} columnIndex
32583          * @param {Roo.EventObject} e
32584          */
32585         "cellclick" : true,
32586         /**
32587          * @event celldblclick
32588          * Fires when a cell is double clicked
32589          * @param {Grid} this
32590          * @param {Number} rowIndex
32591          * @param {Number} columnIndex
32592          * @param {Roo.EventObject} e
32593          */
32594         "celldblclick" : true,
32595         /**
32596          * @event rowclick
32597          * Fires when a row is clicked
32598          * @param {Grid} this
32599          * @param {Number} rowIndex
32600          * @param {Roo.EventObject} e
32601          */
32602         "rowclick" : true,
32603         /**
32604          * @event rowdblclick
32605          * Fires when a row is double clicked
32606          * @param {Grid} this
32607          * @param {Number} rowIndex
32608          * @param {Roo.EventObject} e
32609          */
32610         "rowdblclick" : true,
32611         /**
32612          * @event headerclick
32613          * Fires when a header is clicked
32614          * @param {Grid} this
32615          * @param {Number} columnIndex
32616          * @param {Roo.EventObject} e
32617          */
32618         "headerclick" : true,
32619         /**
32620          * @event headerdblclick
32621          * Fires when a header cell is double clicked
32622          * @param {Grid} this
32623          * @param {Number} columnIndex
32624          * @param {Roo.EventObject} e
32625          */
32626         "headerdblclick" : true,
32627         /**
32628          * @event rowcontextmenu
32629          * Fires when a row is right clicked
32630          * @param {Grid} this
32631          * @param {Number} rowIndex
32632          * @param {Roo.EventObject} e
32633          */
32634         "rowcontextmenu" : true,
32635         /**
32636          * @event cellcontextmenu
32637          * Fires when a cell is right clicked
32638          * @param {Grid} this
32639          * @param {Number} rowIndex
32640          * @param {Number} cellIndex
32641          * @param {Roo.EventObject} e
32642          */
32643          "cellcontextmenu" : true,
32644         /**
32645          * @event headercontextmenu
32646          * Fires when a header is right clicked
32647          * @param {Grid} this
32648          * @param {Number} columnIndex
32649          * @param {Roo.EventObject} e
32650          */
32651         "headercontextmenu" : true,
32652         /**
32653          * @event bodyscroll
32654          * Fires when the body element is scrolled
32655          * @param {Number} scrollLeft
32656          * @param {Number} scrollTop
32657          */
32658         "bodyscroll" : true,
32659         /**
32660          * @event columnresize
32661          * Fires when the user resizes a column
32662          * @param {Number} columnIndex
32663          * @param {Number} newSize
32664          */
32665         "columnresize" : true,
32666         /**
32667          * @event columnmove
32668          * Fires when the user moves a column
32669          * @param {Number} oldIndex
32670          * @param {Number} newIndex
32671          */
32672         "columnmove" : true,
32673         /**
32674          * @event startdrag
32675          * Fires when row(s) start being dragged
32676          * @param {Grid} this
32677          * @param {Roo.GridDD} dd The drag drop object
32678          * @param {event} e The raw browser event
32679          */
32680         "startdrag" : true,
32681         /**
32682          * @event enddrag
32683          * Fires when a drag operation is complete
32684          * @param {Grid} this
32685          * @param {Roo.GridDD} dd The drag drop object
32686          * @param {event} e The raw browser event
32687          */
32688         "enddrag" : true,
32689         /**
32690          * @event dragdrop
32691          * Fires when dragged row(s) are dropped on a valid DD target
32692          * @param {Grid} this
32693          * @param {Roo.GridDD} dd The drag drop object
32694          * @param {String} targetId The target drag drop object
32695          * @param {event} e The raw browser event
32696          */
32697         "dragdrop" : true,
32698         /**
32699          * @event dragover
32700          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32701          * @param {Grid} this
32702          * @param {Roo.GridDD} dd The drag drop object
32703          * @param {String} targetId The target drag drop object
32704          * @param {event} e The raw browser event
32705          */
32706         "dragover" : true,
32707         /**
32708          * @event dragenter
32709          *  Fires when the dragged row(s) first cross another DD target while being dragged
32710          * @param {Grid} this
32711          * @param {Roo.GridDD} dd The drag drop object
32712          * @param {String} targetId The target drag drop object
32713          * @param {event} e The raw browser event
32714          */
32715         "dragenter" : true,
32716         /**
32717          * @event dragout
32718          * Fires when the dragged row(s) leave another DD target while being dragged
32719          * @param {Grid} this
32720          * @param {Roo.GridDD} dd The drag drop object
32721          * @param {String} targetId The target drag drop object
32722          * @param {event} e The raw browser event
32723          */
32724         "dragout" : true,
32725         /**
32726          * @event rowclass
32727          * Fires when a row is rendered, so you can change add a style to it.
32728          * @param {GridView} gridview   The grid view
32729          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32730          */
32731         'rowclass' : true,
32732
32733         /**
32734          * @event render
32735          * Fires when the grid is rendered
32736          * @param {Grid} grid
32737          */
32738         'render' : true
32739     });
32740
32741     Roo.grid.Grid.superclass.constructor.call(this);
32742 };
32743 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32744     
32745     /**
32746      * @cfg {String} ddGroup - drag drop group.
32747      */
32748
32749     /**
32750      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32751      */
32752     minColumnWidth : 25,
32753
32754     /**
32755      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32756      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32757      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32758      */
32759     autoSizeColumns : false,
32760
32761     /**
32762      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32763      */
32764     autoSizeHeaders : true,
32765
32766     /**
32767      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32768      */
32769     monitorWindowResize : true,
32770
32771     /**
32772      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32773      * rows measured to get a columns size. Default is 0 (all rows).
32774      */
32775     maxRowsToMeasure : 0,
32776
32777     /**
32778      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32779      */
32780     trackMouseOver : true,
32781
32782     /**
32783     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32784     */
32785     
32786     /**
32787     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32788     */
32789     enableDragDrop : false,
32790     
32791     /**
32792     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32793     */
32794     enableColumnMove : true,
32795     
32796     /**
32797     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32798     */
32799     enableColumnHide : true,
32800     
32801     /**
32802     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32803     */
32804     enableRowHeightSync : false,
32805     
32806     /**
32807     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32808     */
32809     stripeRows : true,
32810     
32811     /**
32812     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32813     */
32814     autoHeight : false,
32815
32816     /**
32817      * @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.
32818      */
32819     autoExpandColumn : false,
32820
32821     /**
32822     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32823     * Default is 50.
32824     */
32825     autoExpandMin : 50,
32826
32827     /**
32828     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32829     */
32830     autoExpandMax : 1000,
32831
32832     /**
32833     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32834     */
32835     view : null,
32836
32837     /**
32838     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32839     */
32840     loadMask : false,
32841     /**
32842     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32843     */
32844     dropTarget: false,
32845     
32846    
32847     
32848     // private
32849     rendered : false,
32850
32851     /**
32852     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32853     * of a fixed width. Default is false.
32854     */
32855     /**
32856     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32857     */
32858     /**
32859      * Called once after all setup has been completed and the grid is ready to be rendered.
32860      * @return {Roo.grid.Grid} this
32861      */
32862     render : function()
32863     {
32864         var c = this.container;
32865         // try to detect autoHeight/width mode
32866         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32867             this.autoHeight = true;
32868         }
32869         var view = this.getView();
32870         view.init(this);
32871
32872         c.on("click", this.onClick, this);
32873         c.on("dblclick", this.onDblClick, this);
32874         c.on("contextmenu", this.onContextMenu, this);
32875         c.on("keydown", this.onKeyDown, this);
32876         if (Roo.isTouch) {
32877             c.on("touchstart", this.onTouchStart, this);
32878         }
32879
32880         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32881
32882         this.getSelectionModel().init(this);
32883
32884         view.render();
32885
32886         if(this.loadMask){
32887             this.loadMask = new Roo.LoadMask(this.container,
32888                     Roo.apply({store:this.dataSource}, this.loadMask));
32889         }
32890         
32891         
32892         if (this.toolbar && this.toolbar.xtype) {
32893             this.toolbar.container = this.getView().getHeaderPanel(true);
32894             this.toolbar = new Roo.Toolbar(this.toolbar);
32895         }
32896         if (this.footer && this.footer.xtype) {
32897             this.footer.dataSource = this.getDataSource();
32898             this.footer.container = this.getView().getFooterPanel(true);
32899             this.footer = Roo.factory(this.footer, Roo);
32900         }
32901         if (this.dropTarget && this.dropTarget.xtype) {
32902             delete this.dropTarget.xtype;
32903             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32904         }
32905         
32906         
32907         this.rendered = true;
32908         this.fireEvent('render', this);
32909         return this;
32910     },
32911
32912         /**
32913          * Reconfigures the grid to use a different Store and Column Model.
32914          * The View will be bound to the new objects and refreshed.
32915          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32916          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32917          */
32918     reconfigure : function(dataSource, colModel){
32919         if(this.loadMask){
32920             this.loadMask.destroy();
32921             this.loadMask = new Roo.LoadMask(this.container,
32922                     Roo.apply({store:dataSource}, this.loadMask));
32923         }
32924         this.view.bind(dataSource, colModel);
32925         this.dataSource = dataSource;
32926         this.colModel = colModel;
32927         this.view.refresh(true);
32928     },
32929
32930     // private
32931     onKeyDown : function(e){
32932         this.fireEvent("keydown", e);
32933     },
32934
32935     /**
32936      * Destroy this grid.
32937      * @param {Boolean} removeEl True to remove the element
32938      */
32939     destroy : function(removeEl, keepListeners){
32940         if(this.loadMask){
32941             this.loadMask.destroy();
32942         }
32943         var c = this.container;
32944         c.removeAllListeners();
32945         this.view.destroy();
32946         this.colModel.purgeListeners();
32947         if(!keepListeners){
32948             this.purgeListeners();
32949         }
32950         c.update("");
32951         if(removeEl === true){
32952             c.remove();
32953         }
32954     },
32955
32956     // private
32957     processEvent : function(name, e){
32958         // does this fire select???
32959         //Roo.log('grid:processEvent '  + name);
32960         
32961         if (name != 'touchstart' ) {
32962             this.fireEvent(name, e);    
32963         }
32964         
32965         var t = e.getTarget();
32966         var v = this.view;
32967         var header = v.findHeaderIndex(t);
32968         if(header !== false){
32969             var ename = name == 'touchstart' ? 'click' : name;
32970              
32971             this.fireEvent("header" + ename, this, header, e);
32972         }else{
32973             var row = v.findRowIndex(t);
32974             var cell = v.findCellIndex(t);
32975             if (name == 'touchstart') {
32976                 // first touch is always a click.
32977                 // hopefull this happens after selection is updated.?
32978                 name = false;
32979                 
32980                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32981                     var cs = this.selModel.getSelectedCell();
32982                     if (row == cs[0] && cell == cs[1]){
32983                         name = 'dblclick';
32984                     }
32985                 }
32986                 if (typeof(this.selModel.getSelections) != 'undefined') {
32987                     var cs = this.selModel.getSelections();
32988                     var ds = this.dataSource;
32989                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32990                         name = 'dblclick';
32991                     }
32992                 }
32993                 if (!name) {
32994                     return;
32995                 }
32996             }
32997             
32998             
32999             if(row !== false){
33000                 this.fireEvent("row" + name, this, row, e);
33001                 if(cell !== false){
33002                     this.fireEvent("cell" + name, this, row, cell, e);
33003                 }
33004             }
33005         }
33006     },
33007
33008     // private
33009     onClick : function(e){
33010         this.processEvent("click", e);
33011     },
33012    // private
33013     onTouchStart : function(e){
33014         this.processEvent("touchstart", e);
33015     },
33016
33017     // private
33018     onContextMenu : function(e, t){
33019         this.processEvent("contextmenu", e);
33020     },
33021
33022     // private
33023     onDblClick : function(e){
33024         this.processEvent("dblclick", e);
33025     },
33026
33027     // private
33028     walkCells : function(row, col, step, fn, scope){
33029         var cm = this.colModel, clen = cm.getColumnCount();
33030         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33031         if(step < 0){
33032             if(col < 0){
33033                 row--;
33034                 first = false;
33035             }
33036             while(row >= 0){
33037                 if(!first){
33038                     col = clen-1;
33039                 }
33040                 first = false;
33041                 while(col >= 0){
33042                     if(fn.call(scope || this, row, col, cm) === true){
33043                         return [row, col];
33044                     }
33045                     col--;
33046                 }
33047                 row--;
33048             }
33049         } else {
33050             if(col >= clen){
33051                 row++;
33052                 first = false;
33053             }
33054             while(row < rlen){
33055                 if(!first){
33056                     col = 0;
33057                 }
33058                 first = false;
33059                 while(col < clen){
33060                     if(fn.call(scope || this, row, col, cm) === true){
33061                         return [row, col];
33062                     }
33063                     col++;
33064                 }
33065                 row++;
33066             }
33067         }
33068         return null;
33069     },
33070
33071     // private
33072     getSelections : function(){
33073         return this.selModel.getSelections();
33074     },
33075
33076     /**
33077      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33078      * but if manual update is required this method will initiate it.
33079      */
33080     autoSize : function(){
33081         if(this.rendered){
33082             this.view.layout();
33083             if(this.view.adjustForScroll){
33084                 this.view.adjustForScroll();
33085             }
33086         }
33087     },
33088
33089     /**
33090      * Returns the grid's underlying element.
33091      * @return {Element} The element
33092      */
33093     getGridEl : function(){
33094         return this.container;
33095     },
33096
33097     // private for compatibility, overridden by editor grid
33098     stopEditing : function(){},
33099
33100     /**
33101      * Returns the grid's SelectionModel.
33102      * @return {SelectionModel}
33103      */
33104     getSelectionModel : function(){
33105         if(!this.selModel){
33106             this.selModel = new Roo.grid.RowSelectionModel();
33107         }
33108         return this.selModel;
33109     },
33110
33111     /**
33112      * Returns the grid's DataSource.
33113      * @return {DataSource}
33114      */
33115     getDataSource : function(){
33116         return this.dataSource;
33117     },
33118
33119     /**
33120      * Returns the grid's ColumnModel.
33121      * @return {ColumnModel}
33122      */
33123     getColumnModel : function(){
33124         return this.colModel;
33125     },
33126
33127     /**
33128      * Returns the grid's GridView object.
33129      * @return {GridView}
33130      */
33131     getView : function(){
33132         if(!this.view){
33133             this.view = new Roo.grid.GridView(this.viewConfig);
33134         }
33135         return this.view;
33136     },
33137     /**
33138      * Called to get grid's drag proxy text, by default returns this.ddText.
33139      * @return {String}
33140      */
33141     getDragDropText : function(){
33142         var count = this.selModel.getCount();
33143         return String.format(this.ddText, count, count == 1 ? '' : 's');
33144     }
33145 });
33146 /**
33147  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33148  * %0 is replaced with the number of selected rows.
33149  * @type String
33150  */
33151 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33152  * Based on:
33153  * Ext JS Library 1.1.1
33154  * Copyright(c) 2006-2007, Ext JS, LLC.
33155  *
33156  * Originally Released Under LGPL - original licence link has changed is not relivant.
33157  *
33158  * Fork - LGPL
33159  * <script type="text/javascript">
33160  */
33161  
33162 Roo.grid.AbstractGridView = function(){
33163         this.grid = null;
33164         
33165         this.events = {
33166             "beforerowremoved" : true,
33167             "beforerowsinserted" : true,
33168             "beforerefresh" : true,
33169             "rowremoved" : true,
33170             "rowsinserted" : true,
33171             "rowupdated" : true,
33172             "refresh" : true
33173         };
33174     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33175 };
33176
33177 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33178     rowClass : "x-grid-row",
33179     cellClass : "x-grid-cell",
33180     tdClass : "x-grid-td",
33181     hdClass : "x-grid-hd",
33182     splitClass : "x-grid-hd-split",
33183     
33184     init: function(grid){
33185         this.grid = grid;
33186                 var cid = this.grid.getGridEl().id;
33187         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33188         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33189         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33190         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33191         },
33192         
33193     getColumnRenderers : function(){
33194         var renderers = [];
33195         var cm = this.grid.colModel;
33196         var colCount = cm.getColumnCount();
33197         for(var i = 0; i < colCount; i++){
33198             renderers[i] = cm.getRenderer(i);
33199         }
33200         return renderers;
33201     },
33202     
33203     getColumnIds : function(){
33204         var ids = [];
33205         var cm = this.grid.colModel;
33206         var colCount = cm.getColumnCount();
33207         for(var i = 0; i < colCount; i++){
33208             ids[i] = cm.getColumnId(i);
33209         }
33210         return ids;
33211     },
33212     
33213     getDataIndexes : function(){
33214         if(!this.indexMap){
33215             this.indexMap = this.buildIndexMap();
33216         }
33217         return this.indexMap.colToData;
33218     },
33219     
33220     getColumnIndexByDataIndex : function(dataIndex){
33221         if(!this.indexMap){
33222             this.indexMap = this.buildIndexMap();
33223         }
33224         return this.indexMap.dataToCol[dataIndex];
33225     },
33226     
33227     /**
33228      * Set a css style for a column dynamically. 
33229      * @param {Number} colIndex The index of the column
33230      * @param {String} name The css property name
33231      * @param {String} value The css value
33232      */
33233     setCSSStyle : function(colIndex, name, value){
33234         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33235         Roo.util.CSS.updateRule(selector, name, value);
33236     },
33237     
33238     generateRules : function(cm){
33239         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33240         Roo.util.CSS.removeStyleSheet(rulesId);
33241         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33242             var cid = cm.getColumnId(i);
33243             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33244                          this.tdSelector, cid, " {\n}\n",
33245                          this.hdSelector, cid, " {\n}\n",
33246                          this.splitSelector, cid, " {\n}\n");
33247         }
33248         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33249     }
33250 });/*
33251  * Based on:
33252  * Ext JS Library 1.1.1
33253  * Copyright(c) 2006-2007, Ext JS, LLC.
33254  *
33255  * Originally Released Under LGPL - original licence link has changed is not relivant.
33256  *
33257  * Fork - LGPL
33258  * <script type="text/javascript">
33259  */
33260
33261 // private
33262 // This is a support class used internally by the Grid components
33263 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33264     this.grid = grid;
33265     this.view = grid.getView();
33266     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33267     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33268     if(hd2){
33269         this.setHandleElId(Roo.id(hd));
33270         this.setOuterHandleElId(Roo.id(hd2));
33271     }
33272     this.scroll = false;
33273 };
33274 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33275     maxDragWidth: 120,
33276     getDragData : function(e){
33277         var t = Roo.lib.Event.getTarget(e);
33278         var h = this.view.findHeaderCell(t);
33279         if(h){
33280             return {ddel: h.firstChild, header:h};
33281         }
33282         return false;
33283     },
33284
33285     onInitDrag : function(e){
33286         this.view.headersDisabled = true;
33287         var clone = this.dragData.ddel.cloneNode(true);
33288         clone.id = Roo.id();
33289         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33290         this.proxy.update(clone);
33291         return true;
33292     },
33293
33294     afterValidDrop : function(){
33295         var v = this.view;
33296         setTimeout(function(){
33297             v.headersDisabled = false;
33298         }, 50);
33299     },
33300
33301     afterInvalidDrop : function(){
33302         var v = this.view;
33303         setTimeout(function(){
33304             v.headersDisabled = false;
33305         }, 50);
33306     }
33307 });
33308 /*
33309  * Based on:
33310  * Ext JS Library 1.1.1
33311  * Copyright(c) 2006-2007, Ext JS, LLC.
33312  *
33313  * Originally Released Under LGPL - original licence link has changed is not relivant.
33314  *
33315  * Fork - LGPL
33316  * <script type="text/javascript">
33317  */
33318 // private
33319 // This is a support class used internally by the Grid components
33320 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33321     this.grid = grid;
33322     this.view = grid.getView();
33323     // split the proxies so they don't interfere with mouse events
33324     this.proxyTop = Roo.DomHelper.append(document.body, {
33325         cls:"col-move-top", html:"&#160;"
33326     }, true);
33327     this.proxyBottom = Roo.DomHelper.append(document.body, {
33328         cls:"col-move-bottom", html:"&#160;"
33329     }, true);
33330     this.proxyTop.hide = this.proxyBottom.hide = function(){
33331         this.setLeftTop(-100,-100);
33332         this.setStyle("visibility", "hidden");
33333     };
33334     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33335     // temporarily disabled
33336     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33337     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33338 };
33339 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33340     proxyOffsets : [-4, -9],
33341     fly: Roo.Element.fly,
33342
33343     getTargetFromEvent : function(e){
33344         var t = Roo.lib.Event.getTarget(e);
33345         var cindex = this.view.findCellIndex(t);
33346         if(cindex !== false){
33347             return this.view.getHeaderCell(cindex);
33348         }
33349         return null;
33350     },
33351
33352     nextVisible : function(h){
33353         var v = this.view, cm = this.grid.colModel;
33354         h = h.nextSibling;
33355         while(h){
33356             if(!cm.isHidden(v.getCellIndex(h))){
33357                 return h;
33358             }
33359             h = h.nextSibling;
33360         }
33361         return null;
33362     },
33363
33364     prevVisible : function(h){
33365         var v = this.view, cm = this.grid.colModel;
33366         h = h.prevSibling;
33367         while(h){
33368             if(!cm.isHidden(v.getCellIndex(h))){
33369                 return h;
33370             }
33371             h = h.prevSibling;
33372         }
33373         return null;
33374     },
33375
33376     positionIndicator : function(h, n, e){
33377         var x = Roo.lib.Event.getPageX(e);
33378         var r = Roo.lib.Dom.getRegion(n.firstChild);
33379         var px, pt, py = r.top + this.proxyOffsets[1];
33380         if((r.right - x) <= (r.right-r.left)/2){
33381             px = r.right+this.view.borderWidth;
33382             pt = "after";
33383         }else{
33384             px = r.left;
33385             pt = "before";
33386         }
33387         var oldIndex = this.view.getCellIndex(h);
33388         var newIndex = this.view.getCellIndex(n);
33389
33390         if(this.grid.colModel.isFixed(newIndex)){
33391             return false;
33392         }
33393
33394         var locked = this.grid.colModel.isLocked(newIndex);
33395
33396         if(pt == "after"){
33397             newIndex++;
33398         }
33399         if(oldIndex < newIndex){
33400             newIndex--;
33401         }
33402         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33403             return false;
33404         }
33405         px +=  this.proxyOffsets[0];
33406         this.proxyTop.setLeftTop(px, py);
33407         this.proxyTop.show();
33408         if(!this.bottomOffset){
33409             this.bottomOffset = this.view.mainHd.getHeight();
33410         }
33411         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33412         this.proxyBottom.show();
33413         return pt;
33414     },
33415
33416     onNodeEnter : function(n, dd, e, data){
33417         if(data.header != n){
33418             this.positionIndicator(data.header, n, e);
33419         }
33420     },
33421
33422     onNodeOver : function(n, dd, e, data){
33423         var result = false;
33424         if(data.header != n){
33425             result = this.positionIndicator(data.header, n, e);
33426         }
33427         if(!result){
33428             this.proxyTop.hide();
33429             this.proxyBottom.hide();
33430         }
33431         return result ? this.dropAllowed : this.dropNotAllowed;
33432     },
33433
33434     onNodeOut : function(n, dd, e, data){
33435         this.proxyTop.hide();
33436         this.proxyBottom.hide();
33437     },
33438
33439     onNodeDrop : function(n, dd, e, data){
33440         var h = data.header;
33441         if(h != n){
33442             var cm = this.grid.colModel;
33443             var x = Roo.lib.Event.getPageX(e);
33444             var r = Roo.lib.Dom.getRegion(n.firstChild);
33445             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33446             var oldIndex = this.view.getCellIndex(h);
33447             var newIndex = this.view.getCellIndex(n);
33448             var locked = cm.isLocked(newIndex);
33449             if(pt == "after"){
33450                 newIndex++;
33451             }
33452             if(oldIndex < newIndex){
33453                 newIndex--;
33454             }
33455             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33456                 return false;
33457             }
33458             cm.setLocked(oldIndex, locked, true);
33459             cm.moveColumn(oldIndex, newIndex);
33460             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33461             return true;
33462         }
33463         return false;
33464     }
33465 });
33466 /*
33467  * Based on:
33468  * Ext JS Library 1.1.1
33469  * Copyright(c) 2006-2007, Ext JS, LLC.
33470  *
33471  * Originally Released Under LGPL - original licence link has changed is not relivant.
33472  *
33473  * Fork - LGPL
33474  * <script type="text/javascript">
33475  */
33476   
33477 /**
33478  * @class Roo.grid.GridView
33479  * @extends Roo.util.Observable
33480  *
33481  * @constructor
33482  * @param {Object} config
33483  */
33484 Roo.grid.GridView = function(config){
33485     Roo.grid.GridView.superclass.constructor.call(this);
33486     this.el = null;
33487
33488     Roo.apply(this, config);
33489 };
33490
33491 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33492
33493     unselectable :  'unselectable="on"',
33494     unselectableCls :  'x-unselectable',
33495     
33496     
33497     rowClass : "x-grid-row",
33498
33499     cellClass : "x-grid-col",
33500
33501     tdClass : "x-grid-td",
33502
33503     hdClass : "x-grid-hd",
33504
33505     splitClass : "x-grid-split",
33506
33507     sortClasses : ["sort-asc", "sort-desc"],
33508
33509     enableMoveAnim : false,
33510
33511     hlColor: "C3DAF9",
33512
33513     dh : Roo.DomHelper,
33514
33515     fly : Roo.Element.fly,
33516
33517     css : Roo.util.CSS,
33518
33519     borderWidth: 1,
33520
33521     splitOffset: 3,
33522
33523     scrollIncrement : 22,
33524
33525     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33526
33527     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33528
33529     bind : function(ds, cm){
33530         if(this.ds){
33531             this.ds.un("load", this.onLoad, this);
33532             this.ds.un("datachanged", this.onDataChange, this);
33533             this.ds.un("add", this.onAdd, this);
33534             this.ds.un("remove", this.onRemove, this);
33535             this.ds.un("update", this.onUpdate, this);
33536             this.ds.un("clear", this.onClear, this);
33537         }
33538         if(ds){
33539             ds.on("load", this.onLoad, this);
33540             ds.on("datachanged", this.onDataChange, this);
33541             ds.on("add", this.onAdd, this);
33542             ds.on("remove", this.onRemove, this);
33543             ds.on("update", this.onUpdate, this);
33544             ds.on("clear", this.onClear, this);
33545         }
33546         this.ds = ds;
33547
33548         if(this.cm){
33549             this.cm.un("widthchange", this.onColWidthChange, this);
33550             this.cm.un("headerchange", this.onHeaderChange, this);
33551             this.cm.un("hiddenchange", this.onHiddenChange, this);
33552             this.cm.un("columnmoved", this.onColumnMove, this);
33553             this.cm.un("columnlockchange", this.onColumnLock, this);
33554         }
33555         if(cm){
33556             this.generateRules(cm);
33557             cm.on("widthchange", this.onColWidthChange, this);
33558             cm.on("headerchange", this.onHeaderChange, this);
33559             cm.on("hiddenchange", this.onHiddenChange, this);
33560             cm.on("columnmoved", this.onColumnMove, this);
33561             cm.on("columnlockchange", this.onColumnLock, this);
33562         }
33563         this.cm = cm;
33564     },
33565
33566     init: function(grid){
33567         Roo.grid.GridView.superclass.init.call(this, grid);
33568
33569         this.bind(grid.dataSource, grid.colModel);
33570
33571         grid.on("headerclick", this.handleHeaderClick, this);
33572
33573         if(grid.trackMouseOver){
33574             grid.on("mouseover", this.onRowOver, this);
33575             grid.on("mouseout", this.onRowOut, this);
33576         }
33577         grid.cancelTextSelection = function(){};
33578         this.gridId = grid.id;
33579
33580         var tpls = this.templates || {};
33581
33582         if(!tpls.master){
33583             tpls.master = new Roo.Template(
33584                '<div class="x-grid" hidefocus="true">',
33585                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33586                   '<div class="x-grid-topbar"></div>',
33587                   '<div class="x-grid-scroller"><div></div></div>',
33588                   '<div class="x-grid-locked">',
33589                       '<div class="x-grid-header">{lockedHeader}</div>',
33590                       '<div class="x-grid-body">{lockedBody}</div>',
33591                   "</div>",
33592                   '<div class="x-grid-viewport">',
33593                       '<div class="x-grid-header">{header}</div>',
33594                       '<div class="x-grid-body">{body}</div>',
33595                   "</div>",
33596                   '<div class="x-grid-bottombar"></div>',
33597                  
33598                   '<div class="x-grid-resize-proxy">&#160;</div>',
33599                "</div>"
33600             );
33601             tpls.master.disableformats = true;
33602         }
33603
33604         if(!tpls.header){
33605             tpls.header = new Roo.Template(
33606                '<table border="0" cellspacing="0" cellpadding="0">',
33607                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33608                "</table>{splits}"
33609             );
33610             tpls.header.disableformats = true;
33611         }
33612         tpls.header.compile();
33613
33614         if(!tpls.hcell){
33615             tpls.hcell = new Roo.Template(
33616                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33617                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33618                 "</div></td>"
33619              );
33620              tpls.hcell.disableFormats = true;
33621         }
33622         tpls.hcell.compile();
33623
33624         if(!tpls.hsplit){
33625             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33626                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33627             tpls.hsplit.disableFormats = true;
33628         }
33629         tpls.hsplit.compile();
33630
33631         if(!tpls.body){
33632             tpls.body = new Roo.Template(
33633                '<table border="0" cellspacing="0" cellpadding="0">',
33634                "<tbody>{rows}</tbody>",
33635                "</table>"
33636             );
33637             tpls.body.disableFormats = true;
33638         }
33639         tpls.body.compile();
33640
33641         if(!tpls.row){
33642             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33643             tpls.row.disableFormats = true;
33644         }
33645         tpls.row.compile();
33646
33647         if(!tpls.cell){
33648             tpls.cell = new Roo.Template(
33649                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33650                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33651                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33652                 "</td>"
33653             );
33654             tpls.cell.disableFormats = true;
33655         }
33656         tpls.cell.compile();
33657
33658         this.templates = tpls;
33659     },
33660
33661     // remap these for backwards compat
33662     onColWidthChange : function(){
33663         this.updateColumns.apply(this, arguments);
33664     },
33665     onHeaderChange : function(){
33666         this.updateHeaders.apply(this, arguments);
33667     }, 
33668     onHiddenChange : function(){
33669         this.handleHiddenChange.apply(this, arguments);
33670     },
33671     onColumnMove : function(){
33672         this.handleColumnMove.apply(this, arguments);
33673     },
33674     onColumnLock : function(){
33675         this.handleLockChange.apply(this, arguments);
33676     },
33677
33678     onDataChange : function(){
33679         this.refresh();
33680         this.updateHeaderSortState();
33681     },
33682
33683     onClear : function(){
33684         this.refresh();
33685     },
33686
33687     onUpdate : function(ds, record){
33688         this.refreshRow(record);
33689     },
33690
33691     refreshRow : function(record){
33692         var ds = this.ds, index;
33693         if(typeof record == 'number'){
33694             index = record;
33695             record = ds.getAt(index);
33696         }else{
33697             index = ds.indexOf(record);
33698         }
33699         this.insertRows(ds, index, index, true);
33700         this.onRemove(ds, record, index+1, true);
33701         this.syncRowHeights(index, index);
33702         this.layout();
33703         this.fireEvent("rowupdated", this, index, record);
33704     },
33705
33706     onAdd : function(ds, records, index){
33707         this.insertRows(ds, index, index + (records.length-1));
33708     },
33709
33710     onRemove : function(ds, record, index, isUpdate){
33711         if(isUpdate !== true){
33712             this.fireEvent("beforerowremoved", this, index, record);
33713         }
33714         var bt = this.getBodyTable(), lt = this.getLockedTable();
33715         if(bt.rows[index]){
33716             bt.firstChild.removeChild(bt.rows[index]);
33717         }
33718         if(lt.rows[index]){
33719             lt.firstChild.removeChild(lt.rows[index]);
33720         }
33721         if(isUpdate !== true){
33722             this.stripeRows(index);
33723             this.syncRowHeights(index, index);
33724             this.layout();
33725             this.fireEvent("rowremoved", this, index, record);
33726         }
33727     },
33728
33729     onLoad : function(){
33730         this.scrollToTop();
33731     },
33732
33733     /**
33734      * Scrolls the grid to the top
33735      */
33736     scrollToTop : function(){
33737         if(this.scroller){
33738             this.scroller.dom.scrollTop = 0;
33739             this.syncScroll();
33740         }
33741     },
33742
33743     /**
33744      * Gets a panel in the header of the grid that can be used for toolbars etc.
33745      * After modifying the contents of this panel a call to grid.autoSize() may be
33746      * required to register any changes in size.
33747      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33748      * @return Roo.Element
33749      */
33750     getHeaderPanel : function(doShow){
33751         if(doShow){
33752             this.headerPanel.show();
33753         }
33754         return this.headerPanel;
33755     },
33756
33757     /**
33758      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33759      * After modifying the contents of this panel a call to grid.autoSize() may be
33760      * required to register any changes in size.
33761      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33762      * @return Roo.Element
33763      */
33764     getFooterPanel : function(doShow){
33765         if(doShow){
33766             this.footerPanel.show();
33767         }
33768         return this.footerPanel;
33769     },
33770
33771     initElements : function(){
33772         var E = Roo.Element;
33773         var el = this.grid.getGridEl().dom.firstChild;
33774         var cs = el.childNodes;
33775
33776         this.el = new E(el);
33777         
33778          this.focusEl = new E(el.firstChild);
33779         this.focusEl.swallowEvent("click", true);
33780         
33781         this.headerPanel = new E(cs[1]);
33782         this.headerPanel.enableDisplayMode("block");
33783
33784         this.scroller = new E(cs[2]);
33785         this.scrollSizer = new E(this.scroller.dom.firstChild);
33786
33787         this.lockedWrap = new E(cs[3]);
33788         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33789         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33790
33791         this.mainWrap = new E(cs[4]);
33792         this.mainHd = new E(this.mainWrap.dom.firstChild);
33793         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33794
33795         this.footerPanel = new E(cs[5]);
33796         this.footerPanel.enableDisplayMode("block");
33797
33798         this.resizeProxy = new E(cs[6]);
33799
33800         this.headerSelector = String.format(
33801            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33802            this.lockedHd.id, this.mainHd.id
33803         );
33804
33805         this.splitterSelector = String.format(
33806            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33807            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33808         );
33809     },
33810     idToCssName : function(s)
33811     {
33812         return s.replace(/[^a-z0-9]+/ig, '-');
33813     },
33814
33815     getHeaderCell : function(index){
33816         return Roo.DomQuery.select(this.headerSelector)[index];
33817     },
33818
33819     getHeaderCellMeasure : function(index){
33820         return this.getHeaderCell(index).firstChild;
33821     },
33822
33823     getHeaderCellText : function(index){
33824         return this.getHeaderCell(index).firstChild.firstChild;
33825     },
33826
33827     getLockedTable : function(){
33828         return this.lockedBody.dom.firstChild;
33829     },
33830
33831     getBodyTable : function(){
33832         return this.mainBody.dom.firstChild;
33833     },
33834
33835     getLockedRow : function(index){
33836         return this.getLockedTable().rows[index];
33837     },
33838
33839     getRow : function(index){
33840         return this.getBodyTable().rows[index];
33841     },
33842
33843     getRowComposite : function(index){
33844         if(!this.rowEl){
33845             this.rowEl = new Roo.CompositeElementLite();
33846         }
33847         var els = [], lrow, mrow;
33848         if(lrow = this.getLockedRow(index)){
33849             els.push(lrow);
33850         }
33851         if(mrow = this.getRow(index)){
33852             els.push(mrow);
33853         }
33854         this.rowEl.elements = els;
33855         return this.rowEl;
33856     },
33857     /**
33858      * Gets the 'td' of the cell
33859      * 
33860      * @param {Integer} rowIndex row to select
33861      * @param {Integer} colIndex column to select
33862      * 
33863      * @return {Object} 
33864      */
33865     getCell : function(rowIndex, colIndex){
33866         var locked = this.cm.getLockedCount();
33867         var source;
33868         if(colIndex < locked){
33869             source = this.lockedBody.dom.firstChild;
33870         }else{
33871             source = this.mainBody.dom.firstChild;
33872             colIndex -= locked;
33873         }
33874         return source.rows[rowIndex].childNodes[colIndex];
33875     },
33876
33877     getCellText : function(rowIndex, colIndex){
33878         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33879     },
33880
33881     getCellBox : function(cell){
33882         var b = this.fly(cell).getBox();
33883         if(Roo.isOpera){ // opera fails to report the Y
33884             b.y = cell.offsetTop + this.mainBody.getY();
33885         }
33886         return b;
33887     },
33888
33889     getCellIndex : function(cell){
33890         var id = String(cell.className).match(this.cellRE);
33891         if(id){
33892             return parseInt(id[1], 10);
33893         }
33894         return 0;
33895     },
33896
33897     findHeaderIndex : function(n){
33898         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33899         return r ? this.getCellIndex(r) : false;
33900     },
33901
33902     findHeaderCell : function(n){
33903         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33904         return r ? r : false;
33905     },
33906
33907     findRowIndex : function(n){
33908         if(!n){
33909             return false;
33910         }
33911         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33912         return r ? r.rowIndex : false;
33913     },
33914
33915     findCellIndex : function(node){
33916         var stop = this.el.dom;
33917         while(node && node != stop){
33918             if(this.findRE.test(node.className)){
33919                 return this.getCellIndex(node);
33920             }
33921             node = node.parentNode;
33922         }
33923         return false;
33924     },
33925
33926     getColumnId : function(index){
33927         return this.cm.getColumnId(index);
33928     },
33929
33930     getSplitters : function()
33931     {
33932         if(this.splitterSelector){
33933            return Roo.DomQuery.select(this.splitterSelector);
33934         }else{
33935             return null;
33936       }
33937     },
33938
33939     getSplitter : function(index){
33940         return this.getSplitters()[index];
33941     },
33942
33943     onRowOver : function(e, t){
33944         var row;
33945         if((row = this.findRowIndex(t)) !== false){
33946             this.getRowComposite(row).addClass("x-grid-row-over");
33947         }
33948     },
33949
33950     onRowOut : function(e, t){
33951         var row;
33952         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33953             this.getRowComposite(row).removeClass("x-grid-row-over");
33954         }
33955     },
33956
33957     renderHeaders : function(){
33958         var cm = this.cm;
33959         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33960         var cb = [], lb = [], sb = [], lsb = [], p = {};
33961         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33962             p.cellId = "x-grid-hd-0-" + i;
33963             p.splitId = "x-grid-csplit-0-" + i;
33964             p.id = cm.getColumnId(i);
33965             p.value = cm.getColumnHeader(i) || "";
33966             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33967             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33968             if(!cm.isLocked(i)){
33969                 cb[cb.length] = ct.apply(p);
33970                 sb[sb.length] = st.apply(p);
33971             }else{
33972                 lb[lb.length] = ct.apply(p);
33973                 lsb[lsb.length] = st.apply(p);
33974             }
33975         }
33976         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33977                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33978     },
33979
33980     updateHeaders : function(){
33981         var html = this.renderHeaders();
33982         this.lockedHd.update(html[0]);
33983         this.mainHd.update(html[1]);
33984     },
33985
33986     /**
33987      * Focuses the specified row.
33988      * @param {Number} row The row index
33989      */
33990     focusRow : function(row)
33991     {
33992         //Roo.log('GridView.focusRow');
33993         var x = this.scroller.dom.scrollLeft;
33994         this.focusCell(row, 0, false);
33995         this.scroller.dom.scrollLeft = x;
33996     },
33997
33998     /**
33999      * Focuses the specified cell.
34000      * @param {Number} row The row index
34001      * @param {Number} col The column index
34002      * @param {Boolean} hscroll false to disable horizontal scrolling
34003      */
34004     focusCell : function(row, col, hscroll)
34005     {
34006         //Roo.log('GridView.focusCell');
34007         var el = this.ensureVisible(row, col, hscroll);
34008         this.focusEl.alignTo(el, "tl-tl");
34009         if(Roo.isGecko){
34010             this.focusEl.focus();
34011         }else{
34012             this.focusEl.focus.defer(1, this.focusEl);
34013         }
34014     },
34015
34016     /**
34017      * Scrolls the specified cell into view
34018      * @param {Number} row The row index
34019      * @param {Number} col The column index
34020      * @param {Boolean} hscroll false to disable horizontal scrolling
34021      */
34022     ensureVisible : function(row, col, hscroll)
34023     {
34024         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34025         //return null; //disable for testing.
34026         if(typeof row != "number"){
34027             row = row.rowIndex;
34028         }
34029         if(row < 0 && row >= this.ds.getCount()){
34030             return  null;
34031         }
34032         col = (col !== undefined ? col : 0);
34033         var cm = this.grid.colModel;
34034         while(cm.isHidden(col)){
34035             col++;
34036         }
34037
34038         var el = this.getCell(row, col);
34039         if(!el){
34040             return null;
34041         }
34042         var c = this.scroller.dom;
34043
34044         var ctop = parseInt(el.offsetTop, 10);
34045         var cleft = parseInt(el.offsetLeft, 10);
34046         var cbot = ctop + el.offsetHeight;
34047         var cright = cleft + el.offsetWidth;
34048         
34049         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34050         var stop = parseInt(c.scrollTop, 10);
34051         var sleft = parseInt(c.scrollLeft, 10);
34052         var sbot = stop + ch;
34053         var sright = sleft + c.clientWidth;
34054         /*
34055         Roo.log('GridView.ensureVisible:' +
34056                 ' ctop:' + ctop +
34057                 ' c.clientHeight:' + c.clientHeight +
34058                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34059                 ' stop:' + stop +
34060                 ' cbot:' + cbot +
34061                 ' sbot:' + sbot +
34062                 ' ch:' + ch  
34063                 );
34064         */
34065         if(ctop < stop){
34066              c.scrollTop = ctop;
34067             //Roo.log("set scrolltop to ctop DISABLE?");
34068         }else if(cbot > sbot){
34069             //Roo.log("set scrolltop to cbot-ch");
34070             c.scrollTop = cbot-ch;
34071         }
34072         
34073         if(hscroll !== false){
34074             if(cleft < sleft){
34075                 c.scrollLeft = cleft;
34076             }else if(cright > sright){
34077                 c.scrollLeft = cright-c.clientWidth;
34078             }
34079         }
34080          
34081         return el;
34082     },
34083
34084     updateColumns : function(){
34085         this.grid.stopEditing();
34086         var cm = this.grid.colModel, colIds = this.getColumnIds();
34087         //var totalWidth = cm.getTotalWidth();
34088         var pos = 0;
34089         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34090             //if(cm.isHidden(i)) continue;
34091             var w = cm.getColumnWidth(i);
34092             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34093             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34094         }
34095         this.updateSplitters();
34096     },
34097
34098     generateRules : function(cm){
34099         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34100         Roo.util.CSS.removeStyleSheet(rulesId);
34101         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34102             var cid = cm.getColumnId(i);
34103             var align = '';
34104             if(cm.config[i].align){
34105                 align = 'text-align:'+cm.config[i].align+';';
34106             }
34107             var hidden = '';
34108             if(cm.isHidden(i)){
34109                 hidden = 'display:none;';
34110             }
34111             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34112             ruleBuf.push(
34113                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34114                     this.hdSelector, cid, " {\n", align, width, "}\n",
34115                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34116                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34117         }
34118         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34119     },
34120
34121     updateSplitters : function(){
34122         var cm = this.cm, s = this.getSplitters();
34123         if(s){ // splitters not created yet
34124             var pos = 0, locked = true;
34125             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34126                 if(cm.isHidden(i)) {
34127                     continue;
34128                 }
34129                 var w = cm.getColumnWidth(i); // make sure it's a number
34130                 if(!cm.isLocked(i) && locked){
34131                     pos = 0;
34132                     locked = false;
34133                 }
34134                 pos += w;
34135                 s[i].style.left = (pos-this.splitOffset) + "px";
34136             }
34137         }
34138     },
34139
34140     handleHiddenChange : function(colModel, colIndex, hidden){
34141         if(hidden){
34142             this.hideColumn(colIndex);
34143         }else{
34144             this.unhideColumn(colIndex);
34145         }
34146     },
34147
34148     hideColumn : function(colIndex){
34149         var cid = this.getColumnId(colIndex);
34150         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34151         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34152         if(Roo.isSafari){
34153             this.updateHeaders();
34154         }
34155         this.updateSplitters();
34156         this.layout();
34157     },
34158
34159     unhideColumn : function(colIndex){
34160         var cid = this.getColumnId(colIndex);
34161         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34162         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34163
34164         if(Roo.isSafari){
34165             this.updateHeaders();
34166         }
34167         this.updateSplitters();
34168         this.layout();
34169     },
34170
34171     insertRows : function(dm, firstRow, lastRow, isUpdate){
34172         if(firstRow == 0 && lastRow == dm.getCount()-1){
34173             this.refresh();
34174         }else{
34175             if(!isUpdate){
34176                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34177             }
34178             var s = this.getScrollState();
34179             var markup = this.renderRows(firstRow, lastRow);
34180             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34181             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34182             this.restoreScroll(s);
34183             if(!isUpdate){
34184                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34185                 this.syncRowHeights(firstRow, lastRow);
34186                 this.stripeRows(firstRow);
34187                 this.layout();
34188             }
34189         }
34190     },
34191
34192     bufferRows : function(markup, target, index){
34193         var before = null, trows = target.rows, tbody = target.tBodies[0];
34194         if(index < trows.length){
34195             before = trows[index];
34196         }
34197         var b = document.createElement("div");
34198         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34199         var rows = b.firstChild.rows;
34200         for(var i = 0, len = rows.length; i < len; i++){
34201             if(before){
34202                 tbody.insertBefore(rows[0], before);
34203             }else{
34204                 tbody.appendChild(rows[0]);
34205             }
34206         }
34207         b.innerHTML = "";
34208         b = null;
34209     },
34210
34211     deleteRows : function(dm, firstRow, lastRow){
34212         if(dm.getRowCount()<1){
34213             this.fireEvent("beforerefresh", this);
34214             this.mainBody.update("");
34215             this.lockedBody.update("");
34216             this.fireEvent("refresh", this);
34217         }else{
34218             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34219             var bt = this.getBodyTable();
34220             var tbody = bt.firstChild;
34221             var rows = bt.rows;
34222             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34223                 tbody.removeChild(rows[firstRow]);
34224             }
34225             this.stripeRows(firstRow);
34226             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34227         }
34228     },
34229
34230     updateRows : function(dataSource, firstRow, lastRow){
34231         var s = this.getScrollState();
34232         this.refresh();
34233         this.restoreScroll(s);
34234     },
34235
34236     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34237         if(!noRefresh){
34238            this.refresh();
34239         }
34240         this.updateHeaderSortState();
34241     },
34242
34243     getScrollState : function(){
34244         
34245         var sb = this.scroller.dom;
34246         return {left: sb.scrollLeft, top: sb.scrollTop};
34247     },
34248
34249     stripeRows : function(startRow){
34250         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34251             return;
34252         }
34253         startRow = startRow || 0;
34254         var rows = this.getBodyTable().rows;
34255         var lrows = this.getLockedTable().rows;
34256         var cls = ' x-grid-row-alt ';
34257         for(var i = startRow, len = rows.length; i < len; i++){
34258             var row = rows[i], lrow = lrows[i];
34259             var isAlt = ((i+1) % 2 == 0);
34260             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34261             if(isAlt == hasAlt){
34262                 continue;
34263             }
34264             if(isAlt){
34265                 row.className += " x-grid-row-alt";
34266             }else{
34267                 row.className = row.className.replace("x-grid-row-alt", "");
34268             }
34269             if(lrow){
34270                 lrow.className = row.className;
34271             }
34272         }
34273     },
34274
34275     restoreScroll : function(state){
34276         //Roo.log('GridView.restoreScroll');
34277         var sb = this.scroller.dom;
34278         sb.scrollLeft = state.left;
34279         sb.scrollTop = state.top;
34280         this.syncScroll();
34281     },
34282
34283     syncScroll : function(){
34284         //Roo.log('GridView.syncScroll');
34285         var sb = this.scroller.dom;
34286         var sh = this.mainHd.dom;
34287         var bs = this.mainBody.dom;
34288         var lv = this.lockedBody.dom;
34289         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34290         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34291     },
34292
34293     handleScroll : function(e){
34294         this.syncScroll();
34295         var sb = this.scroller.dom;
34296         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34297         e.stopEvent();
34298     },
34299
34300     handleWheel : function(e){
34301         var d = e.getWheelDelta();
34302         this.scroller.dom.scrollTop -= d*22;
34303         // set this here to prevent jumpy scrolling on large tables
34304         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34305         e.stopEvent();
34306     },
34307
34308     renderRows : function(startRow, endRow){
34309         // pull in all the crap needed to render rows
34310         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34311         var colCount = cm.getColumnCount();
34312
34313         if(ds.getCount() < 1){
34314             return ["", ""];
34315         }
34316
34317         // build a map for all the columns
34318         var cs = [];
34319         for(var i = 0; i < colCount; i++){
34320             var name = cm.getDataIndex(i);
34321             cs[i] = {
34322                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34323                 renderer : cm.getRenderer(i),
34324                 id : cm.getColumnId(i),
34325                 locked : cm.isLocked(i),
34326                 has_editor : cm.isCellEditable(i)
34327             };
34328         }
34329
34330         startRow = startRow || 0;
34331         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34332
34333         // records to render
34334         var rs = ds.getRange(startRow, endRow);
34335
34336         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34337     },
34338
34339     // As much as I hate to duplicate code, this was branched because FireFox really hates
34340     // [].join("") on strings. The performance difference was substantial enough to
34341     // branch this function
34342     doRender : Roo.isGecko ?
34343             function(cs, rs, ds, startRow, colCount, stripe){
34344                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34345                 // buffers
34346                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34347                 
34348                 var hasListener = this.grid.hasListener('rowclass');
34349                 var rowcfg = {};
34350                 for(var j = 0, len = rs.length; j < len; j++){
34351                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34352                     for(var i = 0; i < colCount; i++){
34353                         c = cs[i];
34354                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34355                         p.id = c.id;
34356                         p.css = p.attr = "";
34357                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34358                         if(p.value == undefined || p.value === "") {
34359                             p.value = "&#160;";
34360                         }
34361                         if(c.has_editor){
34362                             p.css += ' x-grid-editable-cell';
34363                         }
34364                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34365                             p.css +=  ' x-grid-dirty-cell';
34366                         }
34367                         var markup = ct.apply(p);
34368                         if(!c.locked){
34369                             cb+= markup;
34370                         }else{
34371                             lcb+= markup;
34372                         }
34373                     }
34374                     var alt = [];
34375                     if(stripe && ((rowIndex+1) % 2 == 0)){
34376                         alt.push("x-grid-row-alt")
34377                     }
34378                     if(r.dirty){
34379                         alt.push(  " x-grid-dirty-row");
34380                     }
34381                     rp.cells = lcb;
34382                     if(this.getRowClass){
34383                         alt.push(this.getRowClass(r, rowIndex));
34384                     }
34385                     if (hasListener) {
34386                         rowcfg = {
34387                              
34388                             record: r,
34389                             rowIndex : rowIndex,
34390                             rowClass : ''
34391                         };
34392                         this.grid.fireEvent('rowclass', this, rowcfg);
34393                         alt.push(rowcfg.rowClass);
34394                     }
34395                     rp.alt = alt.join(" ");
34396                     lbuf+= rt.apply(rp);
34397                     rp.cells = cb;
34398                     buf+=  rt.apply(rp);
34399                 }
34400                 return [lbuf, buf];
34401             } :
34402             function(cs, rs, ds, startRow, colCount, stripe){
34403                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34404                 // buffers
34405                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34406                 var hasListener = this.grid.hasListener('rowclass');
34407  
34408                 var rowcfg = {};
34409                 for(var j = 0, len = rs.length; j < len; j++){
34410                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34411                     for(var i = 0; i < colCount; i++){
34412                         c = cs[i];
34413                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34414                         p.id = c.id;
34415                         p.css = p.attr = "";
34416                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34417                         if(p.value == undefined || p.value === "") {
34418                             p.value = "&#160;";
34419                         }
34420                         //Roo.log(c);
34421                          if(c.has_editor){
34422                             p.css += ' x-grid-editable-cell';
34423                         }
34424                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34425                             p.css += ' x-grid-dirty-cell' 
34426                         }
34427                         
34428                         var markup = ct.apply(p);
34429                         if(!c.locked){
34430                             cb[cb.length] = markup;
34431                         }else{
34432                             lcb[lcb.length] = markup;
34433                         }
34434                     }
34435                     var alt = [];
34436                     if(stripe && ((rowIndex+1) % 2 == 0)){
34437                         alt.push( "x-grid-row-alt");
34438                     }
34439                     if(r.dirty){
34440                         alt.push(" x-grid-dirty-row");
34441                     }
34442                     rp.cells = lcb;
34443                     if(this.getRowClass){
34444                         alt.push( this.getRowClass(r, rowIndex));
34445                     }
34446                     if (hasListener) {
34447                         rowcfg = {
34448                              
34449                             record: r,
34450                             rowIndex : rowIndex,
34451                             rowClass : ''
34452                         };
34453                         this.grid.fireEvent('rowclass', this, rowcfg);
34454                         alt.push(rowcfg.rowClass);
34455                     }
34456                     
34457                     rp.alt = alt.join(" ");
34458                     rp.cells = lcb.join("");
34459                     lbuf[lbuf.length] = rt.apply(rp);
34460                     rp.cells = cb.join("");
34461                     buf[buf.length] =  rt.apply(rp);
34462                 }
34463                 return [lbuf.join(""), buf.join("")];
34464             },
34465
34466     renderBody : function(){
34467         var markup = this.renderRows();
34468         var bt = this.templates.body;
34469         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34470     },
34471
34472     /**
34473      * Refreshes the grid
34474      * @param {Boolean} headersToo
34475      */
34476     refresh : function(headersToo){
34477         this.fireEvent("beforerefresh", this);
34478         this.grid.stopEditing();
34479         var result = this.renderBody();
34480         this.lockedBody.update(result[0]);
34481         this.mainBody.update(result[1]);
34482         if(headersToo === true){
34483             this.updateHeaders();
34484             this.updateColumns();
34485             this.updateSplitters();
34486             this.updateHeaderSortState();
34487         }
34488         this.syncRowHeights();
34489         this.layout();
34490         this.fireEvent("refresh", this);
34491     },
34492
34493     handleColumnMove : function(cm, oldIndex, newIndex){
34494         this.indexMap = null;
34495         var s = this.getScrollState();
34496         this.refresh(true);
34497         this.restoreScroll(s);
34498         this.afterMove(newIndex);
34499     },
34500
34501     afterMove : function(colIndex){
34502         if(this.enableMoveAnim && Roo.enableFx){
34503             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34504         }
34505         // if multisort - fix sortOrder, and reload..
34506         if (this.grid.dataSource.multiSort) {
34507             // the we can call sort again..
34508             var dm = this.grid.dataSource;
34509             var cm = this.grid.colModel;
34510             var so = [];
34511             for(var i = 0; i < cm.config.length; i++ ) {
34512                 
34513                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34514                     continue; // dont' bother, it's not in sort list or being set.
34515                 }
34516                 
34517                 so.push(cm.config[i].dataIndex);
34518             };
34519             dm.sortOrder = so;
34520             dm.load(dm.lastOptions);
34521             
34522             
34523         }
34524         
34525     },
34526
34527     updateCell : function(dm, rowIndex, dataIndex){
34528         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34529         if(typeof colIndex == "undefined"){ // not present in grid
34530             return;
34531         }
34532         var cm = this.grid.colModel;
34533         var cell = this.getCell(rowIndex, colIndex);
34534         var cellText = this.getCellText(rowIndex, colIndex);
34535
34536         var p = {
34537             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34538             id : cm.getColumnId(colIndex),
34539             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34540         };
34541         var renderer = cm.getRenderer(colIndex);
34542         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34543         if(typeof val == "undefined" || val === "") {
34544             val = "&#160;";
34545         }
34546         cellText.innerHTML = val;
34547         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34548         this.syncRowHeights(rowIndex, rowIndex);
34549     },
34550
34551     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34552         var maxWidth = 0;
34553         if(this.grid.autoSizeHeaders){
34554             var h = this.getHeaderCellMeasure(colIndex);
34555             maxWidth = Math.max(maxWidth, h.scrollWidth);
34556         }
34557         var tb, index;
34558         if(this.cm.isLocked(colIndex)){
34559             tb = this.getLockedTable();
34560             index = colIndex;
34561         }else{
34562             tb = this.getBodyTable();
34563             index = colIndex - this.cm.getLockedCount();
34564         }
34565         if(tb && tb.rows){
34566             var rows = tb.rows;
34567             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34568             for(var i = 0; i < stopIndex; i++){
34569                 var cell = rows[i].childNodes[index].firstChild;
34570                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34571             }
34572         }
34573         return maxWidth + /*margin for error in IE*/ 5;
34574     },
34575     /**
34576      * Autofit a column to its content.
34577      * @param {Number} colIndex
34578      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34579      */
34580      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34581          if(this.cm.isHidden(colIndex)){
34582              return; // can't calc a hidden column
34583          }
34584         if(forceMinSize){
34585             var cid = this.cm.getColumnId(colIndex);
34586             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34587            if(this.grid.autoSizeHeaders){
34588                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34589            }
34590         }
34591         var newWidth = this.calcColumnWidth(colIndex);
34592         this.cm.setColumnWidth(colIndex,
34593             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34594         if(!suppressEvent){
34595             this.grid.fireEvent("columnresize", colIndex, newWidth);
34596         }
34597     },
34598
34599     /**
34600      * Autofits all columns to their content and then expands to fit any extra space in the grid
34601      */
34602      autoSizeColumns : function(){
34603         var cm = this.grid.colModel;
34604         var colCount = cm.getColumnCount();
34605         for(var i = 0; i < colCount; i++){
34606             this.autoSizeColumn(i, true, true);
34607         }
34608         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34609             this.fitColumns();
34610         }else{
34611             this.updateColumns();
34612             this.layout();
34613         }
34614     },
34615
34616     /**
34617      * Autofits all columns to the grid's width proportionate with their current size
34618      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34619      */
34620     fitColumns : function(reserveScrollSpace){
34621         var cm = this.grid.colModel;
34622         var colCount = cm.getColumnCount();
34623         var cols = [];
34624         var width = 0;
34625         var i, w;
34626         for (i = 0; i < colCount; i++){
34627             if(!cm.isHidden(i) && !cm.isFixed(i)){
34628                 w = cm.getColumnWidth(i);
34629                 cols.push(i);
34630                 cols.push(w);
34631                 width += w;
34632             }
34633         }
34634         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34635         if(reserveScrollSpace){
34636             avail -= 17;
34637         }
34638         var frac = (avail - cm.getTotalWidth())/width;
34639         while (cols.length){
34640             w = cols.pop();
34641             i = cols.pop();
34642             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34643         }
34644         this.updateColumns();
34645         this.layout();
34646     },
34647
34648     onRowSelect : function(rowIndex){
34649         var row = this.getRowComposite(rowIndex);
34650         row.addClass("x-grid-row-selected");
34651     },
34652
34653     onRowDeselect : function(rowIndex){
34654         var row = this.getRowComposite(rowIndex);
34655         row.removeClass("x-grid-row-selected");
34656     },
34657
34658     onCellSelect : function(row, col){
34659         var cell = this.getCell(row, col);
34660         if(cell){
34661             Roo.fly(cell).addClass("x-grid-cell-selected");
34662         }
34663     },
34664
34665     onCellDeselect : function(row, col){
34666         var cell = this.getCell(row, col);
34667         if(cell){
34668             Roo.fly(cell).removeClass("x-grid-cell-selected");
34669         }
34670     },
34671
34672     updateHeaderSortState : function(){
34673         
34674         // sort state can be single { field: xxx, direction : yyy}
34675         // or   { xxx=>ASC , yyy : DESC ..... }
34676         
34677         var mstate = {};
34678         if (!this.ds.multiSort) { 
34679             var state = this.ds.getSortState();
34680             if(!state){
34681                 return;
34682             }
34683             mstate[state.field] = state.direction;
34684             // FIXME... - this is not used here.. but might be elsewhere..
34685             this.sortState = state;
34686             
34687         } else {
34688             mstate = this.ds.sortToggle;
34689         }
34690         //remove existing sort classes..
34691         
34692         var sc = this.sortClasses;
34693         var hds = this.el.select(this.headerSelector).removeClass(sc);
34694         
34695         for(var f in mstate) {
34696         
34697             var sortColumn = this.cm.findColumnIndex(f);
34698             
34699             if(sortColumn != -1){
34700                 var sortDir = mstate[f];        
34701                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34702             }
34703         }
34704         
34705          
34706         
34707     },
34708
34709
34710     handleHeaderClick : function(g, index,e){
34711         
34712         Roo.log("header click");
34713         
34714         if (Roo.isTouch) {
34715             // touch events on header are handled by context
34716             this.handleHdCtx(g,index,e);
34717             return;
34718         }
34719         
34720         
34721         if(this.headersDisabled){
34722             return;
34723         }
34724         var dm = g.dataSource, cm = g.colModel;
34725         if(!cm.isSortable(index)){
34726             return;
34727         }
34728         g.stopEditing();
34729         
34730         if (dm.multiSort) {
34731             // update the sortOrder
34732             var so = [];
34733             for(var i = 0; i < cm.config.length; i++ ) {
34734                 
34735                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34736                     continue; // dont' bother, it's not in sort list or being set.
34737                 }
34738                 
34739                 so.push(cm.config[i].dataIndex);
34740             };
34741             dm.sortOrder = so;
34742         }
34743         
34744         
34745         dm.sort(cm.getDataIndex(index));
34746     },
34747
34748
34749     destroy : function(){
34750         if(this.colMenu){
34751             this.colMenu.removeAll();
34752             Roo.menu.MenuMgr.unregister(this.colMenu);
34753             this.colMenu.getEl().remove();
34754             delete this.colMenu;
34755         }
34756         if(this.hmenu){
34757             this.hmenu.removeAll();
34758             Roo.menu.MenuMgr.unregister(this.hmenu);
34759             this.hmenu.getEl().remove();
34760             delete this.hmenu;
34761         }
34762         if(this.grid.enableColumnMove){
34763             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34764             if(dds){
34765                 for(var dd in dds){
34766                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34767                         var elid = dds[dd].dragElId;
34768                         dds[dd].unreg();
34769                         Roo.get(elid).remove();
34770                     } else if(dds[dd].config.isTarget){
34771                         dds[dd].proxyTop.remove();
34772                         dds[dd].proxyBottom.remove();
34773                         dds[dd].unreg();
34774                     }
34775                     if(Roo.dd.DDM.locationCache[dd]){
34776                         delete Roo.dd.DDM.locationCache[dd];
34777                     }
34778                 }
34779                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34780             }
34781         }
34782         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34783         this.bind(null, null);
34784         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34785     },
34786
34787     handleLockChange : function(){
34788         this.refresh(true);
34789     },
34790
34791     onDenyColumnLock : function(){
34792
34793     },
34794
34795     onDenyColumnHide : function(){
34796
34797     },
34798
34799     handleHdMenuClick : function(item){
34800         var index = this.hdCtxIndex;
34801         var cm = this.cm, ds = this.ds;
34802         switch(item.id){
34803             case "asc":
34804                 ds.sort(cm.getDataIndex(index), "ASC");
34805                 break;
34806             case "desc":
34807                 ds.sort(cm.getDataIndex(index), "DESC");
34808                 break;
34809             case "lock":
34810                 var lc = cm.getLockedCount();
34811                 if(cm.getColumnCount(true) <= lc+1){
34812                     this.onDenyColumnLock();
34813                     return;
34814                 }
34815                 if(lc != index){
34816                     cm.setLocked(index, true, true);
34817                     cm.moveColumn(index, lc);
34818                     this.grid.fireEvent("columnmove", index, lc);
34819                 }else{
34820                     cm.setLocked(index, true);
34821                 }
34822             break;
34823             case "unlock":
34824                 var lc = cm.getLockedCount();
34825                 if((lc-1) != index){
34826                     cm.setLocked(index, false, true);
34827                     cm.moveColumn(index, lc-1);
34828                     this.grid.fireEvent("columnmove", index, lc-1);
34829                 }else{
34830                     cm.setLocked(index, false);
34831                 }
34832             break;
34833             case 'wider': // used to expand cols on touch..
34834             case 'narrow':
34835                 var cw = cm.getColumnWidth(index);
34836                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34837                 cw = Math.max(0, cw);
34838                 cw = Math.min(cw,4000);
34839                 cm.setColumnWidth(index, cw);
34840                 break;
34841                 
34842             default:
34843                 index = cm.getIndexById(item.id.substr(4));
34844                 if(index != -1){
34845                     if(item.checked && cm.getColumnCount(true) <= 1){
34846                         this.onDenyColumnHide();
34847                         return false;
34848                     }
34849                     cm.setHidden(index, item.checked);
34850                 }
34851         }
34852         return true;
34853     },
34854
34855     beforeColMenuShow : function(){
34856         var cm = this.cm,  colCount = cm.getColumnCount();
34857         this.colMenu.removeAll();
34858         for(var i = 0; i < colCount; i++){
34859             this.colMenu.add(new Roo.menu.CheckItem({
34860                 id: "col-"+cm.getColumnId(i),
34861                 text: cm.getColumnHeader(i),
34862                 checked: !cm.isHidden(i),
34863                 hideOnClick:false
34864             }));
34865         }
34866     },
34867
34868     handleHdCtx : function(g, index, e){
34869         e.stopEvent();
34870         var hd = this.getHeaderCell(index);
34871         this.hdCtxIndex = index;
34872         var ms = this.hmenu.items, cm = this.cm;
34873         ms.get("asc").setDisabled(!cm.isSortable(index));
34874         ms.get("desc").setDisabled(!cm.isSortable(index));
34875         if(this.grid.enableColLock !== false){
34876             ms.get("lock").setDisabled(cm.isLocked(index));
34877             ms.get("unlock").setDisabled(!cm.isLocked(index));
34878         }
34879         this.hmenu.show(hd, "tl-bl");
34880     },
34881
34882     handleHdOver : function(e){
34883         var hd = this.findHeaderCell(e.getTarget());
34884         if(hd && !this.headersDisabled){
34885             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34886                this.fly(hd).addClass("x-grid-hd-over");
34887             }
34888         }
34889     },
34890
34891     handleHdOut : function(e){
34892         var hd = this.findHeaderCell(e.getTarget());
34893         if(hd){
34894             this.fly(hd).removeClass("x-grid-hd-over");
34895         }
34896     },
34897
34898     handleSplitDblClick : function(e, t){
34899         var i = this.getCellIndex(t);
34900         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34901             this.autoSizeColumn(i, true);
34902             this.layout();
34903         }
34904     },
34905
34906     render : function(){
34907
34908         var cm = this.cm;
34909         var colCount = cm.getColumnCount();
34910
34911         if(this.grid.monitorWindowResize === true){
34912             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34913         }
34914         var header = this.renderHeaders();
34915         var body = this.templates.body.apply({rows:""});
34916         var html = this.templates.master.apply({
34917             lockedBody: body,
34918             body: body,
34919             lockedHeader: header[0],
34920             header: header[1]
34921         });
34922
34923         //this.updateColumns();
34924
34925         this.grid.getGridEl().dom.innerHTML = html;
34926
34927         this.initElements();
34928         
34929         // a kludge to fix the random scolling effect in webkit
34930         this.el.on("scroll", function() {
34931             this.el.dom.scrollTop=0; // hopefully not recursive..
34932         },this);
34933
34934         this.scroller.on("scroll", this.handleScroll, this);
34935         this.lockedBody.on("mousewheel", this.handleWheel, this);
34936         this.mainBody.on("mousewheel", this.handleWheel, this);
34937
34938         this.mainHd.on("mouseover", this.handleHdOver, this);
34939         this.mainHd.on("mouseout", this.handleHdOut, this);
34940         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34941                 {delegate: "."+this.splitClass});
34942
34943         this.lockedHd.on("mouseover", this.handleHdOver, this);
34944         this.lockedHd.on("mouseout", this.handleHdOut, this);
34945         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34946                 {delegate: "."+this.splitClass});
34947
34948         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34949             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34950         }
34951
34952         this.updateSplitters();
34953
34954         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34955             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34956             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34957         }
34958
34959         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34960             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34961             this.hmenu.add(
34962                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34963                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34964             );
34965             if(this.grid.enableColLock !== false){
34966                 this.hmenu.add('-',
34967                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34968                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34969                 );
34970             }
34971             if (Roo.isTouch) {
34972                  this.hmenu.add('-',
34973                     {id:"wider", text: this.columnsWiderText},
34974                     {id:"narrow", text: this.columnsNarrowText }
34975                 );
34976                 
34977                  
34978             }
34979             
34980             if(this.grid.enableColumnHide !== false){
34981
34982                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34983                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34984                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34985
34986                 this.hmenu.add('-',
34987                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34988                 );
34989             }
34990             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34991
34992             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34993         }
34994
34995         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34996             this.dd = new Roo.grid.GridDragZone(this.grid, {
34997                 ddGroup : this.grid.ddGroup || 'GridDD'
34998             });
34999             
35000         }
35001
35002         /*
35003         for(var i = 0; i < colCount; i++){
35004             if(cm.isHidden(i)){
35005                 this.hideColumn(i);
35006             }
35007             if(cm.config[i].align){
35008                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35009                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35010             }
35011         }*/
35012         
35013         this.updateHeaderSortState();
35014
35015         this.beforeInitialResize();
35016         this.layout(true);
35017
35018         // two part rendering gives faster view to the user
35019         this.renderPhase2.defer(1, this);
35020     },
35021
35022     renderPhase2 : function(){
35023         // render the rows now
35024         this.refresh();
35025         if(this.grid.autoSizeColumns){
35026             this.autoSizeColumns();
35027         }
35028     },
35029
35030     beforeInitialResize : function(){
35031
35032     },
35033
35034     onColumnSplitterMoved : function(i, w){
35035         this.userResized = true;
35036         var cm = this.grid.colModel;
35037         cm.setColumnWidth(i, w, true);
35038         var cid = cm.getColumnId(i);
35039         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35040         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35041         this.updateSplitters();
35042         this.layout();
35043         this.grid.fireEvent("columnresize", i, w);
35044     },
35045
35046     syncRowHeights : function(startIndex, endIndex){
35047         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35048             startIndex = startIndex || 0;
35049             var mrows = this.getBodyTable().rows;
35050             var lrows = this.getLockedTable().rows;
35051             var len = mrows.length-1;
35052             endIndex = Math.min(endIndex || len, len);
35053             for(var i = startIndex; i <= endIndex; i++){
35054                 var m = mrows[i], l = lrows[i];
35055                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35056                 m.style.height = l.style.height = h + "px";
35057             }
35058         }
35059     },
35060
35061     layout : function(initialRender, is2ndPass){
35062         var g = this.grid;
35063         var auto = g.autoHeight;
35064         var scrollOffset = 16;
35065         var c = g.getGridEl(), cm = this.cm,
35066                 expandCol = g.autoExpandColumn,
35067                 gv = this;
35068         //c.beginMeasure();
35069
35070         if(!c.dom.offsetWidth){ // display:none?
35071             if(initialRender){
35072                 this.lockedWrap.show();
35073                 this.mainWrap.show();
35074             }
35075             return;
35076         }
35077
35078         var hasLock = this.cm.isLocked(0);
35079
35080         var tbh = this.headerPanel.getHeight();
35081         var bbh = this.footerPanel.getHeight();
35082
35083         if(auto){
35084             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35085             var newHeight = ch + c.getBorderWidth("tb");
35086             if(g.maxHeight){
35087                 newHeight = Math.min(g.maxHeight, newHeight);
35088             }
35089             c.setHeight(newHeight);
35090         }
35091
35092         if(g.autoWidth){
35093             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35094         }
35095
35096         var s = this.scroller;
35097
35098         var csize = c.getSize(true);
35099
35100         this.el.setSize(csize.width, csize.height);
35101
35102         this.headerPanel.setWidth(csize.width);
35103         this.footerPanel.setWidth(csize.width);
35104
35105         var hdHeight = this.mainHd.getHeight();
35106         var vw = csize.width;
35107         var vh = csize.height - (tbh + bbh);
35108
35109         s.setSize(vw, vh);
35110
35111         var bt = this.getBodyTable();
35112         
35113         if(cm.getLockedCount() == cm.config.length){
35114             bt = this.getLockedTable();
35115         }
35116         
35117         var ltWidth = hasLock ?
35118                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35119
35120         var scrollHeight = bt.offsetHeight;
35121         var scrollWidth = ltWidth + bt.offsetWidth;
35122         var vscroll = false, hscroll = false;
35123
35124         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35125
35126         var lw = this.lockedWrap, mw = this.mainWrap;
35127         var lb = this.lockedBody, mb = this.mainBody;
35128
35129         setTimeout(function(){
35130             var t = s.dom.offsetTop;
35131             var w = s.dom.clientWidth,
35132                 h = s.dom.clientHeight;
35133
35134             lw.setTop(t);
35135             lw.setSize(ltWidth, h);
35136
35137             mw.setLeftTop(ltWidth, t);
35138             mw.setSize(w-ltWidth, h);
35139
35140             lb.setHeight(h-hdHeight);
35141             mb.setHeight(h-hdHeight);
35142
35143             if(is2ndPass !== true && !gv.userResized && expandCol){
35144                 // high speed resize without full column calculation
35145                 
35146                 var ci = cm.getIndexById(expandCol);
35147                 if (ci < 0) {
35148                     ci = cm.findColumnIndex(expandCol);
35149                 }
35150                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35151                 var expandId = cm.getColumnId(ci);
35152                 var  tw = cm.getTotalWidth(false);
35153                 var currentWidth = cm.getColumnWidth(ci);
35154                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35155                 if(currentWidth != cw){
35156                     cm.setColumnWidth(ci, cw, true);
35157                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35158                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35159                     gv.updateSplitters();
35160                     gv.layout(false, true);
35161                 }
35162             }
35163
35164             if(initialRender){
35165                 lw.show();
35166                 mw.show();
35167             }
35168             //c.endMeasure();
35169         }, 10);
35170     },
35171
35172     onWindowResize : function(){
35173         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35174             return;
35175         }
35176         this.layout();
35177     },
35178
35179     appendFooter : function(parentEl){
35180         return null;
35181     },
35182
35183     sortAscText : "Sort Ascending",
35184     sortDescText : "Sort Descending",
35185     lockText : "Lock Column",
35186     unlockText : "Unlock Column",
35187     columnsText : "Columns",
35188  
35189     columnsWiderText : "Wider",
35190     columnsNarrowText : "Thinner"
35191 });
35192
35193
35194 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35195     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35196     this.proxy.el.addClass('x-grid3-col-dd');
35197 };
35198
35199 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35200     handleMouseDown : function(e){
35201
35202     },
35203
35204     callHandleMouseDown : function(e){
35205         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35206     }
35207 });
35208 /*
35209  * Based on:
35210  * Ext JS Library 1.1.1
35211  * Copyright(c) 2006-2007, Ext JS, LLC.
35212  *
35213  * Originally Released Under LGPL - original licence link has changed is not relivant.
35214  *
35215  * Fork - LGPL
35216  * <script type="text/javascript">
35217  */
35218  
35219 // private
35220 // This is a support class used internally by the Grid components
35221 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35222     this.grid = grid;
35223     this.view = grid.getView();
35224     this.proxy = this.view.resizeProxy;
35225     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35226         "gridSplitters" + this.grid.getGridEl().id, {
35227         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35228     });
35229     this.setHandleElId(Roo.id(hd));
35230     this.setOuterHandleElId(Roo.id(hd2));
35231     this.scroll = false;
35232 };
35233 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35234     fly: Roo.Element.fly,
35235
35236     b4StartDrag : function(x, y){
35237         this.view.headersDisabled = true;
35238         this.proxy.setHeight(this.view.mainWrap.getHeight());
35239         var w = this.cm.getColumnWidth(this.cellIndex);
35240         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35241         this.resetConstraints();
35242         this.setXConstraint(minw, 1000);
35243         this.setYConstraint(0, 0);
35244         this.minX = x - minw;
35245         this.maxX = x + 1000;
35246         this.startPos = x;
35247         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35248     },
35249
35250
35251     handleMouseDown : function(e){
35252         ev = Roo.EventObject.setEvent(e);
35253         var t = this.fly(ev.getTarget());
35254         if(t.hasClass("x-grid-split")){
35255             this.cellIndex = this.view.getCellIndex(t.dom);
35256             this.split = t.dom;
35257             this.cm = this.grid.colModel;
35258             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35259                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35260             }
35261         }
35262     },
35263
35264     endDrag : function(e){
35265         this.view.headersDisabled = false;
35266         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35267         var diff = endX - this.startPos;
35268         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35269     },
35270
35271     autoOffset : function(){
35272         this.setDelta(0,0);
35273     }
35274 });/*
35275  * Based on:
35276  * Ext JS Library 1.1.1
35277  * Copyright(c) 2006-2007, Ext JS, LLC.
35278  *
35279  * Originally Released Under LGPL - original licence link has changed is not relivant.
35280  *
35281  * Fork - LGPL
35282  * <script type="text/javascript">
35283  */
35284  
35285 // private
35286 // This is a support class used internally by the Grid components
35287 Roo.grid.GridDragZone = function(grid, config){
35288     this.view = grid.getView();
35289     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35290     if(this.view.lockedBody){
35291         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35292         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35293     }
35294     this.scroll = false;
35295     this.grid = grid;
35296     this.ddel = document.createElement('div');
35297     this.ddel.className = 'x-grid-dd-wrap';
35298 };
35299
35300 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35301     ddGroup : "GridDD",
35302
35303     getDragData : function(e){
35304         var t = Roo.lib.Event.getTarget(e);
35305         var rowIndex = this.view.findRowIndex(t);
35306         var sm = this.grid.selModel;
35307             
35308         //Roo.log(rowIndex);
35309         
35310         if (sm.getSelectedCell) {
35311             // cell selection..
35312             if (!sm.getSelectedCell()) {
35313                 return false;
35314             }
35315             if (rowIndex != sm.getSelectedCell()[0]) {
35316                 return false;
35317             }
35318         
35319         }
35320         
35321         if(rowIndex !== false){
35322             
35323             // if editorgrid.. 
35324             
35325             
35326             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35327                
35328             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35329               //  
35330             //}
35331             if (e.hasModifier()){
35332                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35333             }
35334             
35335             Roo.log("getDragData");
35336             
35337             return {
35338                 grid: this.grid,
35339                 ddel: this.ddel,
35340                 rowIndex: rowIndex,
35341                 selections:sm.getSelections ? sm.getSelections() : (
35342                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35343                 )
35344             };
35345         }
35346         return false;
35347     },
35348
35349     onInitDrag : function(e){
35350         var data = this.dragData;
35351         this.ddel.innerHTML = this.grid.getDragDropText();
35352         this.proxy.update(this.ddel);
35353         // fire start drag?
35354     },
35355
35356     afterRepair : function(){
35357         this.dragging = false;
35358     },
35359
35360     getRepairXY : function(e, data){
35361         return false;
35362     },
35363
35364     onEndDrag : function(data, e){
35365         // fire end drag?
35366     },
35367
35368     onValidDrop : function(dd, e, id){
35369         // fire drag drop?
35370         this.hideProxy();
35371     },
35372
35373     beforeInvalidDrop : function(e, id){
35374
35375     }
35376 });/*
35377  * Based on:
35378  * Ext JS Library 1.1.1
35379  * Copyright(c) 2006-2007, Ext JS, LLC.
35380  *
35381  * Originally Released Under LGPL - original licence link has changed is not relivant.
35382  *
35383  * Fork - LGPL
35384  * <script type="text/javascript">
35385  */
35386  
35387
35388 /**
35389  * @class Roo.grid.ColumnModel
35390  * @extends Roo.util.Observable
35391  * This is the default implementation of a ColumnModel used by the Grid. It defines
35392  * the columns in the grid.
35393  * <br>Usage:<br>
35394  <pre><code>
35395  var colModel = new Roo.grid.ColumnModel([
35396         {header: "Ticker", width: 60, sortable: true, locked: true},
35397         {header: "Company Name", width: 150, sortable: true},
35398         {header: "Market Cap.", width: 100, sortable: true},
35399         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35400         {header: "Employees", width: 100, sortable: true, resizable: false}
35401  ]);
35402  </code></pre>
35403  * <p>
35404  
35405  * The config options listed for this class are options which may appear in each
35406  * individual column definition.
35407  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35408  * @constructor
35409  * @param {Object} config An Array of column config objects. See this class's
35410  * config objects for details.
35411 */
35412 Roo.grid.ColumnModel = function(config){
35413         /**
35414      * The config passed into the constructor
35415      */
35416     this.config = config;
35417     this.lookup = {};
35418
35419     // if no id, create one
35420     // if the column does not have a dataIndex mapping,
35421     // map it to the order it is in the config
35422     for(var i = 0, len = config.length; i < len; i++){
35423         var c = config[i];
35424         if(typeof c.dataIndex == "undefined"){
35425             c.dataIndex = i;
35426         }
35427         if(typeof c.renderer == "string"){
35428             c.renderer = Roo.util.Format[c.renderer];
35429         }
35430         if(typeof c.id == "undefined"){
35431             c.id = Roo.id();
35432         }
35433         if(c.editor && c.editor.xtype){
35434             c.editor  = Roo.factory(c.editor, Roo.grid);
35435         }
35436         if(c.editor && c.editor.isFormField){
35437             c.editor = new Roo.grid.GridEditor(c.editor);
35438         }
35439         this.lookup[c.id] = c;
35440     }
35441
35442     /**
35443      * The width of columns which have no width specified (defaults to 100)
35444      * @type Number
35445      */
35446     this.defaultWidth = 100;
35447
35448     /**
35449      * Default sortable of columns which have no sortable specified (defaults to false)
35450      * @type Boolean
35451      */
35452     this.defaultSortable = false;
35453
35454     this.addEvents({
35455         /**
35456              * @event widthchange
35457              * Fires when the width of a column changes.
35458              * @param {ColumnModel} this
35459              * @param {Number} columnIndex The column index
35460              * @param {Number} newWidth The new width
35461              */
35462             "widthchange": true,
35463         /**
35464              * @event headerchange
35465              * Fires when the text of a header changes.
35466              * @param {ColumnModel} this
35467              * @param {Number} columnIndex The column index
35468              * @param {Number} newText The new header text
35469              */
35470             "headerchange": true,
35471         /**
35472              * @event hiddenchange
35473              * Fires when a column is hidden or "unhidden".
35474              * @param {ColumnModel} this
35475              * @param {Number} columnIndex The column index
35476              * @param {Boolean} hidden true if hidden, false otherwise
35477              */
35478             "hiddenchange": true,
35479             /**
35480          * @event columnmoved
35481          * Fires when a column is moved.
35482          * @param {ColumnModel} this
35483          * @param {Number} oldIndex
35484          * @param {Number} newIndex
35485          */
35486         "columnmoved" : true,
35487         /**
35488          * @event columlockchange
35489          * Fires when a column's locked state is changed
35490          * @param {ColumnModel} this
35491          * @param {Number} colIndex
35492          * @param {Boolean} locked true if locked
35493          */
35494         "columnlockchange" : true
35495     });
35496     Roo.grid.ColumnModel.superclass.constructor.call(this);
35497 };
35498 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35499     /**
35500      * @cfg {String} header The header text to display in the Grid view.
35501      */
35502     /**
35503      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35504      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35505      * specified, the column's index is used as an index into the Record's data Array.
35506      */
35507     /**
35508      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35509      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35510      */
35511     /**
35512      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35513      * Defaults to the value of the {@link #defaultSortable} property.
35514      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35515      */
35516     /**
35517      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35518      */
35519     /**
35520      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35521      */
35522     /**
35523      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35524      */
35525     /**
35526      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35527      */
35528     /**
35529      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35530      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35531      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35532      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35533      */
35534        /**
35535      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35536      */
35537     /**
35538      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35539      */
35540     /**
35541      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35542      */
35543     /**
35544      * @cfg {String} cursor (Optional)
35545      */
35546     /**
35547      * @cfg {String} tooltip (Optional)
35548      */
35549     /**
35550      * @cfg {Number} xs (Optional)
35551      */
35552     /**
35553      * @cfg {Number} sm (Optional)
35554      */
35555     /**
35556      * @cfg {Number} md (Optional)
35557      */
35558     /**
35559      * @cfg {Number} lg (Optional)
35560      */
35561     /**
35562      * Returns the id of the column at the specified index.
35563      * @param {Number} index The column index
35564      * @return {String} the id
35565      */
35566     getColumnId : function(index){
35567         return this.config[index].id;
35568     },
35569
35570     /**
35571      * Returns the column for a specified id.
35572      * @param {String} id The column id
35573      * @return {Object} the column
35574      */
35575     getColumnById : function(id){
35576         return this.lookup[id];
35577     },
35578
35579     
35580     /**
35581      * Returns the column for a specified dataIndex.
35582      * @param {String} dataIndex The column dataIndex
35583      * @return {Object|Boolean} the column or false if not found
35584      */
35585     getColumnByDataIndex: function(dataIndex){
35586         var index = this.findColumnIndex(dataIndex);
35587         return index > -1 ? this.config[index] : false;
35588     },
35589     
35590     /**
35591      * Returns the index for a specified column id.
35592      * @param {String} id The column id
35593      * @return {Number} the index, or -1 if not found
35594      */
35595     getIndexById : function(id){
35596         for(var i = 0, len = this.config.length; i < len; i++){
35597             if(this.config[i].id == id){
35598                 return i;
35599             }
35600         }
35601         return -1;
35602     },
35603     
35604     /**
35605      * Returns the index for a specified column dataIndex.
35606      * @param {String} dataIndex The column dataIndex
35607      * @return {Number} the index, or -1 if not found
35608      */
35609     
35610     findColumnIndex : function(dataIndex){
35611         for(var i = 0, len = this.config.length; i < len; i++){
35612             if(this.config[i].dataIndex == dataIndex){
35613                 return i;
35614             }
35615         }
35616         return -1;
35617     },
35618     
35619     
35620     moveColumn : function(oldIndex, newIndex){
35621         var c = this.config[oldIndex];
35622         this.config.splice(oldIndex, 1);
35623         this.config.splice(newIndex, 0, c);
35624         this.dataMap = null;
35625         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35626     },
35627
35628     isLocked : function(colIndex){
35629         return this.config[colIndex].locked === true;
35630     },
35631
35632     setLocked : function(colIndex, value, suppressEvent){
35633         if(this.isLocked(colIndex) == value){
35634             return;
35635         }
35636         this.config[colIndex].locked = value;
35637         if(!suppressEvent){
35638             this.fireEvent("columnlockchange", this, colIndex, value);
35639         }
35640     },
35641
35642     getTotalLockedWidth : function(){
35643         var totalWidth = 0;
35644         for(var i = 0; i < this.config.length; i++){
35645             if(this.isLocked(i) && !this.isHidden(i)){
35646                 this.totalWidth += this.getColumnWidth(i);
35647             }
35648         }
35649         return totalWidth;
35650     },
35651
35652     getLockedCount : function(){
35653         for(var i = 0, len = this.config.length; i < len; i++){
35654             if(!this.isLocked(i)){
35655                 return i;
35656             }
35657         }
35658         
35659         return this.config.length;
35660     },
35661
35662     /**
35663      * Returns the number of columns.
35664      * @return {Number}
35665      */
35666     getColumnCount : function(visibleOnly){
35667         if(visibleOnly === true){
35668             var c = 0;
35669             for(var i = 0, len = this.config.length; i < len; i++){
35670                 if(!this.isHidden(i)){
35671                     c++;
35672                 }
35673             }
35674             return c;
35675         }
35676         return this.config.length;
35677     },
35678
35679     /**
35680      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35681      * @param {Function} fn
35682      * @param {Object} scope (optional)
35683      * @return {Array} result
35684      */
35685     getColumnsBy : function(fn, scope){
35686         var r = [];
35687         for(var i = 0, len = this.config.length; i < len; i++){
35688             var c = this.config[i];
35689             if(fn.call(scope||this, c, i) === true){
35690                 r[r.length] = c;
35691             }
35692         }
35693         return r;
35694     },
35695
35696     /**
35697      * Returns true if the specified column is sortable.
35698      * @param {Number} col The column index
35699      * @return {Boolean}
35700      */
35701     isSortable : function(col){
35702         if(typeof this.config[col].sortable == "undefined"){
35703             return this.defaultSortable;
35704         }
35705         return this.config[col].sortable;
35706     },
35707
35708     /**
35709      * Returns the rendering (formatting) function defined for the column.
35710      * @param {Number} col The column index.
35711      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35712      */
35713     getRenderer : function(col){
35714         if(!this.config[col].renderer){
35715             return Roo.grid.ColumnModel.defaultRenderer;
35716         }
35717         return this.config[col].renderer;
35718     },
35719
35720     /**
35721      * Sets the rendering (formatting) function for a column.
35722      * @param {Number} col The column index
35723      * @param {Function} fn The function to use to process the cell's raw data
35724      * to return HTML markup for the grid view. The render function is called with
35725      * the following parameters:<ul>
35726      * <li>Data value.</li>
35727      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35728      * <li>css A CSS style string to apply to the table cell.</li>
35729      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35730      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35731      * <li>Row index</li>
35732      * <li>Column index</li>
35733      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35734      */
35735     setRenderer : function(col, fn){
35736         this.config[col].renderer = fn;
35737     },
35738
35739     /**
35740      * Returns the width for the specified column.
35741      * @param {Number} col The column index
35742      * @return {Number}
35743      */
35744     getColumnWidth : function(col){
35745         return this.config[col].width * 1 || this.defaultWidth;
35746     },
35747
35748     /**
35749      * Sets the width for a column.
35750      * @param {Number} col The column index
35751      * @param {Number} width The new width
35752      */
35753     setColumnWidth : function(col, width, suppressEvent){
35754         this.config[col].width = width;
35755         this.totalWidth = null;
35756         if(!suppressEvent){
35757              this.fireEvent("widthchange", this, col, width);
35758         }
35759     },
35760
35761     /**
35762      * Returns the total width of all columns.
35763      * @param {Boolean} includeHidden True to include hidden column widths
35764      * @return {Number}
35765      */
35766     getTotalWidth : function(includeHidden){
35767         if(!this.totalWidth){
35768             this.totalWidth = 0;
35769             for(var i = 0, len = this.config.length; i < len; i++){
35770                 if(includeHidden || !this.isHidden(i)){
35771                     this.totalWidth += this.getColumnWidth(i);
35772                 }
35773             }
35774         }
35775         return this.totalWidth;
35776     },
35777
35778     /**
35779      * Returns the header for the specified column.
35780      * @param {Number} col The column index
35781      * @return {String}
35782      */
35783     getColumnHeader : function(col){
35784         return this.config[col].header;
35785     },
35786
35787     /**
35788      * Sets the header for a column.
35789      * @param {Number} col The column index
35790      * @param {String} header The new header
35791      */
35792     setColumnHeader : function(col, header){
35793         this.config[col].header = header;
35794         this.fireEvent("headerchange", this, col, header);
35795     },
35796
35797     /**
35798      * Returns the tooltip for the specified column.
35799      * @param {Number} col The column index
35800      * @return {String}
35801      */
35802     getColumnTooltip : function(col){
35803             return this.config[col].tooltip;
35804     },
35805     /**
35806      * Sets the tooltip for a column.
35807      * @param {Number} col The column index
35808      * @param {String} tooltip The new tooltip
35809      */
35810     setColumnTooltip : function(col, tooltip){
35811             this.config[col].tooltip = tooltip;
35812     },
35813
35814     /**
35815      * Returns the dataIndex for the specified column.
35816      * @param {Number} col The column index
35817      * @return {Number}
35818      */
35819     getDataIndex : function(col){
35820         return this.config[col].dataIndex;
35821     },
35822
35823     /**
35824      * Sets the dataIndex for a column.
35825      * @param {Number} col The column index
35826      * @param {Number} dataIndex The new dataIndex
35827      */
35828     setDataIndex : function(col, dataIndex){
35829         this.config[col].dataIndex = dataIndex;
35830     },
35831
35832     
35833     
35834     /**
35835      * Returns true if the cell is editable.
35836      * @param {Number} colIndex The column index
35837      * @param {Number} rowIndex The row index - this is nto actually used..?
35838      * @return {Boolean}
35839      */
35840     isCellEditable : function(colIndex, rowIndex){
35841         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35842     },
35843
35844     /**
35845      * Returns the editor defined for the cell/column.
35846      * return false or null to disable editing.
35847      * @param {Number} colIndex The column index
35848      * @param {Number} rowIndex The row index
35849      * @return {Object}
35850      */
35851     getCellEditor : function(colIndex, rowIndex){
35852         return this.config[colIndex].editor;
35853     },
35854
35855     /**
35856      * Sets if a column is editable.
35857      * @param {Number} col The column index
35858      * @param {Boolean} editable True if the column is editable
35859      */
35860     setEditable : function(col, editable){
35861         this.config[col].editable = editable;
35862     },
35863
35864
35865     /**
35866      * Returns true if the column is hidden.
35867      * @param {Number} colIndex The column index
35868      * @return {Boolean}
35869      */
35870     isHidden : function(colIndex){
35871         return this.config[colIndex].hidden;
35872     },
35873
35874
35875     /**
35876      * Returns true if the column width cannot be changed
35877      */
35878     isFixed : function(colIndex){
35879         return this.config[colIndex].fixed;
35880     },
35881
35882     /**
35883      * Returns true if the column can be resized
35884      * @return {Boolean}
35885      */
35886     isResizable : function(colIndex){
35887         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35888     },
35889     /**
35890      * Sets if a column is hidden.
35891      * @param {Number} colIndex The column index
35892      * @param {Boolean} hidden True if the column is hidden
35893      */
35894     setHidden : function(colIndex, hidden){
35895         this.config[colIndex].hidden = hidden;
35896         this.totalWidth = null;
35897         this.fireEvent("hiddenchange", this, colIndex, hidden);
35898     },
35899
35900     /**
35901      * Sets the editor for a column.
35902      * @param {Number} col The column index
35903      * @param {Object} editor The editor object
35904      */
35905     setEditor : function(col, editor){
35906         this.config[col].editor = editor;
35907     }
35908 });
35909
35910 Roo.grid.ColumnModel.defaultRenderer = function(value)
35911 {
35912     if(typeof value == "object") {
35913         return value;
35914     }
35915         if(typeof value == "string" && value.length < 1){
35916             return "&#160;";
35917         }
35918     
35919         return String.format("{0}", value);
35920 };
35921
35922 // Alias for backwards compatibility
35923 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35924 /*
35925  * Based on:
35926  * Ext JS Library 1.1.1
35927  * Copyright(c) 2006-2007, Ext JS, LLC.
35928  *
35929  * Originally Released Under LGPL - original licence link has changed is not relivant.
35930  *
35931  * Fork - LGPL
35932  * <script type="text/javascript">
35933  */
35934
35935 /**
35936  * @class Roo.grid.AbstractSelectionModel
35937  * @extends Roo.util.Observable
35938  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35939  * implemented by descendant classes.  This class should not be directly instantiated.
35940  * @constructor
35941  */
35942 Roo.grid.AbstractSelectionModel = function(){
35943     this.locked = false;
35944     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35945 };
35946
35947 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35948     /** @ignore Called by the grid automatically. Do not call directly. */
35949     init : function(grid){
35950         this.grid = grid;
35951         this.initEvents();
35952     },
35953
35954     /**
35955      * Locks the selections.
35956      */
35957     lock : function(){
35958         this.locked = true;
35959     },
35960
35961     /**
35962      * Unlocks the selections.
35963      */
35964     unlock : function(){
35965         this.locked = false;
35966     },
35967
35968     /**
35969      * Returns true if the selections are locked.
35970      * @return {Boolean}
35971      */
35972     isLocked : function(){
35973         return this.locked;
35974     }
35975 });/*
35976  * Based on:
35977  * Ext JS Library 1.1.1
35978  * Copyright(c) 2006-2007, Ext JS, LLC.
35979  *
35980  * Originally Released Under LGPL - original licence link has changed is not relivant.
35981  *
35982  * Fork - LGPL
35983  * <script type="text/javascript">
35984  */
35985 /**
35986  * @extends Roo.grid.AbstractSelectionModel
35987  * @class Roo.grid.RowSelectionModel
35988  * The default SelectionModel used by {@link Roo.grid.Grid}.
35989  * It supports multiple selections and keyboard selection/navigation. 
35990  * @constructor
35991  * @param {Object} config
35992  */
35993 Roo.grid.RowSelectionModel = function(config){
35994     Roo.apply(this, config);
35995     this.selections = new Roo.util.MixedCollection(false, function(o){
35996         return o.id;
35997     });
35998
35999     this.last = false;
36000     this.lastActive = false;
36001
36002     this.addEvents({
36003         /**
36004              * @event selectionchange
36005              * Fires when the selection changes
36006              * @param {SelectionModel} this
36007              */
36008             "selectionchange" : true,
36009         /**
36010              * @event afterselectionchange
36011              * Fires after the selection changes (eg. by key press or clicking)
36012              * @param {SelectionModel} this
36013              */
36014             "afterselectionchange" : true,
36015         /**
36016              * @event beforerowselect
36017              * Fires when a row is selected being selected, return false to cancel.
36018              * @param {SelectionModel} this
36019              * @param {Number} rowIndex The selected index
36020              * @param {Boolean} keepExisting False if other selections will be cleared
36021              */
36022             "beforerowselect" : true,
36023         /**
36024              * @event rowselect
36025              * Fires when a row is selected.
36026              * @param {SelectionModel} this
36027              * @param {Number} rowIndex The selected index
36028              * @param {Roo.data.Record} r The record
36029              */
36030             "rowselect" : true,
36031         /**
36032              * @event rowdeselect
36033              * Fires when a row is deselected.
36034              * @param {SelectionModel} this
36035              * @param {Number} rowIndex The selected index
36036              */
36037         "rowdeselect" : true
36038     });
36039     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36040     this.locked = false;
36041 };
36042
36043 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36044     /**
36045      * @cfg {Boolean} singleSelect
36046      * True to allow selection of only one row at a time (defaults to false)
36047      */
36048     singleSelect : false,
36049
36050     // private
36051     initEvents : function(){
36052
36053         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36054             this.grid.on("mousedown", this.handleMouseDown, this);
36055         }else{ // allow click to work like normal
36056             this.grid.on("rowclick", this.handleDragableRowClick, this);
36057         }
36058
36059         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36060             "up" : function(e){
36061                 if(!e.shiftKey){
36062                     this.selectPrevious(e.shiftKey);
36063                 }else if(this.last !== false && this.lastActive !== false){
36064                     var last = this.last;
36065                     this.selectRange(this.last,  this.lastActive-1);
36066                     this.grid.getView().focusRow(this.lastActive);
36067                     if(last !== false){
36068                         this.last = last;
36069                     }
36070                 }else{
36071                     this.selectFirstRow();
36072                 }
36073                 this.fireEvent("afterselectionchange", this);
36074             },
36075             "down" : function(e){
36076                 if(!e.shiftKey){
36077                     this.selectNext(e.shiftKey);
36078                 }else if(this.last !== false && this.lastActive !== false){
36079                     var last = this.last;
36080                     this.selectRange(this.last,  this.lastActive+1);
36081                     this.grid.getView().focusRow(this.lastActive);
36082                     if(last !== false){
36083                         this.last = last;
36084                     }
36085                 }else{
36086                     this.selectFirstRow();
36087                 }
36088                 this.fireEvent("afterselectionchange", this);
36089             },
36090             scope: this
36091         });
36092
36093         var view = this.grid.view;
36094         view.on("refresh", this.onRefresh, this);
36095         view.on("rowupdated", this.onRowUpdated, this);
36096         view.on("rowremoved", this.onRemove, this);
36097     },
36098
36099     // private
36100     onRefresh : function(){
36101         var ds = this.grid.dataSource, i, v = this.grid.view;
36102         var s = this.selections;
36103         s.each(function(r){
36104             if((i = ds.indexOfId(r.id)) != -1){
36105                 v.onRowSelect(i);
36106                 s.add(ds.getAt(i)); // updating the selection relate data
36107             }else{
36108                 s.remove(r);
36109             }
36110         });
36111     },
36112
36113     // private
36114     onRemove : function(v, index, r){
36115         this.selections.remove(r);
36116     },
36117
36118     // private
36119     onRowUpdated : function(v, index, r){
36120         if(this.isSelected(r)){
36121             v.onRowSelect(index);
36122         }
36123     },
36124
36125     /**
36126      * Select records.
36127      * @param {Array} records The records to select
36128      * @param {Boolean} keepExisting (optional) True to keep existing selections
36129      */
36130     selectRecords : function(records, keepExisting){
36131         if(!keepExisting){
36132             this.clearSelections();
36133         }
36134         var ds = this.grid.dataSource;
36135         for(var i = 0, len = records.length; i < len; i++){
36136             this.selectRow(ds.indexOf(records[i]), true);
36137         }
36138     },
36139
36140     /**
36141      * Gets the number of selected rows.
36142      * @return {Number}
36143      */
36144     getCount : function(){
36145         return this.selections.length;
36146     },
36147
36148     /**
36149      * Selects the first row in the grid.
36150      */
36151     selectFirstRow : function(){
36152         this.selectRow(0);
36153     },
36154
36155     /**
36156      * Select the last row.
36157      * @param {Boolean} keepExisting (optional) True to keep existing selections
36158      */
36159     selectLastRow : function(keepExisting){
36160         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36161     },
36162
36163     /**
36164      * Selects the row immediately following the last selected row.
36165      * @param {Boolean} keepExisting (optional) True to keep existing selections
36166      */
36167     selectNext : function(keepExisting){
36168         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36169             this.selectRow(this.last+1, keepExisting);
36170             this.grid.getView().focusRow(this.last);
36171         }
36172     },
36173
36174     /**
36175      * Selects the row that precedes the last selected row.
36176      * @param {Boolean} keepExisting (optional) True to keep existing selections
36177      */
36178     selectPrevious : function(keepExisting){
36179         if(this.last){
36180             this.selectRow(this.last-1, keepExisting);
36181             this.grid.getView().focusRow(this.last);
36182         }
36183     },
36184
36185     /**
36186      * Returns the selected records
36187      * @return {Array} Array of selected records
36188      */
36189     getSelections : function(){
36190         return [].concat(this.selections.items);
36191     },
36192
36193     /**
36194      * Returns the first selected record.
36195      * @return {Record}
36196      */
36197     getSelected : function(){
36198         return this.selections.itemAt(0);
36199     },
36200
36201
36202     /**
36203      * Clears all selections.
36204      */
36205     clearSelections : function(fast){
36206         if(this.locked) {
36207             return;
36208         }
36209         if(fast !== true){
36210             var ds = this.grid.dataSource;
36211             var s = this.selections;
36212             s.each(function(r){
36213                 this.deselectRow(ds.indexOfId(r.id));
36214             }, this);
36215             s.clear();
36216         }else{
36217             this.selections.clear();
36218         }
36219         this.last = false;
36220     },
36221
36222
36223     /**
36224      * Selects all rows.
36225      */
36226     selectAll : function(){
36227         if(this.locked) {
36228             return;
36229         }
36230         this.selections.clear();
36231         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36232             this.selectRow(i, true);
36233         }
36234     },
36235
36236     /**
36237      * Returns True if there is a selection.
36238      * @return {Boolean}
36239      */
36240     hasSelection : function(){
36241         return this.selections.length > 0;
36242     },
36243
36244     /**
36245      * Returns True if the specified row is selected.
36246      * @param {Number/Record} record The record or index of the record to check
36247      * @return {Boolean}
36248      */
36249     isSelected : function(index){
36250         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36251         return (r && this.selections.key(r.id) ? true : false);
36252     },
36253
36254     /**
36255      * Returns True if the specified record id is selected.
36256      * @param {String} id The id of record to check
36257      * @return {Boolean}
36258      */
36259     isIdSelected : function(id){
36260         return (this.selections.key(id) ? true : false);
36261     },
36262
36263     // private
36264     handleMouseDown : function(e, t){
36265         var view = this.grid.getView(), rowIndex;
36266         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36267             return;
36268         };
36269         if(e.shiftKey && this.last !== false){
36270             var last = this.last;
36271             this.selectRange(last, rowIndex, e.ctrlKey);
36272             this.last = last; // reset the last
36273             view.focusRow(rowIndex);
36274         }else{
36275             var isSelected = this.isSelected(rowIndex);
36276             if(e.button !== 0 && isSelected){
36277                 view.focusRow(rowIndex);
36278             }else if(e.ctrlKey && isSelected){
36279                 this.deselectRow(rowIndex);
36280             }else if(!isSelected){
36281                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36282                 view.focusRow(rowIndex);
36283             }
36284         }
36285         this.fireEvent("afterselectionchange", this);
36286     },
36287     // private
36288     handleDragableRowClick :  function(grid, rowIndex, e) 
36289     {
36290         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36291             this.selectRow(rowIndex, false);
36292             grid.view.focusRow(rowIndex);
36293              this.fireEvent("afterselectionchange", this);
36294         }
36295     },
36296     
36297     /**
36298      * Selects multiple rows.
36299      * @param {Array} rows Array of the indexes of the row to select
36300      * @param {Boolean} keepExisting (optional) True to keep existing selections
36301      */
36302     selectRows : function(rows, keepExisting){
36303         if(!keepExisting){
36304             this.clearSelections();
36305         }
36306         for(var i = 0, len = rows.length; i < len; i++){
36307             this.selectRow(rows[i], true);
36308         }
36309     },
36310
36311     /**
36312      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36313      * @param {Number} startRow The index of the first row in the range
36314      * @param {Number} endRow The index of the last row in the range
36315      * @param {Boolean} keepExisting (optional) True to retain existing selections
36316      */
36317     selectRange : function(startRow, endRow, keepExisting){
36318         if(this.locked) {
36319             return;
36320         }
36321         if(!keepExisting){
36322             this.clearSelections();
36323         }
36324         if(startRow <= endRow){
36325             for(var i = startRow; i <= endRow; i++){
36326                 this.selectRow(i, true);
36327             }
36328         }else{
36329             for(var i = startRow; i >= endRow; i--){
36330                 this.selectRow(i, true);
36331             }
36332         }
36333     },
36334
36335     /**
36336      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36337      * @param {Number} startRow The index of the first row in the range
36338      * @param {Number} endRow The index of the last row in the range
36339      */
36340     deselectRange : function(startRow, endRow, preventViewNotify){
36341         if(this.locked) {
36342             return;
36343         }
36344         for(var i = startRow; i <= endRow; i++){
36345             this.deselectRow(i, preventViewNotify);
36346         }
36347     },
36348
36349     /**
36350      * Selects a row.
36351      * @param {Number} row The index of the row to select
36352      * @param {Boolean} keepExisting (optional) True to keep existing selections
36353      */
36354     selectRow : function(index, keepExisting, preventViewNotify){
36355         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36356             return;
36357         }
36358         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36359             if(!keepExisting || this.singleSelect){
36360                 this.clearSelections();
36361             }
36362             var r = this.grid.dataSource.getAt(index);
36363             this.selections.add(r);
36364             this.last = this.lastActive = index;
36365             if(!preventViewNotify){
36366                 this.grid.getView().onRowSelect(index);
36367             }
36368             this.fireEvent("rowselect", this, index, r);
36369             this.fireEvent("selectionchange", this);
36370         }
36371     },
36372
36373     /**
36374      * Deselects a row.
36375      * @param {Number} row The index of the row to deselect
36376      */
36377     deselectRow : function(index, preventViewNotify){
36378         if(this.locked) {
36379             return;
36380         }
36381         if(this.last == index){
36382             this.last = false;
36383         }
36384         if(this.lastActive == index){
36385             this.lastActive = false;
36386         }
36387         var r = this.grid.dataSource.getAt(index);
36388         this.selections.remove(r);
36389         if(!preventViewNotify){
36390             this.grid.getView().onRowDeselect(index);
36391         }
36392         this.fireEvent("rowdeselect", this, index);
36393         this.fireEvent("selectionchange", this);
36394     },
36395
36396     // private
36397     restoreLast : function(){
36398         if(this._last){
36399             this.last = this._last;
36400         }
36401     },
36402
36403     // private
36404     acceptsNav : function(row, col, cm){
36405         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36406     },
36407
36408     // private
36409     onEditorKey : function(field, e){
36410         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36411         if(k == e.TAB){
36412             e.stopEvent();
36413             ed.completeEdit();
36414             if(e.shiftKey){
36415                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36416             }else{
36417                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36418             }
36419         }else if(k == e.ENTER && !e.ctrlKey){
36420             e.stopEvent();
36421             ed.completeEdit();
36422             if(e.shiftKey){
36423                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36424             }else{
36425                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36426             }
36427         }else if(k == e.ESC){
36428             ed.cancelEdit();
36429         }
36430         if(newCell){
36431             g.startEditing(newCell[0], newCell[1]);
36432         }
36433     }
36434 });/*
36435  * Based on:
36436  * Ext JS Library 1.1.1
36437  * Copyright(c) 2006-2007, Ext JS, LLC.
36438  *
36439  * Originally Released Under LGPL - original licence link has changed is not relivant.
36440  *
36441  * Fork - LGPL
36442  * <script type="text/javascript">
36443  */
36444 /**
36445  * @class Roo.grid.CellSelectionModel
36446  * @extends Roo.grid.AbstractSelectionModel
36447  * This class provides the basic implementation for cell selection in a grid.
36448  * @constructor
36449  * @param {Object} config The object containing the configuration of this model.
36450  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36451  */
36452 Roo.grid.CellSelectionModel = function(config){
36453     Roo.apply(this, config);
36454
36455     this.selection = null;
36456
36457     this.addEvents({
36458         /**
36459              * @event beforerowselect
36460              * Fires before a cell is selected.
36461              * @param {SelectionModel} this
36462              * @param {Number} rowIndex The selected row index
36463              * @param {Number} colIndex The selected cell index
36464              */
36465             "beforecellselect" : true,
36466         /**
36467              * @event cellselect
36468              * Fires when a cell is selected.
36469              * @param {SelectionModel} this
36470              * @param {Number} rowIndex The selected row index
36471              * @param {Number} colIndex The selected cell index
36472              */
36473             "cellselect" : true,
36474         /**
36475              * @event selectionchange
36476              * Fires when the active selection changes.
36477              * @param {SelectionModel} this
36478              * @param {Object} selection null for no selection or an object (o) with two properties
36479                 <ul>
36480                 <li>o.record: the record object for the row the selection is in</li>
36481                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36482                 </ul>
36483              */
36484             "selectionchange" : true,
36485         /**
36486              * @event tabend
36487              * Fires when the tab (or enter) was pressed on the last editable cell
36488              * You can use this to trigger add new row.
36489              * @param {SelectionModel} this
36490              */
36491             "tabend" : true,
36492          /**
36493              * @event beforeeditnext
36494              * Fires before the next editable sell is made active
36495              * You can use this to skip to another cell or fire the tabend
36496              *    if you set cell to false
36497              * @param {Object} eventdata object : { cell : [ row, col ] } 
36498              */
36499             "beforeeditnext" : true
36500     });
36501     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36502 };
36503
36504 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36505     
36506     enter_is_tab: false,
36507
36508     /** @ignore */
36509     initEvents : function(){
36510         this.grid.on("mousedown", this.handleMouseDown, this);
36511         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36512         var view = this.grid.view;
36513         view.on("refresh", this.onViewChange, this);
36514         view.on("rowupdated", this.onRowUpdated, this);
36515         view.on("beforerowremoved", this.clearSelections, this);
36516         view.on("beforerowsinserted", this.clearSelections, this);
36517         if(this.grid.isEditor){
36518             this.grid.on("beforeedit", this.beforeEdit,  this);
36519         }
36520     },
36521
36522         //private
36523     beforeEdit : function(e){
36524         this.select(e.row, e.column, false, true, e.record);
36525     },
36526
36527         //private
36528     onRowUpdated : function(v, index, r){
36529         if(this.selection && this.selection.record == r){
36530             v.onCellSelect(index, this.selection.cell[1]);
36531         }
36532     },
36533
36534         //private
36535     onViewChange : function(){
36536         this.clearSelections(true);
36537     },
36538
36539         /**
36540          * Returns the currently selected cell,.
36541          * @return {Array} The selected cell (row, column) or null if none selected.
36542          */
36543     getSelectedCell : function(){
36544         return this.selection ? this.selection.cell : null;
36545     },
36546
36547     /**
36548      * Clears all selections.
36549      * @param {Boolean} true to prevent the gridview from being notified about the change.
36550      */
36551     clearSelections : function(preventNotify){
36552         var s = this.selection;
36553         if(s){
36554             if(preventNotify !== true){
36555                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36556             }
36557             this.selection = null;
36558             this.fireEvent("selectionchange", this, null);
36559         }
36560     },
36561
36562     /**
36563      * Returns true if there is a selection.
36564      * @return {Boolean}
36565      */
36566     hasSelection : function(){
36567         return this.selection ? true : false;
36568     },
36569
36570     /** @ignore */
36571     handleMouseDown : function(e, t){
36572         var v = this.grid.getView();
36573         if(this.isLocked()){
36574             return;
36575         };
36576         var row = v.findRowIndex(t);
36577         var cell = v.findCellIndex(t);
36578         if(row !== false && cell !== false){
36579             this.select(row, cell);
36580         }
36581     },
36582
36583     /**
36584      * Selects a cell.
36585      * @param {Number} rowIndex
36586      * @param {Number} collIndex
36587      */
36588     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36589         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36590             this.clearSelections();
36591             r = r || this.grid.dataSource.getAt(rowIndex);
36592             this.selection = {
36593                 record : r,
36594                 cell : [rowIndex, colIndex]
36595             };
36596             if(!preventViewNotify){
36597                 var v = this.grid.getView();
36598                 v.onCellSelect(rowIndex, colIndex);
36599                 if(preventFocus !== true){
36600                     v.focusCell(rowIndex, colIndex);
36601                 }
36602             }
36603             this.fireEvent("cellselect", this, rowIndex, colIndex);
36604             this.fireEvent("selectionchange", this, this.selection);
36605         }
36606     },
36607
36608         //private
36609     isSelectable : function(rowIndex, colIndex, cm){
36610         return !cm.isHidden(colIndex);
36611     },
36612
36613     /** @ignore */
36614     handleKeyDown : function(e){
36615         //Roo.log('Cell Sel Model handleKeyDown');
36616         if(!e.isNavKeyPress()){
36617             return;
36618         }
36619         var g = this.grid, s = this.selection;
36620         if(!s){
36621             e.stopEvent();
36622             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36623             if(cell){
36624                 this.select(cell[0], cell[1]);
36625             }
36626             return;
36627         }
36628         var sm = this;
36629         var walk = function(row, col, step){
36630             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36631         };
36632         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36633         var newCell;
36634
36635       
36636
36637         switch(k){
36638             case e.TAB:
36639                 // handled by onEditorKey
36640                 if (g.isEditor && g.editing) {
36641                     return;
36642                 }
36643                 if(e.shiftKey) {
36644                     newCell = walk(r, c-1, -1);
36645                 } else {
36646                     newCell = walk(r, c+1, 1);
36647                 }
36648                 break;
36649             
36650             case e.DOWN:
36651                newCell = walk(r+1, c, 1);
36652                 break;
36653             
36654             case e.UP:
36655                 newCell = walk(r-1, c, -1);
36656                 break;
36657             
36658             case e.RIGHT:
36659                 newCell = walk(r, c+1, 1);
36660                 break;
36661             
36662             case e.LEFT:
36663                 newCell = walk(r, c-1, -1);
36664                 break;
36665             
36666             case e.ENTER:
36667                 
36668                 if(g.isEditor && !g.editing){
36669                    g.startEditing(r, c);
36670                    e.stopEvent();
36671                    return;
36672                 }
36673                 
36674                 
36675              break;
36676         };
36677         if(newCell){
36678             this.select(newCell[0], newCell[1]);
36679             e.stopEvent();
36680             
36681         }
36682     },
36683
36684     acceptsNav : function(row, col, cm){
36685         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36686     },
36687     /**
36688      * Selects a cell.
36689      * @param {Number} field (not used) - as it's normally used as a listener
36690      * @param {Number} e - event - fake it by using
36691      *
36692      * var e = Roo.EventObjectImpl.prototype;
36693      * e.keyCode = e.TAB
36694      *
36695      * 
36696      */
36697     onEditorKey : function(field, e){
36698         
36699         var k = e.getKey(),
36700             newCell,
36701             g = this.grid,
36702             ed = g.activeEditor,
36703             forward = false;
36704         ///Roo.log('onEditorKey' + k);
36705         
36706         
36707         if (this.enter_is_tab && k == e.ENTER) {
36708             k = e.TAB;
36709         }
36710         
36711         if(k == e.TAB){
36712             if(e.shiftKey){
36713                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36714             }else{
36715                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36716                 forward = true;
36717             }
36718             
36719             e.stopEvent();
36720             
36721         } else if(k == e.ENTER &&  !e.ctrlKey){
36722             ed.completeEdit();
36723             e.stopEvent();
36724             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36725         
36726                 } else if(k == e.ESC){
36727             ed.cancelEdit();
36728         }
36729                 
36730         if (newCell) {
36731             var ecall = { cell : newCell, forward : forward };
36732             this.fireEvent('beforeeditnext', ecall );
36733             newCell = ecall.cell;
36734                         forward = ecall.forward;
36735         }
36736                 
36737         if(newCell){
36738             //Roo.log('next cell after edit');
36739             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36740         } else if (forward) {
36741             // tabbed past last
36742             this.fireEvent.defer(100, this, ['tabend',this]);
36743         }
36744     }
36745 });/*
36746  * Based on:
36747  * Ext JS Library 1.1.1
36748  * Copyright(c) 2006-2007, Ext JS, LLC.
36749  *
36750  * Originally Released Under LGPL - original licence link has changed is not relivant.
36751  *
36752  * Fork - LGPL
36753  * <script type="text/javascript">
36754  */
36755  
36756 /**
36757  * @class Roo.grid.EditorGrid
36758  * @extends Roo.grid.Grid
36759  * Class for creating and editable grid.
36760  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36761  * The container MUST have some type of size defined for the grid to fill. The container will be 
36762  * automatically set to position relative if it isn't already.
36763  * @param {Object} dataSource The data model to bind to
36764  * @param {Object} colModel The column model with info about this grid's columns
36765  */
36766 Roo.grid.EditorGrid = function(container, config){
36767     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36768     this.getGridEl().addClass("xedit-grid");
36769
36770     if(!this.selModel){
36771         this.selModel = new Roo.grid.CellSelectionModel();
36772     }
36773
36774     this.activeEditor = null;
36775
36776         this.addEvents({
36777             /**
36778              * @event beforeedit
36779              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36780              * <ul style="padding:5px;padding-left:16px;">
36781              * <li>grid - This grid</li>
36782              * <li>record - The record being edited</li>
36783              * <li>field - The field name being edited</li>
36784              * <li>value - The value for the field being edited.</li>
36785              * <li>row - The grid row index</li>
36786              * <li>column - The grid column index</li>
36787              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36788              * </ul>
36789              * @param {Object} e An edit event (see above for description)
36790              */
36791             "beforeedit" : true,
36792             /**
36793              * @event afteredit
36794              * Fires after a cell is edited. <br />
36795              * <ul style="padding:5px;padding-left:16px;">
36796              * <li>grid - This grid</li>
36797              * <li>record - The record being edited</li>
36798              * <li>field - The field name being edited</li>
36799              * <li>value - The value being set</li>
36800              * <li>originalValue - The original value for the field, before the edit.</li>
36801              * <li>row - The grid row index</li>
36802              * <li>column - The grid column index</li>
36803              * </ul>
36804              * @param {Object} e An edit event (see above for description)
36805              */
36806             "afteredit" : true,
36807             /**
36808              * @event validateedit
36809              * Fires after a cell is edited, but before the value is set in the record. 
36810          * You can use this to modify the value being set in the field, Return false
36811              * to cancel the change. The edit event object has the following properties <br />
36812              * <ul style="padding:5px;padding-left:16px;">
36813          * <li>editor - This editor</li>
36814              * <li>grid - This grid</li>
36815              * <li>record - The record being edited</li>
36816              * <li>field - The field name being edited</li>
36817              * <li>value - The value being set</li>
36818              * <li>originalValue - The original value for the field, before the edit.</li>
36819              * <li>row - The grid row index</li>
36820              * <li>column - The grid column index</li>
36821              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36822              * </ul>
36823              * @param {Object} e An edit event (see above for description)
36824              */
36825             "validateedit" : true
36826         });
36827     this.on("bodyscroll", this.stopEditing,  this);
36828     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36829 };
36830
36831 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36832     /**
36833      * @cfg {Number} clicksToEdit
36834      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36835      */
36836     clicksToEdit: 2,
36837
36838     // private
36839     isEditor : true,
36840     // private
36841     trackMouseOver: false, // causes very odd FF errors
36842
36843     onCellDblClick : function(g, row, col){
36844         this.startEditing(row, col);
36845     },
36846
36847     onEditComplete : function(ed, value, startValue){
36848         this.editing = false;
36849         this.activeEditor = null;
36850         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36851         var r = ed.record;
36852         var field = this.colModel.getDataIndex(ed.col);
36853         var e = {
36854             grid: this,
36855             record: r,
36856             field: field,
36857             originalValue: startValue,
36858             value: value,
36859             row: ed.row,
36860             column: ed.col,
36861             cancel:false,
36862             editor: ed
36863         };
36864         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36865         cell.show();
36866           
36867         if(String(value) !== String(startValue)){
36868             
36869             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36870                 r.set(field, e.value);
36871                 // if we are dealing with a combo box..
36872                 // then we also set the 'name' colum to be the displayField
36873                 if (ed.field.displayField && ed.field.name) {
36874                     r.set(ed.field.name, ed.field.el.dom.value);
36875                 }
36876                 
36877                 delete e.cancel; //?? why!!!
36878                 this.fireEvent("afteredit", e);
36879             }
36880         } else {
36881             this.fireEvent("afteredit", e); // always fire it!
36882         }
36883         this.view.focusCell(ed.row, ed.col);
36884     },
36885
36886     /**
36887      * Starts editing the specified for the specified row/column
36888      * @param {Number} rowIndex
36889      * @param {Number} colIndex
36890      */
36891     startEditing : function(row, col){
36892         this.stopEditing();
36893         if(this.colModel.isCellEditable(col, row)){
36894             this.view.ensureVisible(row, col, true);
36895           
36896             var r = this.dataSource.getAt(row);
36897             var field = this.colModel.getDataIndex(col);
36898             var cell = Roo.get(this.view.getCell(row,col));
36899             var e = {
36900                 grid: this,
36901                 record: r,
36902                 field: field,
36903                 value: r.data[field],
36904                 row: row,
36905                 column: col,
36906                 cancel:false 
36907             };
36908             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36909                 this.editing = true;
36910                 var ed = this.colModel.getCellEditor(col, row);
36911                 
36912                 if (!ed) {
36913                     return;
36914                 }
36915                 if(!ed.rendered){
36916                     ed.render(ed.parentEl || document.body);
36917                 }
36918                 ed.field.reset();
36919                
36920                 cell.hide();
36921                 
36922                 (function(){ // complex but required for focus issues in safari, ie and opera
36923                     ed.row = row;
36924                     ed.col = col;
36925                     ed.record = r;
36926                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36927                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36928                     this.activeEditor = ed;
36929                     var v = r.data[field];
36930                     ed.startEdit(this.view.getCell(row, col), v);
36931                     // combo's with 'displayField and name set
36932                     if (ed.field.displayField && ed.field.name) {
36933                         ed.field.el.dom.value = r.data[ed.field.name];
36934                     }
36935                     
36936                     
36937                 }).defer(50, this);
36938             }
36939         }
36940     },
36941         
36942     /**
36943      * Stops any active editing
36944      */
36945     stopEditing : function(){
36946         if(this.activeEditor){
36947             this.activeEditor.completeEdit();
36948         }
36949         this.activeEditor = null;
36950     },
36951         
36952          /**
36953      * Called to get grid's drag proxy text, by default returns this.ddText.
36954      * @return {String}
36955      */
36956     getDragDropText : function(){
36957         var count = this.selModel.getSelectedCell() ? 1 : 0;
36958         return String.format(this.ddText, count, count == 1 ? '' : 's');
36959     }
36960         
36961 });/*
36962  * Based on:
36963  * Ext JS Library 1.1.1
36964  * Copyright(c) 2006-2007, Ext JS, LLC.
36965  *
36966  * Originally Released Under LGPL - original licence link has changed is not relivant.
36967  *
36968  * Fork - LGPL
36969  * <script type="text/javascript">
36970  */
36971
36972 // private - not really -- you end up using it !
36973 // This is a support class used internally by the Grid components
36974
36975 /**
36976  * @class Roo.grid.GridEditor
36977  * @extends Roo.Editor
36978  * Class for creating and editable grid elements.
36979  * @param {Object} config any settings (must include field)
36980  */
36981 Roo.grid.GridEditor = function(field, config){
36982     if (!config && field.field) {
36983         config = field;
36984         field = Roo.factory(config.field, Roo.form);
36985     }
36986     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36987     field.monitorTab = false;
36988 };
36989
36990 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36991     
36992     /**
36993      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36994      */
36995     
36996     alignment: "tl-tl",
36997     autoSize: "width",
36998     hideEl : false,
36999     cls: "x-small-editor x-grid-editor",
37000     shim:false,
37001     shadow:"frame"
37002 });/*
37003  * Based on:
37004  * Ext JS Library 1.1.1
37005  * Copyright(c) 2006-2007, Ext JS, LLC.
37006  *
37007  * Originally Released Under LGPL - original licence link has changed is not relivant.
37008  *
37009  * Fork - LGPL
37010  * <script type="text/javascript">
37011  */
37012   
37013
37014   
37015 Roo.grid.PropertyRecord = Roo.data.Record.create([
37016     {name:'name',type:'string'},  'value'
37017 ]);
37018
37019
37020 Roo.grid.PropertyStore = function(grid, source){
37021     this.grid = grid;
37022     this.store = new Roo.data.Store({
37023         recordType : Roo.grid.PropertyRecord
37024     });
37025     this.store.on('update', this.onUpdate,  this);
37026     if(source){
37027         this.setSource(source);
37028     }
37029     Roo.grid.PropertyStore.superclass.constructor.call(this);
37030 };
37031
37032
37033
37034 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37035     setSource : function(o){
37036         this.source = o;
37037         this.store.removeAll();
37038         var data = [];
37039         for(var k in o){
37040             if(this.isEditableValue(o[k])){
37041                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37042             }
37043         }
37044         this.store.loadRecords({records: data}, {}, true);
37045     },
37046
37047     onUpdate : function(ds, record, type){
37048         if(type == Roo.data.Record.EDIT){
37049             var v = record.data['value'];
37050             var oldValue = record.modified['value'];
37051             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37052                 this.source[record.id] = v;
37053                 record.commit();
37054                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37055             }else{
37056                 record.reject();
37057             }
37058         }
37059     },
37060
37061     getProperty : function(row){
37062        return this.store.getAt(row);
37063     },
37064
37065     isEditableValue: function(val){
37066         if(val && val instanceof Date){
37067             return true;
37068         }else if(typeof val == 'object' || typeof val == 'function'){
37069             return false;
37070         }
37071         return true;
37072     },
37073
37074     setValue : function(prop, value){
37075         this.source[prop] = value;
37076         this.store.getById(prop).set('value', value);
37077     },
37078
37079     getSource : function(){
37080         return this.source;
37081     }
37082 });
37083
37084 Roo.grid.PropertyColumnModel = function(grid, store){
37085     this.grid = grid;
37086     var g = Roo.grid;
37087     g.PropertyColumnModel.superclass.constructor.call(this, [
37088         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37089         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37090     ]);
37091     this.store = store;
37092     this.bselect = Roo.DomHelper.append(document.body, {
37093         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37094             {tag: 'option', value: 'true', html: 'true'},
37095             {tag: 'option', value: 'false', html: 'false'}
37096         ]
37097     });
37098     Roo.id(this.bselect);
37099     var f = Roo.form;
37100     this.editors = {
37101         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37102         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37103         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37104         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37105         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37106     };
37107     this.renderCellDelegate = this.renderCell.createDelegate(this);
37108     this.renderPropDelegate = this.renderProp.createDelegate(this);
37109 };
37110
37111 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37112     
37113     
37114     nameText : 'Name',
37115     valueText : 'Value',
37116     
37117     dateFormat : 'm/j/Y',
37118     
37119     
37120     renderDate : function(dateVal){
37121         return dateVal.dateFormat(this.dateFormat);
37122     },
37123
37124     renderBool : function(bVal){
37125         return bVal ? 'true' : 'false';
37126     },
37127
37128     isCellEditable : function(colIndex, rowIndex){
37129         return colIndex == 1;
37130     },
37131
37132     getRenderer : function(col){
37133         return col == 1 ?
37134             this.renderCellDelegate : this.renderPropDelegate;
37135     },
37136
37137     renderProp : function(v){
37138         return this.getPropertyName(v);
37139     },
37140
37141     renderCell : function(val){
37142         var rv = val;
37143         if(val instanceof Date){
37144             rv = this.renderDate(val);
37145         }else if(typeof val == 'boolean'){
37146             rv = this.renderBool(val);
37147         }
37148         return Roo.util.Format.htmlEncode(rv);
37149     },
37150
37151     getPropertyName : function(name){
37152         var pn = this.grid.propertyNames;
37153         return pn && pn[name] ? pn[name] : name;
37154     },
37155
37156     getCellEditor : function(colIndex, rowIndex){
37157         var p = this.store.getProperty(rowIndex);
37158         var n = p.data['name'], val = p.data['value'];
37159         
37160         if(typeof(this.grid.customEditors[n]) == 'string'){
37161             return this.editors[this.grid.customEditors[n]];
37162         }
37163         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37164             return this.grid.customEditors[n];
37165         }
37166         if(val instanceof Date){
37167             return this.editors['date'];
37168         }else if(typeof val == 'number'){
37169             return this.editors['number'];
37170         }else if(typeof val == 'boolean'){
37171             return this.editors['boolean'];
37172         }else{
37173             return this.editors['string'];
37174         }
37175     }
37176 });
37177
37178 /**
37179  * @class Roo.grid.PropertyGrid
37180  * @extends Roo.grid.EditorGrid
37181  * This class represents the  interface of a component based property grid control.
37182  * <br><br>Usage:<pre><code>
37183  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37184       
37185  });
37186  // set any options
37187  grid.render();
37188  * </code></pre>
37189   
37190  * @constructor
37191  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37192  * The container MUST have some type of size defined for the grid to fill. The container will be
37193  * automatically set to position relative if it isn't already.
37194  * @param {Object} config A config object that sets properties on this grid.
37195  */
37196 Roo.grid.PropertyGrid = function(container, config){
37197     config = config || {};
37198     var store = new Roo.grid.PropertyStore(this);
37199     this.store = store;
37200     var cm = new Roo.grid.PropertyColumnModel(this, store);
37201     store.store.sort('name', 'ASC');
37202     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37203         ds: store.store,
37204         cm: cm,
37205         enableColLock:false,
37206         enableColumnMove:false,
37207         stripeRows:false,
37208         trackMouseOver: false,
37209         clicksToEdit:1
37210     }, config));
37211     this.getGridEl().addClass('x-props-grid');
37212     this.lastEditRow = null;
37213     this.on('columnresize', this.onColumnResize, this);
37214     this.addEvents({
37215          /**
37216              * @event beforepropertychange
37217              * Fires before a property changes (return false to stop?)
37218              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37219              * @param {String} id Record Id
37220              * @param {String} newval New Value
37221          * @param {String} oldval Old Value
37222              */
37223         "beforepropertychange": true,
37224         /**
37225              * @event propertychange
37226              * Fires after a property changes
37227              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37228              * @param {String} id Record Id
37229              * @param {String} newval New Value
37230          * @param {String} oldval Old Value
37231              */
37232         "propertychange": true
37233     });
37234     this.customEditors = this.customEditors || {};
37235 };
37236 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37237     
37238      /**
37239      * @cfg {Object} customEditors map of colnames=> custom editors.
37240      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37241      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37242      * false disables editing of the field.
37243          */
37244     
37245       /**
37246      * @cfg {Object} propertyNames map of property Names to their displayed value
37247          */
37248     
37249     render : function(){
37250         Roo.grid.PropertyGrid.superclass.render.call(this);
37251         this.autoSize.defer(100, this);
37252     },
37253
37254     autoSize : function(){
37255         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37256         if(this.view){
37257             this.view.fitColumns();
37258         }
37259     },
37260
37261     onColumnResize : function(){
37262         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37263         this.autoSize();
37264     },
37265     /**
37266      * Sets the data for the Grid
37267      * accepts a Key => Value object of all the elements avaiable.
37268      * @param {Object} data  to appear in grid.
37269      */
37270     setSource : function(source){
37271         this.store.setSource(source);
37272         //this.autoSize();
37273     },
37274     /**
37275      * Gets all the data from the grid.
37276      * @return {Object} data  data stored in grid
37277      */
37278     getSource : function(){
37279         return this.store.getSource();
37280     }
37281 });/*
37282   
37283  * Licence LGPL
37284  
37285  */
37286  
37287 /**
37288  * @class Roo.grid.Calendar
37289  * @extends Roo.util.Grid
37290  * This class extends the Grid to provide a calendar widget
37291  * <br><br>Usage:<pre><code>
37292  var grid = new Roo.grid.Calendar("my-container-id", {
37293      ds: myDataStore,
37294      cm: myColModel,
37295      selModel: mySelectionModel,
37296      autoSizeColumns: true,
37297      monitorWindowResize: false,
37298      trackMouseOver: true
37299      eventstore : real data store..
37300  });
37301  // set any options
37302  grid.render();
37303   
37304   * @constructor
37305  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37306  * The container MUST have some type of size defined for the grid to fill. The container will be
37307  * automatically set to position relative if it isn't already.
37308  * @param {Object} config A config object that sets properties on this grid.
37309  */
37310 Roo.grid.Calendar = function(container, config){
37311         // initialize the container
37312         this.container = Roo.get(container);
37313         this.container.update("");
37314         this.container.setStyle("overflow", "hidden");
37315     this.container.addClass('x-grid-container');
37316
37317     this.id = this.container.id;
37318
37319     Roo.apply(this, config);
37320     // check and correct shorthanded configs
37321     
37322     var rows = [];
37323     var d =1;
37324     for (var r = 0;r < 6;r++) {
37325         
37326         rows[r]=[];
37327         for (var c =0;c < 7;c++) {
37328             rows[r][c]= '';
37329         }
37330     }
37331     if (this.eventStore) {
37332         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37333         this.eventStore.on('load',this.onLoad, this);
37334         this.eventStore.on('beforeload',this.clearEvents, this);
37335          
37336     }
37337     
37338     this.dataSource = new Roo.data.Store({
37339             proxy: new Roo.data.MemoryProxy(rows),
37340             reader: new Roo.data.ArrayReader({}, [
37341                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37342     });
37343
37344     this.dataSource.load();
37345     this.ds = this.dataSource;
37346     this.ds.xmodule = this.xmodule || false;
37347     
37348     
37349     var cellRender = function(v,x,r)
37350     {
37351         return String.format(
37352             '<div class="fc-day  fc-widget-content"><div>' +
37353                 '<div class="fc-event-container"></div>' +
37354                 '<div class="fc-day-number">{0}</div>'+
37355                 
37356                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37357             '</div></div>', v);
37358     
37359     }
37360     
37361     
37362     this.colModel = new Roo.grid.ColumnModel( [
37363         {
37364             xtype: 'ColumnModel',
37365             xns: Roo.grid,
37366             dataIndex : 'weekday0',
37367             header : 'Sunday',
37368             renderer : cellRender
37369         },
37370         {
37371             xtype: 'ColumnModel',
37372             xns: Roo.grid,
37373             dataIndex : 'weekday1',
37374             header : 'Monday',
37375             renderer : cellRender
37376         },
37377         {
37378             xtype: 'ColumnModel',
37379             xns: Roo.grid,
37380             dataIndex : 'weekday2',
37381             header : 'Tuesday',
37382             renderer : cellRender
37383         },
37384         {
37385             xtype: 'ColumnModel',
37386             xns: Roo.grid,
37387             dataIndex : 'weekday3',
37388             header : 'Wednesday',
37389             renderer : cellRender
37390         },
37391         {
37392             xtype: 'ColumnModel',
37393             xns: Roo.grid,
37394             dataIndex : 'weekday4',
37395             header : 'Thursday',
37396             renderer : cellRender
37397         },
37398         {
37399             xtype: 'ColumnModel',
37400             xns: Roo.grid,
37401             dataIndex : 'weekday5',
37402             header : 'Friday',
37403             renderer : cellRender
37404         },
37405         {
37406             xtype: 'ColumnModel',
37407             xns: Roo.grid,
37408             dataIndex : 'weekday6',
37409             header : 'Saturday',
37410             renderer : cellRender
37411         }
37412     ]);
37413     this.cm = this.colModel;
37414     this.cm.xmodule = this.xmodule || false;
37415  
37416         
37417           
37418     //this.selModel = new Roo.grid.CellSelectionModel();
37419     //this.sm = this.selModel;
37420     //this.selModel.init(this);
37421     
37422     
37423     if(this.width){
37424         this.container.setWidth(this.width);
37425     }
37426
37427     if(this.height){
37428         this.container.setHeight(this.height);
37429     }
37430     /** @private */
37431         this.addEvents({
37432         // raw events
37433         /**
37434          * @event click
37435          * The raw click event for the entire grid.
37436          * @param {Roo.EventObject} e
37437          */
37438         "click" : true,
37439         /**
37440          * @event dblclick
37441          * The raw dblclick event for the entire grid.
37442          * @param {Roo.EventObject} e
37443          */
37444         "dblclick" : true,
37445         /**
37446          * @event contextmenu
37447          * The raw contextmenu event for the entire grid.
37448          * @param {Roo.EventObject} e
37449          */
37450         "contextmenu" : true,
37451         /**
37452          * @event mousedown
37453          * The raw mousedown event for the entire grid.
37454          * @param {Roo.EventObject} e
37455          */
37456         "mousedown" : true,
37457         /**
37458          * @event mouseup
37459          * The raw mouseup event for the entire grid.
37460          * @param {Roo.EventObject} e
37461          */
37462         "mouseup" : true,
37463         /**
37464          * @event mouseover
37465          * The raw mouseover event for the entire grid.
37466          * @param {Roo.EventObject} e
37467          */
37468         "mouseover" : true,
37469         /**
37470          * @event mouseout
37471          * The raw mouseout event for the entire grid.
37472          * @param {Roo.EventObject} e
37473          */
37474         "mouseout" : true,
37475         /**
37476          * @event keypress
37477          * The raw keypress event for the entire grid.
37478          * @param {Roo.EventObject} e
37479          */
37480         "keypress" : true,
37481         /**
37482          * @event keydown
37483          * The raw keydown event for the entire grid.
37484          * @param {Roo.EventObject} e
37485          */
37486         "keydown" : true,
37487
37488         // custom events
37489
37490         /**
37491          * @event cellclick
37492          * Fires when a cell is clicked
37493          * @param {Grid} this
37494          * @param {Number} rowIndex
37495          * @param {Number} columnIndex
37496          * @param {Roo.EventObject} e
37497          */
37498         "cellclick" : true,
37499         /**
37500          * @event celldblclick
37501          * Fires when a cell is double clicked
37502          * @param {Grid} this
37503          * @param {Number} rowIndex
37504          * @param {Number} columnIndex
37505          * @param {Roo.EventObject} e
37506          */
37507         "celldblclick" : true,
37508         /**
37509          * @event rowclick
37510          * Fires when a row is clicked
37511          * @param {Grid} this
37512          * @param {Number} rowIndex
37513          * @param {Roo.EventObject} e
37514          */
37515         "rowclick" : true,
37516         /**
37517          * @event rowdblclick
37518          * Fires when a row is double clicked
37519          * @param {Grid} this
37520          * @param {Number} rowIndex
37521          * @param {Roo.EventObject} e
37522          */
37523         "rowdblclick" : true,
37524         /**
37525          * @event headerclick
37526          * Fires when a header is clicked
37527          * @param {Grid} this
37528          * @param {Number} columnIndex
37529          * @param {Roo.EventObject} e
37530          */
37531         "headerclick" : true,
37532         /**
37533          * @event headerdblclick
37534          * Fires when a header cell is double clicked
37535          * @param {Grid} this
37536          * @param {Number} columnIndex
37537          * @param {Roo.EventObject} e
37538          */
37539         "headerdblclick" : true,
37540         /**
37541          * @event rowcontextmenu
37542          * Fires when a row is right clicked
37543          * @param {Grid} this
37544          * @param {Number} rowIndex
37545          * @param {Roo.EventObject} e
37546          */
37547         "rowcontextmenu" : true,
37548         /**
37549          * @event cellcontextmenu
37550          * Fires when a cell is right clicked
37551          * @param {Grid} this
37552          * @param {Number} rowIndex
37553          * @param {Number} cellIndex
37554          * @param {Roo.EventObject} e
37555          */
37556          "cellcontextmenu" : true,
37557         /**
37558          * @event headercontextmenu
37559          * Fires when a header is right clicked
37560          * @param {Grid} this
37561          * @param {Number} columnIndex
37562          * @param {Roo.EventObject} e
37563          */
37564         "headercontextmenu" : true,
37565         /**
37566          * @event bodyscroll
37567          * Fires when the body element is scrolled
37568          * @param {Number} scrollLeft
37569          * @param {Number} scrollTop
37570          */
37571         "bodyscroll" : true,
37572         /**
37573          * @event columnresize
37574          * Fires when the user resizes a column
37575          * @param {Number} columnIndex
37576          * @param {Number} newSize
37577          */
37578         "columnresize" : true,
37579         /**
37580          * @event columnmove
37581          * Fires when the user moves a column
37582          * @param {Number} oldIndex
37583          * @param {Number} newIndex
37584          */
37585         "columnmove" : true,
37586         /**
37587          * @event startdrag
37588          * Fires when row(s) start being dragged
37589          * @param {Grid} this
37590          * @param {Roo.GridDD} dd The drag drop object
37591          * @param {event} e The raw browser event
37592          */
37593         "startdrag" : true,
37594         /**
37595          * @event enddrag
37596          * Fires when a drag operation is complete
37597          * @param {Grid} this
37598          * @param {Roo.GridDD} dd The drag drop object
37599          * @param {event} e The raw browser event
37600          */
37601         "enddrag" : true,
37602         /**
37603          * @event dragdrop
37604          * Fires when dragged row(s) are dropped on a valid DD target
37605          * @param {Grid} this
37606          * @param {Roo.GridDD} dd The drag drop object
37607          * @param {String} targetId The target drag drop object
37608          * @param {event} e The raw browser event
37609          */
37610         "dragdrop" : true,
37611         /**
37612          * @event dragover
37613          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37614          * @param {Grid} this
37615          * @param {Roo.GridDD} dd The drag drop object
37616          * @param {String} targetId The target drag drop object
37617          * @param {event} e The raw browser event
37618          */
37619         "dragover" : true,
37620         /**
37621          * @event dragenter
37622          *  Fires when the dragged row(s) first cross another DD target while being dragged
37623          * @param {Grid} this
37624          * @param {Roo.GridDD} dd The drag drop object
37625          * @param {String} targetId The target drag drop object
37626          * @param {event} e The raw browser event
37627          */
37628         "dragenter" : true,
37629         /**
37630          * @event dragout
37631          * Fires when the dragged row(s) leave another DD target while being dragged
37632          * @param {Grid} this
37633          * @param {Roo.GridDD} dd The drag drop object
37634          * @param {String} targetId The target drag drop object
37635          * @param {event} e The raw browser event
37636          */
37637         "dragout" : true,
37638         /**
37639          * @event rowclass
37640          * Fires when a row is rendered, so you can change add a style to it.
37641          * @param {GridView} gridview   The grid view
37642          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37643          */
37644         'rowclass' : true,
37645
37646         /**
37647          * @event render
37648          * Fires when the grid is rendered
37649          * @param {Grid} grid
37650          */
37651         'render' : true,
37652             /**
37653              * @event select
37654              * Fires when a date is selected
37655              * @param {DatePicker} this
37656              * @param {Date} date The selected date
37657              */
37658         'select': true,
37659         /**
37660              * @event monthchange
37661              * Fires when the displayed month changes 
37662              * @param {DatePicker} this
37663              * @param {Date} date The selected month
37664              */
37665         'monthchange': true,
37666         /**
37667              * @event evententer
37668              * Fires when mouse over an event
37669              * @param {Calendar} this
37670              * @param {event} Event
37671              */
37672         'evententer': true,
37673         /**
37674              * @event eventleave
37675              * Fires when the mouse leaves an
37676              * @param {Calendar} this
37677              * @param {event}
37678              */
37679         'eventleave': true,
37680         /**
37681              * @event eventclick
37682              * Fires when the mouse click an
37683              * @param {Calendar} this
37684              * @param {event}
37685              */
37686         'eventclick': true,
37687         /**
37688              * @event eventrender
37689              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37690              * @param {Calendar} this
37691              * @param {data} data to be modified
37692              */
37693         'eventrender': true
37694         
37695     });
37696
37697     Roo.grid.Grid.superclass.constructor.call(this);
37698     this.on('render', function() {
37699         this.view.el.addClass('x-grid-cal'); 
37700         
37701         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37702
37703     },this);
37704     
37705     if (!Roo.grid.Calendar.style) {
37706         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37707             
37708             
37709             '.x-grid-cal .x-grid-col' :  {
37710                 height: 'auto !important',
37711                 'vertical-align': 'top'
37712             },
37713             '.x-grid-cal  .fc-event-hori' : {
37714                 height: '14px'
37715             }
37716              
37717             
37718         }, Roo.id());
37719     }
37720
37721     
37722     
37723 };
37724 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37725     /**
37726      * @cfg {Store} eventStore The store that loads events.
37727      */
37728     eventStore : 25,
37729
37730      
37731     activeDate : false,
37732     startDay : 0,
37733     autoWidth : true,
37734     monitorWindowResize : false,
37735
37736     
37737     resizeColumns : function() {
37738         var col = (this.view.el.getWidth() / 7) - 3;
37739         // loop through cols, and setWidth
37740         for(var i =0 ; i < 7 ; i++){
37741             this.cm.setColumnWidth(i, col);
37742         }
37743     },
37744      setDate :function(date) {
37745         
37746         Roo.log('setDate?');
37747         
37748         this.resizeColumns();
37749         var vd = this.activeDate;
37750         this.activeDate = date;
37751 //        if(vd && this.el){
37752 //            var t = date.getTime();
37753 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37754 //                Roo.log('using add remove');
37755 //                
37756 //                this.fireEvent('monthchange', this, date);
37757 //                
37758 //                this.cells.removeClass("fc-state-highlight");
37759 //                this.cells.each(function(c){
37760 //                   if(c.dateValue == t){
37761 //                       c.addClass("fc-state-highlight");
37762 //                       setTimeout(function(){
37763 //                            try{c.dom.firstChild.focus();}catch(e){}
37764 //                       }, 50);
37765 //                       return false;
37766 //                   }
37767 //                   return true;
37768 //                });
37769 //                return;
37770 //            }
37771 //        }
37772         
37773         var days = date.getDaysInMonth();
37774         
37775         var firstOfMonth = date.getFirstDateOfMonth();
37776         var startingPos = firstOfMonth.getDay()-this.startDay;
37777         
37778         if(startingPos < this.startDay){
37779             startingPos += 7;
37780         }
37781         
37782         var pm = date.add(Date.MONTH, -1);
37783         var prevStart = pm.getDaysInMonth()-startingPos;
37784 //        
37785         
37786         
37787         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37788         
37789         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37790         //this.cells.addClassOnOver('fc-state-hover');
37791         
37792         var cells = this.cells.elements;
37793         var textEls = this.textNodes;
37794         
37795         //Roo.each(cells, function(cell){
37796         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37797         //});
37798         
37799         days += startingPos;
37800
37801         // convert everything to numbers so it's fast
37802         var day = 86400000;
37803         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37804         //Roo.log(d);
37805         //Roo.log(pm);
37806         //Roo.log(prevStart);
37807         
37808         var today = new Date().clearTime().getTime();
37809         var sel = date.clearTime().getTime();
37810         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37811         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37812         var ddMatch = this.disabledDatesRE;
37813         var ddText = this.disabledDatesText;
37814         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37815         var ddaysText = this.disabledDaysText;
37816         var format = this.format;
37817         
37818         var setCellClass = function(cal, cell){
37819             
37820             //Roo.log('set Cell Class');
37821             cell.title = "";
37822             var t = d.getTime();
37823             
37824             //Roo.log(d);
37825             
37826             
37827             cell.dateValue = t;
37828             if(t == today){
37829                 cell.className += " fc-today";
37830                 cell.className += " fc-state-highlight";
37831                 cell.title = cal.todayText;
37832             }
37833             if(t == sel){
37834                 // disable highlight in other month..
37835                 cell.className += " fc-state-highlight";
37836                 
37837             }
37838             // disabling
37839             if(t < min) {
37840                 //cell.className = " fc-state-disabled";
37841                 cell.title = cal.minText;
37842                 return;
37843             }
37844             if(t > max) {
37845                 //cell.className = " fc-state-disabled";
37846                 cell.title = cal.maxText;
37847                 return;
37848             }
37849             if(ddays){
37850                 if(ddays.indexOf(d.getDay()) != -1){
37851                     // cell.title = ddaysText;
37852                    // cell.className = " fc-state-disabled";
37853                 }
37854             }
37855             if(ddMatch && format){
37856                 var fvalue = d.dateFormat(format);
37857                 if(ddMatch.test(fvalue)){
37858                     cell.title = ddText.replace("%0", fvalue);
37859                    cell.className = " fc-state-disabled";
37860                 }
37861             }
37862             
37863             if (!cell.initialClassName) {
37864                 cell.initialClassName = cell.dom.className;
37865             }
37866             
37867             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37868         };
37869
37870         var i = 0;
37871         
37872         for(; i < startingPos; i++) {
37873             cells[i].dayName =  (++prevStart);
37874             Roo.log(textEls[i]);
37875             d.setDate(d.getDate()+1);
37876             
37877             //cells[i].className = "fc-past fc-other-month";
37878             setCellClass(this, cells[i]);
37879         }
37880         
37881         var intDay = 0;
37882         
37883         for(; i < days; i++){
37884             intDay = i - startingPos + 1;
37885             cells[i].dayName =  (intDay);
37886             d.setDate(d.getDate()+1);
37887             
37888             cells[i].className = ''; // "x-date-active";
37889             setCellClass(this, cells[i]);
37890         }
37891         var extraDays = 0;
37892         
37893         for(; i < 42; i++) {
37894             //textEls[i].innerHTML = (++extraDays);
37895             
37896             d.setDate(d.getDate()+1);
37897             cells[i].dayName = (++extraDays);
37898             cells[i].className = "fc-future fc-other-month";
37899             setCellClass(this, cells[i]);
37900         }
37901         
37902         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37903         
37904         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37905         
37906         // this will cause all the cells to mis
37907         var rows= [];
37908         var i =0;
37909         for (var r = 0;r < 6;r++) {
37910             for (var c =0;c < 7;c++) {
37911                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37912             }    
37913         }
37914         
37915         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37916         for(i=0;i<cells.length;i++) {
37917             
37918             this.cells.elements[i].dayName = cells[i].dayName ;
37919             this.cells.elements[i].className = cells[i].className;
37920             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37921             this.cells.elements[i].title = cells[i].title ;
37922             this.cells.elements[i].dateValue = cells[i].dateValue ;
37923         }
37924         
37925         
37926         
37927         
37928         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37929         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37930         
37931         ////if(totalRows != 6){
37932             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37933            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37934        // }
37935         
37936         this.fireEvent('monthchange', this, date);
37937         
37938         
37939     },
37940  /**
37941      * Returns the grid's SelectionModel.
37942      * @return {SelectionModel}
37943      */
37944     getSelectionModel : function(){
37945         if(!this.selModel){
37946             this.selModel = new Roo.grid.CellSelectionModel();
37947         }
37948         return this.selModel;
37949     },
37950
37951     load: function() {
37952         this.eventStore.load()
37953         
37954         
37955         
37956     },
37957     
37958     findCell : function(dt) {
37959         dt = dt.clearTime().getTime();
37960         var ret = false;
37961         this.cells.each(function(c){
37962             //Roo.log("check " +c.dateValue + '?=' + dt);
37963             if(c.dateValue == dt){
37964                 ret = c;
37965                 return false;
37966             }
37967             return true;
37968         });
37969         
37970         return ret;
37971     },
37972     
37973     findCells : function(rec) {
37974         var s = rec.data.start_dt.clone().clearTime().getTime();
37975        // Roo.log(s);
37976         var e= rec.data.end_dt.clone().clearTime().getTime();
37977        // Roo.log(e);
37978         var ret = [];
37979         this.cells.each(function(c){
37980              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37981             
37982             if(c.dateValue > e){
37983                 return ;
37984             }
37985             if(c.dateValue < s){
37986                 return ;
37987             }
37988             ret.push(c);
37989         });
37990         
37991         return ret;    
37992     },
37993     
37994     findBestRow: function(cells)
37995     {
37996         var ret = 0;
37997         
37998         for (var i =0 ; i < cells.length;i++) {
37999             ret  = Math.max(cells[i].rows || 0,ret);
38000         }
38001         return ret;
38002         
38003     },
38004     
38005     
38006     addItem : function(rec)
38007     {
38008         // look for vertical location slot in
38009         var cells = this.findCells(rec);
38010         
38011         rec.row = this.findBestRow(cells);
38012         
38013         // work out the location.
38014         
38015         var crow = false;
38016         var rows = [];
38017         for(var i =0; i < cells.length; i++) {
38018             if (!crow) {
38019                 crow = {
38020                     start : cells[i],
38021                     end :  cells[i]
38022                 };
38023                 continue;
38024             }
38025             if (crow.start.getY() == cells[i].getY()) {
38026                 // on same row.
38027                 crow.end = cells[i];
38028                 continue;
38029             }
38030             // different row.
38031             rows.push(crow);
38032             crow = {
38033                 start: cells[i],
38034                 end : cells[i]
38035             };
38036             
38037         }
38038         
38039         rows.push(crow);
38040         rec.els = [];
38041         rec.rows = rows;
38042         rec.cells = cells;
38043         for (var i = 0; i < cells.length;i++) {
38044             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38045             
38046         }
38047         
38048         
38049     },
38050     
38051     clearEvents: function() {
38052         
38053         if (!this.eventStore.getCount()) {
38054             return;
38055         }
38056         // reset number of rows in cells.
38057         Roo.each(this.cells.elements, function(c){
38058             c.rows = 0;
38059         });
38060         
38061         this.eventStore.each(function(e) {
38062             this.clearEvent(e);
38063         },this);
38064         
38065     },
38066     
38067     clearEvent : function(ev)
38068     {
38069         if (ev.els) {
38070             Roo.each(ev.els, function(el) {
38071                 el.un('mouseenter' ,this.onEventEnter, this);
38072                 el.un('mouseleave' ,this.onEventLeave, this);
38073                 el.remove();
38074             },this);
38075             ev.els = [];
38076         }
38077     },
38078     
38079     
38080     renderEvent : function(ev,ctr) {
38081         if (!ctr) {
38082              ctr = this.view.el.select('.fc-event-container',true).first();
38083         }
38084         
38085          
38086         this.clearEvent(ev);
38087             //code
38088        
38089         
38090         
38091         ev.els = [];
38092         var cells = ev.cells;
38093         var rows = ev.rows;
38094         this.fireEvent('eventrender', this, ev);
38095         
38096         for(var i =0; i < rows.length; i++) {
38097             
38098             cls = '';
38099             if (i == 0) {
38100                 cls += ' fc-event-start';
38101             }
38102             if ((i+1) == rows.length) {
38103                 cls += ' fc-event-end';
38104             }
38105             
38106             //Roo.log(ev.data);
38107             // how many rows should it span..
38108             var cg = this.eventTmpl.append(ctr,Roo.apply({
38109                 fccls : cls
38110                 
38111             }, ev.data) , true);
38112             
38113             
38114             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38115             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38116             cg.on('click', this.onEventClick, this, ev);
38117             
38118             ev.els.push(cg);
38119             
38120             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38121             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38122             //Roo.log(cg);
38123              
38124             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38125             cg.setWidth(ebox.right - sbox.x -2);
38126         }
38127     },
38128     
38129     renderEvents: function()
38130     {   
38131         // first make sure there is enough space..
38132         
38133         if (!this.eventTmpl) {
38134             this.eventTmpl = new Roo.Template(
38135                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38136                     '<div class="fc-event-inner">' +
38137                         '<span class="fc-event-time">{time}</span>' +
38138                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38139                     '</div>' +
38140                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38141                 '</div>'
38142             );
38143                 
38144         }
38145                
38146         
38147         
38148         this.cells.each(function(c) {
38149             //Roo.log(c.select('.fc-day-content div',true).first());
38150             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38151         });
38152         
38153         var ctr = this.view.el.select('.fc-event-container',true).first();
38154         
38155         var cls;
38156         this.eventStore.each(function(ev){
38157             
38158             this.renderEvent(ev);
38159              
38160              
38161         }, this);
38162         this.view.layout();
38163         
38164     },
38165     
38166     onEventEnter: function (e, el,event,d) {
38167         this.fireEvent('evententer', this, el, event);
38168     },
38169     
38170     onEventLeave: function (e, el,event,d) {
38171         this.fireEvent('eventleave', this, el, event);
38172     },
38173     
38174     onEventClick: function (e, el,event,d) {
38175         this.fireEvent('eventclick', this, el, event);
38176     },
38177     
38178     onMonthChange: function () {
38179         this.store.load();
38180     },
38181     
38182     onLoad: function () {
38183         
38184         //Roo.log('calendar onload');
38185 //         
38186         if(this.eventStore.getCount() > 0){
38187             
38188            
38189             
38190             this.eventStore.each(function(d){
38191                 
38192                 
38193                 // FIXME..
38194                 var add =   d.data;
38195                 if (typeof(add.end_dt) == 'undefined')  {
38196                     Roo.log("Missing End time in calendar data: ");
38197                     Roo.log(d);
38198                     return;
38199                 }
38200                 if (typeof(add.start_dt) == 'undefined')  {
38201                     Roo.log("Missing Start time in calendar data: ");
38202                     Roo.log(d);
38203                     return;
38204                 }
38205                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38206                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38207                 add.id = add.id || d.id;
38208                 add.title = add.title || '??';
38209                 
38210                 this.addItem(d);
38211                 
38212              
38213             },this);
38214         }
38215         
38216         this.renderEvents();
38217     }
38218     
38219
38220 });
38221 /*
38222  grid : {
38223                 xtype: 'Grid',
38224                 xns: Roo.grid,
38225                 listeners : {
38226                     render : function ()
38227                     {
38228                         _this.grid = this;
38229                         
38230                         if (!this.view.el.hasClass('course-timesheet')) {
38231                             this.view.el.addClass('course-timesheet');
38232                         }
38233                         if (this.tsStyle) {
38234                             this.ds.load({});
38235                             return; 
38236                         }
38237                         Roo.log('width');
38238                         Roo.log(_this.grid.view.el.getWidth());
38239                         
38240                         
38241                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38242                             '.course-timesheet .x-grid-row' : {
38243                                 height: '80px'
38244                             },
38245                             '.x-grid-row td' : {
38246                                 'vertical-align' : 0
38247                             },
38248                             '.course-edit-link' : {
38249                                 'color' : 'blue',
38250                                 'text-overflow' : 'ellipsis',
38251                                 'overflow' : 'hidden',
38252                                 'white-space' : 'nowrap',
38253                                 'cursor' : 'pointer'
38254                             },
38255                             '.sub-link' : {
38256                                 'color' : 'green'
38257                             },
38258                             '.de-act-sup-link' : {
38259                                 'color' : 'purple',
38260                                 'text-decoration' : 'line-through'
38261                             },
38262                             '.de-act-link' : {
38263                                 'color' : 'red',
38264                                 'text-decoration' : 'line-through'
38265                             },
38266                             '.course-timesheet .course-highlight' : {
38267                                 'border-top-style': 'dashed !important',
38268                                 'border-bottom-bottom': 'dashed !important'
38269                             },
38270                             '.course-timesheet .course-item' : {
38271                                 'font-family'   : 'tahoma, arial, helvetica',
38272                                 'font-size'     : '11px',
38273                                 'overflow'      : 'hidden',
38274                                 'padding-left'  : '10px',
38275                                 'padding-right' : '10px',
38276                                 'padding-top' : '10px' 
38277                             }
38278                             
38279                         }, Roo.id());
38280                                 this.ds.load({});
38281                     }
38282                 },
38283                 autoWidth : true,
38284                 monitorWindowResize : false,
38285                 cellrenderer : function(v,x,r)
38286                 {
38287                     return v;
38288                 },
38289                 sm : {
38290                     xtype: 'CellSelectionModel',
38291                     xns: Roo.grid
38292                 },
38293                 dataSource : {
38294                     xtype: 'Store',
38295                     xns: Roo.data,
38296                     listeners : {
38297                         beforeload : function (_self, options)
38298                         {
38299                             options.params = options.params || {};
38300                             options.params._month = _this.monthField.getValue();
38301                             options.params.limit = 9999;
38302                             options.params['sort'] = 'when_dt';    
38303                             options.params['dir'] = 'ASC';    
38304                             this.proxy.loadResponse = this.loadResponse;
38305                             Roo.log("load?");
38306                             //this.addColumns();
38307                         },
38308                         load : function (_self, records, options)
38309                         {
38310                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38311                                 // if you click on the translation.. you can edit it...
38312                                 var el = Roo.get(this);
38313                                 var id = el.dom.getAttribute('data-id');
38314                                 var d = el.dom.getAttribute('data-date');
38315                                 var t = el.dom.getAttribute('data-time');
38316                                 //var id = this.child('span').dom.textContent;
38317                                 
38318                                 //Roo.log(this);
38319                                 Pman.Dialog.CourseCalendar.show({
38320                                     id : id,
38321                                     when_d : d,
38322                                     when_t : t,
38323                                     productitem_active : id ? 1 : 0
38324                                 }, function() {
38325                                     _this.grid.ds.load({});
38326                                 });
38327                            
38328                            });
38329                            
38330                            _this.panel.fireEvent('resize', [ '', '' ]);
38331                         }
38332                     },
38333                     loadResponse : function(o, success, response){
38334                             // this is overridden on before load..
38335                             
38336                             Roo.log("our code?");       
38337                             //Roo.log(success);
38338                             //Roo.log(response)
38339                             delete this.activeRequest;
38340                             if(!success){
38341                                 this.fireEvent("loadexception", this, o, response);
38342                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38343                                 return;
38344                             }
38345                             var result;
38346                             try {
38347                                 result = o.reader.read(response);
38348                             }catch(e){
38349                                 Roo.log("load exception?");
38350                                 this.fireEvent("loadexception", this, o, response, e);
38351                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38352                                 return;
38353                             }
38354                             Roo.log("ready...");        
38355                             // loop through result.records;
38356                             // and set this.tdate[date] = [] << array of records..
38357                             _this.tdata  = {};
38358                             Roo.each(result.records, function(r){
38359                                 //Roo.log(r.data);
38360                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38361                                     _this.tdata[r.data.when_dt.format('j')] = [];
38362                                 }
38363                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38364                             });
38365                             
38366                             //Roo.log(_this.tdata);
38367                             
38368                             result.records = [];
38369                             result.totalRecords = 6;
38370                     
38371                             // let's generate some duumy records for the rows.
38372                             //var st = _this.dateField.getValue();
38373                             
38374                             // work out monday..
38375                             //st = st.add(Date.DAY, -1 * st.format('w'));
38376                             
38377                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38378                             
38379                             var firstOfMonth = date.getFirstDayOfMonth();
38380                             var days = date.getDaysInMonth();
38381                             var d = 1;
38382                             var firstAdded = false;
38383                             for (var i = 0; i < result.totalRecords ; i++) {
38384                                 //var d= st.add(Date.DAY, i);
38385                                 var row = {};
38386                                 var added = 0;
38387                                 for(var w = 0 ; w < 7 ; w++){
38388                                     if(!firstAdded && firstOfMonth != w){
38389                                         continue;
38390                                     }
38391                                     if(d > days){
38392                                         continue;
38393                                     }
38394                                     firstAdded = true;
38395                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38396                                     row['weekday'+w] = String.format(
38397                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38398                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38399                                                     d,
38400                                                     date.format('Y-m-')+dd
38401                                                 );
38402                                     added++;
38403                                     if(typeof(_this.tdata[d]) != 'undefined'){
38404                                         Roo.each(_this.tdata[d], function(r){
38405                                             var is_sub = '';
38406                                             var deactive = '';
38407                                             var id = r.id;
38408                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38409                                             if(r.parent_id*1>0){
38410                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38411                                                 id = r.parent_id;
38412                                             }
38413                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38414                                                 deactive = 'de-act-link';
38415                                             }
38416                                             
38417                                             row['weekday'+w] += String.format(
38418                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38419                                                     id, //0
38420                                                     r.product_id_name, //1
38421                                                     r.when_dt.format('h:ia'), //2
38422                                                     is_sub, //3
38423                                                     deactive, //4
38424                                                     desc // 5
38425                                             );
38426                                         });
38427                                     }
38428                                     d++;
38429                                 }
38430                                 
38431                                 // only do this if something added..
38432                                 if(added > 0){ 
38433                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38434                                 }
38435                                 
38436                                 
38437                                 // push it twice. (second one with an hour..
38438                                 
38439                             }
38440                             //Roo.log(result);
38441                             this.fireEvent("load", this, o, o.request.arg);
38442                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38443                         },
38444                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38445                     proxy : {
38446                         xtype: 'HttpProxy',
38447                         xns: Roo.data,
38448                         method : 'GET',
38449                         url : baseURL + '/Roo/Shop_course.php'
38450                     },
38451                     reader : {
38452                         xtype: 'JsonReader',
38453                         xns: Roo.data,
38454                         id : 'id',
38455                         fields : [
38456                             {
38457                                 'name': 'id',
38458                                 'type': 'int'
38459                             },
38460                             {
38461                                 'name': 'when_dt',
38462                                 'type': 'string'
38463                             },
38464                             {
38465                                 'name': 'end_dt',
38466                                 'type': 'string'
38467                             },
38468                             {
38469                                 'name': 'parent_id',
38470                                 'type': 'int'
38471                             },
38472                             {
38473                                 'name': 'product_id',
38474                                 'type': 'int'
38475                             },
38476                             {
38477                                 'name': 'productitem_id',
38478                                 'type': 'int'
38479                             },
38480                             {
38481                                 'name': 'guid',
38482                                 'type': 'int'
38483                             }
38484                         ]
38485                     }
38486                 },
38487                 toolbar : {
38488                     xtype: 'Toolbar',
38489                     xns: Roo,
38490                     items : [
38491                         {
38492                             xtype: 'Button',
38493                             xns: Roo.Toolbar,
38494                             listeners : {
38495                                 click : function (_self, e)
38496                                 {
38497                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38498                                     sd.setMonth(sd.getMonth()-1);
38499                                     _this.monthField.setValue(sd.format('Y-m-d'));
38500                                     _this.grid.ds.load({});
38501                                 }
38502                             },
38503                             text : "Back"
38504                         },
38505                         {
38506                             xtype: 'Separator',
38507                             xns: Roo.Toolbar
38508                         },
38509                         {
38510                             xtype: 'MonthField',
38511                             xns: Roo.form,
38512                             listeners : {
38513                                 render : function (_self)
38514                                 {
38515                                     _this.monthField = _self;
38516                                    // _this.monthField.set  today
38517                                 },
38518                                 select : function (combo, date)
38519                                 {
38520                                     _this.grid.ds.load({});
38521                                 }
38522                             },
38523                             value : (function() { return new Date(); })()
38524                         },
38525                         {
38526                             xtype: 'Separator',
38527                             xns: Roo.Toolbar
38528                         },
38529                         {
38530                             xtype: 'TextItem',
38531                             xns: Roo.Toolbar,
38532                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38533                         },
38534                         {
38535                             xtype: 'Fill',
38536                             xns: Roo.Toolbar
38537                         },
38538                         {
38539                             xtype: 'Button',
38540                             xns: Roo.Toolbar,
38541                             listeners : {
38542                                 click : function (_self, e)
38543                                 {
38544                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38545                                     sd.setMonth(sd.getMonth()+1);
38546                                     _this.monthField.setValue(sd.format('Y-m-d'));
38547                                     _this.grid.ds.load({});
38548                                 }
38549                             },
38550                             text : "Next"
38551                         }
38552                     ]
38553                 },
38554                  
38555             }
38556         };
38557         
38558         *//*
38559  * Based on:
38560  * Ext JS Library 1.1.1
38561  * Copyright(c) 2006-2007, Ext JS, LLC.
38562  *
38563  * Originally Released Under LGPL - original licence link has changed is not relivant.
38564  *
38565  * Fork - LGPL
38566  * <script type="text/javascript">
38567  */
38568  
38569 /**
38570  * @class Roo.LoadMask
38571  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38572  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38573  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38574  * element's UpdateManager load indicator and will be destroyed after the initial load.
38575  * @constructor
38576  * Create a new LoadMask
38577  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38578  * @param {Object} config The config object
38579  */
38580 Roo.LoadMask = function(el, config){
38581     this.el = Roo.get(el);
38582     Roo.apply(this, config);
38583     if(this.store){
38584         this.store.on('beforeload', this.onBeforeLoad, this);
38585         this.store.on('load', this.onLoad, this);
38586         this.store.on('loadexception', this.onLoadException, this);
38587         this.removeMask = false;
38588     }else{
38589         var um = this.el.getUpdateManager();
38590         um.showLoadIndicator = false; // disable the default indicator
38591         um.on('beforeupdate', this.onBeforeLoad, this);
38592         um.on('update', this.onLoad, this);
38593         um.on('failure', this.onLoad, this);
38594         this.removeMask = true;
38595     }
38596 };
38597
38598 Roo.LoadMask.prototype = {
38599     /**
38600      * @cfg {Boolean} removeMask
38601      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38602      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38603      */
38604     /**
38605      * @cfg {String} msg
38606      * The text to display in a centered loading message box (defaults to 'Loading...')
38607      */
38608     msg : 'Loading...',
38609     /**
38610      * @cfg {String} msgCls
38611      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38612      */
38613     msgCls : 'x-mask-loading',
38614
38615     /**
38616      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38617      * @type Boolean
38618      */
38619     disabled: false,
38620
38621     /**
38622      * Disables the mask to prevent it from being displayed
38623      */
38624     disable : function(){
38625        this.disabled = true;
38626     },
38627
38628     /**
38629      * Enables the mask so that it can be displayed
38630      */
38631     enable : function(){
38632         this.disabled = false;
38633     },
38634     
38635     onLoadException : function()
38636     {
38637         Roo.log(arguments);
38638         
38639         if (typeof(arguments[3]) != 'undefined') {
38640             Roo.MessageBox.alert("Error loading",arguments[3]);
38641         } 
38642         /*
38643         try {
38644             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38645                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38646             }   
38647         } catch(e) {
38648             
38649         }
38650         */
38651     
38652         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38653     },
38654     // private
38655     onLoad : function()
38656     {
38657         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38658     },
38659
38660     // private
38661     onBeforeLoad : function(){
38662         if(!this.disabled){
38663             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38664         }
38665     },
38666
38667     // private
38668     destroy : function(){
38669         if(this.store){
38670             this.store.un('beforeload', this.onBeforeLoad, this);
38671             this.store.un('load', this.onLoad, this);
38672             this.store.un('loadexception', this.onLoadException, this);
38673         }else{
38674             var um = this.el.getUpdateManager();
38675             um.un('beforeupdate', this.onBeforeLoad, this);
38676             um.un('update', this.onLoad, this);
38677             um.un('failure', this.onLoad, this);
38678         }
38679     }
38680 };/*
38681  * Based on:
38682  * Ext JS Library 1.1.1
38683  * Copyright(c) 2006-2007, Ext JS, LLC.
38684  *
38685  * Originally Released Under LGPL - original licence link has changed is not relivant.
38686  *
38687  * Fork - LGPL
38688  * <script type="text/javascript">
38689  */
38690
38691
38692 /**
38693  * @class Roo.XTemplate
38694  * @extends Roo.Template
38695  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38696 <pre><code>
38697 var t = new Roo.XTemplate(
38698         '&lt;select name="{name}"&gt;',
38699                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38700         '&lt;/select&gt;'
38701 );
38702  
38703 // then append, applying the master template values
38704  </code></pre>
38705  *
38706  * Supported features:
38707  *
38708  *  Tags:
38709
38710 <pre><code>
38711       {a_variable} - output encoded.
38712       {a_variable.format:("Y-m-d")} - call a method on the variable
38713       {a_variable:raw} - unencoded output
38714       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38715       {a_variable:this.method_on_template(...)} - call a method on the template object.
38716  
38717 </code></pre>
38718  *  The tpl tag:
38719 <pre><code>
38720         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38721         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38722         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38723         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38724   
38725         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38726         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38727 </code></pre>
38728  *      
38729  */
38730 Roo.XTemplate = function()
38731 {
38732     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38733     if (this.html) {
38734         this.compile();
38735     }
38736 };
38737
38738
38739 Roo.extend(Roo.XTemplate, Roo.Template, {
38740
38741     /**
38742      * The various sub templates
38743      */
38744     tpls : false,
38745     /**
38746      *
38747      * basic tag replacing syntax
38748      * WORD:WORD()
38749      *
38750      * // you can fake an object call by doing this
38751      *  x.t:(test,tesT) 
38752      * 
38753      */
38754     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38755
38756     /**
38757      * compile the template
38758      *
38759      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38760      *
38761      */
38762     compile: function()
38763     {
38764         var s = this.html;
38765      
38766         s = ['<tpl>', s, '</tpl>'].join('');
38767     
38768         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38769             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38770             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38771             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38772             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38773             m,
38774             id     = 0,
38775             tpls   = [];
38776     
38777         while(true == !!(m = s.match(re))){
38778             var forMatch   = m[0].match(nameRe),
38779                 ifMatch   = m[0].match(ifRe),
38780                 execMatch   = m[0].match(execRe),
38781                 namedMatch   = m[0].match(namedRe),
38782                 
38783                 exp  = null, 
38784                 fn   = null,
38785                 exec = null,
38786                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38787                 
38788             if (ifMatch) {
38789                 // if - puts fn into test..
38790                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38791                 if(exp){
38792                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38793                 }
38794             }
38795             
38796             if (execMatch) {
38797                 // exec - calls a function... returns empty if true is  returned.
38798                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38799                 if(exp){
38800                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38801                 }
38802             }
38803             
38804             
38805             if (name) {
38806                 // for = 
38807                 switch(name){
38808                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38809                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38810                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38811                 }
38812             }
38813             var uid = namedMatch ? namedMatch[1] : id;
38814             
38815             
38816             tpls.push({
38817                 id:     namedMatch ? namedMatch[1] : id,
38818                 target: name,
38819                 exec:   exec,
38820                 test:   fn,
38821                 body:   m[1] || ''
38822             });
38823             if (namedMatch) {
38824                 s = s.replace(m[0], '');
38825             } else { 
38826                 s = s.replace(m[0], '{xtpl'+ id + '}');
38827             }
38828             ++id;
38829         }
38830         this.tpls = [];
38831         for(var i = tpls.length-1; i >= 0; --i){
38832             this.compileTpl(tpls[i]);
38833             this.tpls[tpls[i].id] = tpls[i];
38834         }
38835         this.master = tpls[tpls.length-1];
38836         return this;
38837     },
38838     /**
38839      * same as applyTemplate, except it's done to one of the subTemplates
38840      * when using named templates, you can do:
38841      *
38842      * var str = pl.applySubTemplate('your-name', values);
38843      *
38844      * 
38845      * @param {Number} id of the template
38846      * @param {Object} values to apply to template
38847      * @param {Object} parent (normaly the instance of this object)
38848      */
38849     applySubTemplate : function(id, values, parent)
38850     {
38851         
38852         
38853         var t = this.tpls[id];
38854         
38855         
38856         try { 
38857             if(t.test && !t.test.call(this, values, parent)){
38858                 return '';
38859             }
38860         } catch(e) {
38861             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38862             Roo.log(e.toString());
38863             Roo.log(t.test);
38864             return ''
38865         }
38866         try { 
38867             
38868             if(t.exec && t.exec.call(this, values, parent)){
38869                 return '';
38870             }
38871         } catch(e) {
38872             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38873             Roo.log(e.toString());
38874             Roo.log(t.exec);
38875             return ''
38876         }
38877         try {
38878             var vs = t.target ? t.target.call(this, values, parent) : values;
38879             parent = t.target ? values : parent;
38880             if(t.target && vs instanceof Array){
38881                 var buf = [];
38882                 for(var i = 0, len = vs.length; i < len; i++){
38883                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38884                 }
38885                 return buf.join('');
38886             }
38887             return t.compiled.call(this, vs, parent);
38888         } catch (e) {
38889             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38890             Roo.log(e.toString());
38891             Roo.log(t.compiled);
38892             return '';
38893         }
38894     },
38895
38896     compileTpl : function(tpl)
38897     {
38898         var fm = Roo.util.Format;
38899         var useF = this.disableFormats !== true;
38900         var sep = Roo.isGecko ? "+" : ",";
38901         var undef = function(str) {
38902             Roo.log("Property not found :"  + str);
38903             return '';
38904         };
38905         
38906         var fn = function(m, name, format, args)
38907         {
38908             //Roo.log(arguments);
38909             args = args ? args.replace(/\\'/g,"'") : args;
38910             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38911             if (typeof(format) == 'undefined') {
38912                 format= 'htmlEncode';
38913             }
38914             if (format == 'raw' ) {
38915                 format = false;
38916             }
38917             
38918             if(name.substr(0, 4) == 'xtpl'){
38919                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38920             }
38921             
38922             // build an array of options to determine if value is undefined..
38923             
38924             // basically get 'xxxx.yyyy' then do
38925             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38926             //    (function () { Roo.log("Property not found"); return ''; })() :
38927             //    ......
38928             
38929             var udef_ar = [];
38930             var lookfor = '';
38931             Roo.each(name.split('.'), function(st) {
38932                 lookfor += (lookfor.length ? '.': '') + st;
38933                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38934             });
38935             
38936             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38937             
38938             
38939             if(format && useF){
38940                 
38941                 args = args ? ',' + args : "";
38942                  
38943                 if(format.substr(0, 5) != "this."){
38944                     format = "fm." + format + '(';
38945                 }else{
38946                     format = 'this.call("'+ format.substr(5) + '", ';
38947                     args = ", values";
38948                 }
38949                 
38950                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38951             }
38952              
38953             if (args.length) {
38954                 // called with xxyx.yuu:(test,test)
38955                 // change to ()
38956                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38957             }
38958             // raw.. - :raw modifier..
38959             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38960             
38961         };
38962         var body;
38963         // branched to use + in gecko and [].join() in others
38964         if(Roo.isGecko){
38965             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38966                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38967                     "';};};";
38968         }else{
38969             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38970             body.push(tpl.body.replace(/(\r\n|\n)/g,
38971                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38972             body.push("'].join('');};};");
38973             body = body.join('');
38974         }
38975         
38976         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38977        
38978         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38979         eval(body);
38980         
38981         return this;
38982     },
38983
38984     applyTemplate : function(values){
38985         return this.master.compiled.call(this, values, {});
38986         //var s = this.subs;
38987     },
38988
38989     apply : function(){
38990         return this.applyTemplate.apply(this, arguments);
38991     }
38992
38993  });
38994
38995 Roo.XTemplate.from = function(el){
38996     el = Roo.getDom(el);
38997     return new Roo.XTemplate(el.value || el.innerHTML);
38998 };