b612cbfc921dead1c6cf57cbd306aa65df165064
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204             var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     mode: false,
3059     /**
3060      * @cfg {String} offset
3061      * The number of pixels to offset the shadow from the element (defaults to 4)
3062      */
3063     offset: 4,
3064
3065     // private
3066     defaultMode: "drop",
3067
3068     /**
3069      * Displays the shadow under the target element
3070      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3071      */
3072     show : function(target){
3073         target = Roo.get(target);
3074         if(!this.el){
3075             this.el = Roo.Shadow.Pool.pull();
3076             if(this.el.dom.nextSibling != target.dom){
3077                 this.el.insertBefore(target);
3078             }
3079         }
3080         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3081         if(Roo.isIE){
3082             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3083         }
3084         this.realign(
3085             target.getLeft(true),
3086             target.getTop(true),
3087             target.getWidth(),
3088             target.getHeight()
3089         );
3090         this.el.dom.style.display = "block";
3091     },
3092
3093     /**
3094      * Returns true if the shadow is visible, else false
3095      */
3096     isVisible : function(){
3097         return this.el ? true : false;  
3098     },
3099
3100     /**
3101      * Direct alignment when values are already available. Show must be called at least once before
3102      * calling this method to ensure it is initialized.
3103      * @param {Number} left The target element left position
3104      * @param {Number} top The target element top position
3105      * @param {Number} width The target element width
3106      * @param {Number} height The target element height
3107      */
3108     realign : function(l, t, w, h){
3109         if(!this.el){
3110             return;
3111         }
3112         var a = this.adjusts, d = this.el.dom, s = d.style;
3113         var iea = 0;
3114         s.left = (l+a.l)+"px";
3115         s.top = (t+a.t)+"px";
3116         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3117  
3118         if(s.width != sws || s.height != shs){
3119             s.width = sws;
3120             s.height = shs;
3121             if(!Roo.isIE){
3122                 var cn = d.childNodes;
3123                 var sww = Math.max(0, (sw-12))+"px";
3124                 cn[0].childNodes[1].style.width = sww;
3125                 cn[1].childNodes[1].style.width = sww;
3126                 cn[2].childNodes[1].style.width = sww;
3127                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3128             }
3129         }
3130     },
3131
3132     /**
3133      * Hides this shadow
3134      */
3135     hide : function(){
3136         if(this.el){
3137             this.el.dom.style.display = "none";
3138             Roo.Shadow.Pool.push(this.el);
3139             delete this.el;
3140         }
3141     },
3142
3143     /**
3144      * Adjust the z-index of this shadow
3145      * @param {Number} zindex The new z-index
3146      */
3147     setZIndex : function(z){
3148         this.zIndex = z;
3149         if(this.el){
3150             this.el.setStyle("z-index", z);
3151         }
3152     }
3153 };
3154
3155 // Private utility class that manages the internal Shadow cache
3156 Roo.Shadow.Pool = function(){
3157     var p = [];
3158     var markup = Roo.isIE ?
3159                  '<div class="x-ie-shadow"></div>' :
3160                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3161     return {
3162         pull : function(){
3163             var sh = p.shift();
3164             if(!sh){
3165                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3166                 sh.autoBoxAdjust = false;
3167             }
3168             return sh;
3169         },
3170
3171         push : function(sh){
3172             p.push(sh);
3173         }
3174     };
3175 }();/*
3176  * Based on:
3177  * Ext JS Library 1.1.1
3178  * Copyright(c) 2006-2007, Ext JS, LLC.
3179  *
3180  * Originally Released Under LGPL - original licence link has changed is not relivant.
3181  *
3182  * Fork - LGPL
3183  * <script type="text/javascript">
3184  */
3185
3186
3187 /**
3188  * @class Roo.SplitBar
3189  * @extends Roo.util.Observable
3190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3191  * <br><br>
3192  * Usage:
3193  * <pre><code>
3194 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3195                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3196 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3197 split.minSize = 100;
3198 split.maxSize = 600;
3199 split.animate = true;
3200 split.on('moved', splitterMoved);
3201 </code></pre>
3202  * @constructor
3203  * Create a new SplitBar
3204  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3205  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3206  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3207  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3208                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3209                         position of the SplitBar).
3210  */
3211 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3212     
3213     /** @private */
3214     this.el = Roo.get(dragElement, true);
3215     this.el.dom.unselectable = "on";
3216     /** @private */
3217     this.resizingEl = Roo.get(resizingElement, true);
3218
3219     /**
3220      * @private
3221      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3223      * @type Number
3224      */
3225     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3226     
3227     /**
3228      * The minimum size of the resizing element. (Defaults to 0)
3229      * @type Number
3230      */
3231     this.minSize = 0;
3232     
3233     /**
3234      * The maximum size of the resizing element. (Defaults to 2000)
3235      * @type Number
3236      */
3237     this.maxSize = 2000;
3238     
3239     /**
3240      * Whether to animate the transition to the new size
3241      * @type Boolean
3242      */
3243     this.animate = false;
3244     
3245     /**
3246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3247      * @type Boolean
3248      */
3249     this.useShim = false;
3250     
3251     /** @private */
3252     this.shim = null;
3253     
3254     if(!existingProxy){
3255         /** @private */
3256         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3257     }else{
3258         this.proxy = Roo.get(existingProxy).dom;
3259     }
3260     /** @private */
3261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3262     
3263     /** @private */
3264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3265     
3266     /** @private */
3267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dragSpecs = {};
3271     
3272     /**
3273      * @private The adapter to use to positon and resize elements
3274      */
3275     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3276     this.adapter.init(this);
3277     
3278     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3279         /** @private */
3280         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3281         this.el.addClass("x-splitbar-h");
3282     }else{
3283         /** @private */
3284         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3285         this.el.addClass("x-splitbar-v");
3286     }
3287     
3288     this.addEvents({
3289         /**
3290          * @event resize
3291          * Fires when the splitter is moved (alias for {@link #event-moved})
3292          * @param {Roo.SplitBar} this
3293          * @param {Number} newSize the new width or height
3294          */
3295         "resize" : true,
3296         /**
3297          * @event moved
3298          * Fires when the splitter is moved
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "moved" : true,
3303         /**
3304          * @event beforeresize
3305          * Fires before the splitter is dragged
3306          * @param {Roo.SplitBar} this
3307          */
3308         "beforeresize" : true,
3309
3310         "beforeapply" : true
3311     });
3312
3313     Roo.util.Observable.call(this);
3314 };
3315
3316 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3317     onStartProxyDrag : function(x, y){
3318         this.fireEvent("beforeresize", this);
3319         if(!this.overlay){
3320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3321             o.unselectable();
3322             o.enableDisplayMode("block");
3323             // all splitbars share the same overlay
3324             Roo.SplitBar.prototype.overlay = o;
3325         }
3326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3327         this.overlay.show();
3328         Roo.get(this.proxy).setDisplayed("block");
3329         var size = this.adapter.getElementSize(this);
3330         this.activeMinSize = this.getMinimumSize();;
3331         this.activeMaxSize = this.getMaximumSize();;
3332         var c1 = size - this.activeMinSize;
3333         var c2 = Math.max(this.activeMaxSize - size, 0);
3334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3335             this.dd.resetConstraints();
3336             this.dd.setXConstraint(
3337                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3338                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3339             );
3340             this.dd.setYConstraint(0, 0);
3341         }else{
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(0, 0);
3344             this.dd.setYConstraint(
3345                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3346                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3347             );
3348          }
3349         this.dragSpecs.startSize = size;
3350         this.dragSpecs.startPoint = [x, y];
3351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3352     },
3353     
3354     /** 
3355      * @private Called after the drag operation by the DDProxy
3356      */
3357     onEndProxyDrag : function(e){
3358         Roo.get(this.proxy).setDisplayed(false);
3359         var endPoint = Roo.lib.Event.getXY(e);
3360         if(this.overlay){
3361             this.overlay.hide();
3362         }
3363         var newSize;
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             newSize = this.dragSpecs.startSize + 
3366                 (this.placement == Roo.SplitBar.LEFT ?
3367                     endPoint[0] - this.dragSpecs.startPoint[0] :
3368                     this.dragSpecs.startPoint[0] - endPoint[0]
3369                 );
3370         }else{
3371             newSize = this.dragSpecs.startSize + 
3372                 (this.placement == Roo.SplitBar.TOP ?
3373                     endPoint[1] - this.dragSpecs.startPoint[1] :
3374                     this.dragSpecs.startPoint[1] - endPoint[1]
3375                 );
3376         }
3377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3378         if(newSize != this.dragSpecs.startSize){
3379             if(this.fireEvent('beforeapply', this, newSize) !== false){
3380                 this.adapter.setElementSize(this, newSize);
3381                 this.fireEvent("moved", this, newSize);
3382                 this.fireEvent("resize", this, newSize);
3383             }
3384         }
3385     },
3386     
3387     /**
3388      * Get the adapter this SplitBar uses
3389      * @return The adapter object
3390      */
3391     getAdapter : function(){
3392         return this.adapter;
3393     },
3394     
3395     /**
3396      * Set the adapter this SplitBar uses
3397      * @param {Object} adapter A SplitBar adapter object
3398      */
3399     setAdapter : function(adapter){
3400         this.adapter = adapter;
3401         this.adapter.init(this);
3402     },
3403     
3404     /**
3405      * Gets the minimum size for the resizing element
3406      * @return {Number} The minimum size
3407      */
3408     getMinimumSize : function(){
3409         return this.minSize;
3410     },
3411     
3412     /**
3413      * Sets the minimum size for the resizing element
3414      * @param {Number} minSize The minimum size
3415      */
3416     setMinimumSize : function(minSize){
3417         this.minSize = minSize;
3418     },
3419     
3420     /**
3421      * Gets the maximum size for the resizing element
3422      * @return {Number} The maximum size
3423      */
3424     getMaximumSize : function(){
3425         return this.maxSize;
3426     },
3427     
3428     /**
3429      * Sets the maximum size for the resizing element
3430      * @param {Number} maxSize The maximum size
3431      */
3432     setMaximumSize : function(maxSize){
3433         this.maxSize = maxSize;
3434     },
3435     
3436     /**
3437      * Sets the initialize size for the resizing element
3438      * @param {Number} size The initial size
3439      */
3440     setCurrentSize : function(size){
3441         var oldAnimate = this.animate;
3442         this.animate = false;
3443         this.adapter.setElementSize(this, size);
3444         this.animate = oldAnimate;
3445     },
3446     
3447     /**
3448      * Destroy this splitbar. 
3449      * @param {Boolean} removeEl True to remove the element
3450      */
3451     destroy : function(removeEl){
3452         if(this.shim){
3453             this.shim.remove();
3454         }
3455         this.dd.unreg();
3456         this.proxy.parentNode.removeChild(this.proxy);
3457         if(removeEl){
3458             this.el.remove();
3459         }
3460     }
3461 });
3462
3463 /**
3464  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3465  */
3466 Roo.SplitBar.createProxy = function(dir){
3467     var proxy = new Roo.Element(document.createElement("div"));
3468     proxy.unselectable();
3469     var cls = 'x-splitbar-proxy';
3470     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3471     document.body.appendChild(proxy.dom);
3472     return proxy.dom;
3473 };
3474
3475 /** 
3476  * @class Roo.SplitBar.BasicLayoutAdapter
3477  * Default Adapter. It assumes the splitter and resizing element are not positioned
3478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3479  */
3480 Roo.SplitBar.BasicLayoutAdapter = function(){
3481 };
3482
3483 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3484     // do nothing for now
3485     init : function(s){
3486     
3487     },
3488     /**
3489      * Called before drag operations to get the current size of the resizing element. 
3490      * @param {Roo.SplitBar} s The SplitBar using this adapter
3491      */
3492      getElementSize : function(s){
3493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3494             return s.resizingEl.getWidth();
3495         }else{
3496             return s.resizingEl.getHeight();
3497         }
3498     },
3499     
3500     /**
3501      * Called after drag operations to set the size of the resizing element.
3502      * @param {Roo.SplitBar} s The SplitBar using this adapter
3503      * @param {Number} newSize The new size to set
3504      * @param {Function} onComplete A function to be invoked when resizing is complete
3505      */
3506     setElementSize : function(s, newSize, onComplete){
3507         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3508             if(!s.animate){
3509                 s.resizingEl.setWidth(newSize);
3510                 if(onComplete){
3511                     onComplete(s, newSize);
3512                 }
3513             }else{
3514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3515             }
3516         }else{
3517             
3518             if(!s.animate){
3519                 s.resizingEl.setHeight(newSize);
3520                 if(onComplete){
3521                     onComplete(s, newSize);
3522                 }
3523             }else{
3524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3525             }
3526         }
3527     }
3528 };
3529
3530 /** 
3531  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3532  * @extends Roo.SplitBar.BasicLayoutAdapter
3533  * Adapter that  moves the splitter element to align with the resized sizing element. 
3534  * Used with an absolute positioned SplitBar.
3535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3536  * document.body, make sure you assign an id to the body element.
3537  */
3538 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3539     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3540     this.container = Roo.get(container);
3541 };
3542
3543 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3544     init : function(s){
3545         this.basic.init(s);
3546     },
3547     
3548     getElementSize : function(s){
3549         return this.basic.getElementSize(s);
3550     },
3551     
3552     setElementSize : function(s, newSize, onComplete){
3553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3554     },
3555     
3556     moveSplitter : function(s){
3557         var yes = Roo.SplitBar;
3558         switch(s.placement){
3559             case yes.LEFT:
3560                 s.el.setX(s.resizingEl.getRight());
3561                 break;
3562             case yes.RIGHT:
3563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3564                 break;
3565             case yes.TOP:
3566                 s.el.setY(s.resizingEl.getBottom());
3567                 break;
3568             case yes.BOTTOM:
3569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3570                 break;
3571         }
3572     }
3573 };
3574
3575 /**
3576  * Orientation constant - Create a vertical SplitBar
3577  * @static
3578  * @type Number
3579  */
3580 Roo.SplitBar.VERTICAL = 1;
3581
3582 /**
3583  * Orientation constant - Create a horizontal SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.HORIZONTAL = 2;
3588
3589 /**
3590  * Placement constant - The resizing element is to the left of the splitter element
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.LEFT = 1;
3595
3596 /**
3597  * Placement constant - The resizing element is to the right of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.RIGHT = 2;
3602
3603 /**
3604  * Placement constant - The resizing element is positioned above the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.TOP = 3;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned under splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.BOTTOM = 4;
3616 /*
3617  * Based on:
3618  * Ext JS Library 1.1.1
3619  * Copyright(c) 2006-2007, Ext JS, LLC.
3620  *
3621  * Originally Released Under LGPL - original licence link has changed is not relivant.
3622  *
3623  * Fork - LGPL
3624  * <script type="text/javascript">
3625  */
3626
3627 /**
3628  * @class Roo.View
3629  * @extends Roo.util.Observable
3630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3631  * This class also supports single and multi selection modes. <br>
3632  * Create a data model bound view:
3633  <pre><code>
3634  var store = new Roo.data.Store(...);
3635
3636  var view = new Roo.View({
3637     el : "my-element",
3638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3639  
3640     singleSelect: true,
3641     selectedClass: "ydataview-selected",
3642     store: store
3643  });
3644
3645  // listen for node click?
3646  view.on("click", function(vw, index, node, e){
3647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3648  });
3649
3650  // load XML data
3651  dataModel.load("foobar.xml");
3652  </code></pre>
3653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3654  * <br><br>
3655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3657  * 
3658  * Note: old style constructor is still suported (container, template, config)
3659  * 
3660  * @constructor
3661  * Create a new View
3662  * @param {Object} config The config object
3663  * 
3664  */
3665 Roo.View = function(config, depreciated_tpl, depreciated_config){
3666     
3667     this.parent = false;
3668     
3669     if (typeof(depreciated_tpl) == 'undefined') {
3670         // new way.. - universal constructor.
3671         Roo.apply(this, config);
3672         this.el  = Roo.get(this.el);
3673     } else {
3674         // old format..
3675         this.el  = Roo.get(config);
3676         this.tpl = depreciated_tpl;
3677         Roo.apply(this, depreciated_config);
3678     }
3679     this.wrapEl  = this.el.wrap().wrap();
3680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3681     
3682     
3683     if(typeof(this.tpl) == "string"){
3684         this.tpl = new Roo.Template(this.tpl);
3685     } else {
3686         // support xtype ctors..
3687         this.tpl = new Roo.factory(this.tpl, Roo);
3688     }
3689     
3690     
3691     this.tpl.compile();
3692     
3693     /** @private */
3694     this.addEvents({
3695         /**
3696          * @event beforeclick
3697          * Fires before a click is processed. Returns false to cancel the default action.
3698          * @param {Roo.View} this
3699          * @param {Number} index The index of the target node
3700          * @param {HTMLElement} node The target node
3701          * @param {Roo.EventObject} e The raw event object
3702          */
3703             "beforeclick" : true,
3704         /**
3705          * @event click
3706          * Fires when a template node is clicked.
3707          * @param {Roo.View} this
3708          * @param {Number} index The index of the target node
3709          * @param {HTMLElement} node The target node
3710          * @param {Roo.EventObject} e The raw event object
3711          */
3712             "click" : true,
3713         /**
3714          * @event dblclick
3715          * Fires when a template node is double clicked.
3716          * @param {Roo.View} this
3717          * @param {Number} index The index of the target node
3718          * @param {HTMLElement} node The target node
3719          * @param {Roo.EventObject} e The raw event object
3720          */
3721             "dblclick" : true,
3722         /**
3723          * @event contextmenu
3724          * Fires when a template node is right clicked.
3725          * @param {Roo.View} this
3726          * @param {Number} index The index of the target node
3727          * @param {HTMLElement} node The target node
3728          * @param {Roo.EventObject} e The raw event object
3729          */
3730             "contextmenu" : true,
3731         /**
3732          * @event selectionchange
3733          * Fires when the selected nodes change.
3734          * @param {Roo.View} this
3735          * @param {Array} selections Array of the selected nodes
3736          */
3737             "selectionchange" : true,
3738     
3739         /**
3740          * @event beforeselect
3741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3742          * @param {Roo.View} this
3743          * @param {HTMLElement} node The node to be selected
3744          * @param {Array} selections Array of currently selected nodes
3745          */
3746             "beforeselect" : true,
3747         /**
3748          * @event preparedata
3749          * Fires on every row to render, to allow you to change the data.
3750          * @param {Roo.View} this
3751          * @param {Object} data to be rendered (change this)
3752          */
3753           "preparedata" : true
3754           
3755           
3756         });
3757
3758
3759
3760     this.el.on({
3761         "click": this.onClick,
3762         "dblclick": this.onDblClick,
3763         "contextmenu": this.onContextMenu,
3764         scope:this
3765     });
3766
3767     this.selections = [];
3768     this.nodes = [];
3769     this.cmp = new Roo.CompositeElementLite([]);
3770     if(this.store){
3771         this.store = Roo.factory(this.store, Roo.data);
3772         this.setStore(this.store, true);
3773     }
3774     
3775     if ( this.footer && this.footer.xtype) {
3776            
3777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3778         
3779         this.footer.dataSource = this.store;
3780         this.footer.container = fctr;
3781         this.footer = Roo.factory(this.footer, Roo);
3782         fctr.insertFirst(this.el);
3783         
3784         // this is a bit insane - as the paging toolbar seems to detach the el..
3785 //        dom.parentNode.parentNode.parentNode
3786          // they get detached?
3787     }
3788     
3789     
3790     Roo.View.superclass.constructor.call(this);
3791     
3792     
3793 };
3794
3795 Roo.extend(Roo.View, Roo.util.Observable, {
3796     
3797      /**
3798      * @cfg {Roo.data.Store} store Data store to load data from.
3799      */
3800     store : false,
3801     
3802     /**
3803      * @cfg {String|Roo.Element} el The container element.
3804      */
3805     el : '',
3806     
3807     /**
3808      * @cfg {String|Roo.Template} tpl The template used by this View 
3809      */
3810     tpl : false,
3811     /**
3812      * @cfg {String} dataName the named area of the template to use as the data area
3813      *                          Works with domtemplates roo-name="name"
3814      */
3815     dataName: false,
3816     /**
3817      * @cfg {String} selectedClass The css class to add to selected nodes
3818      */
3819     selectedClass : "x-view-selected",
3820      /**
3821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3822      */
3823     emptyText : "",
3824     
3825     /**
3826      * @cfg {String} text to display on mask (default Loading)
3827      */
3828     mask : false,
3829     /**
3830      * @cfg {Boolean} multiSelect Allow multiple selection
3831      */
3832     multiSelect : false,
3833     /**
3834      * @cfg {Boolean} singleSelect Allow single selection
3835      */
3836     singleSelect:  false,
3837     
3838     /**
3839      * @cfg {Boolean} toggleSelect - selecting 
3840      */
3841     toggleSelect : false,
3842     
3843     /**
3844      * @cfg {Boolean} tickable - selecting 
3845      */
3846     tickable : false,
3847     
3848     /**
3849      * Returns the element this view is bound to.
3850      * @return {Roo.Element}
3851      */
3852     getEl : function(){
3853         return this.wrapEl;
3854     },
3855     
3856     
3857
3858     /**
3859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3860      */
3861     refresh : function(){
3862         //Roo.log('refresh');
3863         var t = this.tpl;
3864         
3865         // if we are using something like 'domtemplate', then
3866         // the what gets used is:
3867         // t.applySubtemplate(NAME, data, wrapping data..)
3868         // the outer template then get' applied with
3869         //     the store 'extra data'
3870         // and the body get's added to the
3871         //      roo-name="data" node?
3872         //      <span class='roo-tpl-{name}'></span> ?????
3873         
3874         
3875         
3876         this.clearSelections();
3877         this.el.update("");
3878         var html = [];
3879         var records = this.store.getRange();
3880         if(records.length < 1) {
3881             
3882             // is this valid??  = should it render a template??
3883             
3884             this.el.update(this.emptyText);
3885             return;
3886         }
3887         var el = this.el;
3888         if (this.dataName) {
3889             this.el.update(t.apply(this.store.meta)); //????
3890             el = this.el.child('.roo-tpl-' + this.dataName);
3891         }
3892         
3893         for(var i = 0, len = records.length; i < len; i++){
3894             var data = this.prepareData(records[i].data, i, records[i]);
3895             this.fireEvent("preparedata", this, data, i, records[i]);
3896             
3897             var d = Roo.apply({}, data);
3898             
3899             if(this.tickable){
3900                 Roo.apply(d, {'roo-id' : Roo.id()});
3901                 
3902                 var _this = this;
3903             
3904                 Roo.each(this.parent.item, function(item){
3905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3906                         return;
3907                     }
3908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3909                 });
3910             }
3911             
3912             html[html.length] = Roo.util.Format.trim(
3913                 this.dataName ?
3914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3915                     t.apply(d)
3916             );
3917         }
3918         
3919         
3920         
3921         el.update(html.join(""));
3922         this.nodes = el.dom.childNodes;
3923         this.updateIndexes(0);
3924     },
3925     
3926
3927     /**
3928      * Function to override to reformat the data that is sent to
3929      * the template for each node.
3930      * DEPRICATED - use the preparedata event handler.
3931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3932      * a JSON object for an UpdateManager bound view).
3933      */
3934     prepareData : function(data, index, record)
3935     {
3936         this.fireEvent("preparedata", this, data, index, record);
3937         return data;
3938     },
3939
3940     onUpdate : function(ds, record){
3941         // Roo.log('on update');   
3942         this.clearSelections();
3943         var index = this.store.indexOf(record);
3944         var n = this.nodes[index];
3945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3946         n.parentNode.removeChild(n);
3947         this.updateIndexes(index, index);
3948     },
3949
3950     
3951     
3952 // --------- FIXME     
3953     onAdd : function(ds, records, index)
3954     {
3955         //Roo.log(['on Add', ds, records, index] );        
3956         this.clearSelections();
3957         if(this.nodes.length == 0){
3958             this.refresh();
3959             return;
3960         }
3961         var n = this.nodes[index];
3962         for(var i = 0, len = records.length; i < len; i++){
3963             var d = this.prepareData(records[i].data, i, records[i]);
3964             if(n){
3965                 this.tpl.insertBefore(n, d);
3966             }else{
3967                 
3968                 this.tpl.append(this.el, d);
3969             }
3970         }
3971         this.updateIndexes(index);
3972     },
3973
3974     onRemove : function(ds, record, index){
3975        // Roo.log('onRemove');
3976         this.clearSelections();
3977         var el = this.dataName  ?
3978             this.el.child('.roo-tpl-' + this.dataName) :
3979             this.el; 
3980         
3981         el.dom.removeChild(this.nodes[index]);
3982         this.updateIndexes(index);
3983     },
3984
3985     /**
3986      * Refresh an individual node.
3987      * @param {Number} index
3988      */
3989     refreshNode : function(index){
3990         this.onUpdate(this.store, this.store.getAt(index));
3991     },
3992
3993     updateIndexes : function(startIndex, endIndex){
3994         var ns = this.nodes;
3995         startIndex = startIndex || 0;
3996         endIndex = endIndex || ns.length - 1;
3997         for(var i = startIndex; i <= endIndex; i++){
3998             ns[i].nodeIndex = i;
3999         }
4000     },
4001
4002     /**
4003      * Changes the data store this view uses and refresh the view.
4004      * @param {Store} store
4005      */
4006     setStore : function(store, initial){
4007         if(!initial && this.store){
4008             this.store.un("datachanged", this.refresh);
4009             this.store.un("add", this.onAdd);
4010             this.store.un("remove", this.onRemove);
4011             this.store.un("update", this.onUpdate);
4012             this.store.un("clear", this.refresh);
4013             this.store.un("beforeload", this.onBeforeLoad);
4014             this.store.un("load", this.onLoad);
4015             this.store.un("loadexception", this.onLoad);
4016         }
4017         if(store){
4018           
4019             store.on("datachanged", this.refresh, this);
4020             store.on("add", this.onAdd, this);
4021             store.on("remove", this.onRemove, this);
4022             store.on("update", this.onUpdate, this);
4023             store.on("clear", this.refresh, this);
4024             store.on("beforeload", this.onBeforeLoad, this);
4025             store.on("load", this.onLoad, this);
4026             store.on("loadexception", this.onLoad, this);
4027         }
4028         
4029         if(store){
4030             this.refresh();
4031         }
4032     },
4033     /**
4034      * onbeforeLoad - masks the loading area.
4035      *
4036      */
4037     onBeforeLoad : function(store,opts)
4038     {
4039          //Roo.log('onBeforeLoad');   
4040         if (!opts.add) {
4041             this.el.update("");
4042         }
4043         this.el.mask(this.mask ? this.mask : "Loading" ); 
4044     },
4045     onLoad : function ()
4046     {
4047         this.el.unmask();
4048     },
4049     
4050
4051     /**
4052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4053      * @param {HTMLElement} node
4054      * @return {HTMLElement} The template node
4055      */
4056     findItemFromChild : function(node){
4057         var el = this.dataName  ?
4058             this.el.child('.roo-tpl-' + this.dataName,true) :
4059             this.el.dom; 
4060         
4061         if(!node || node.parentNode == el){
4062                     return node;
4063             }
4064             var p = node.parentNode;
4065             while(p && p != el){
4066             if(p.parentNode == el){
4067                 return p;
4068             }
4069             p = p.parentNode;
4070         }
4071             return null;
4072     },
4073
4074     /** @ignore */
4075     onClick : function(e){
4076         var item = this.findItemFromChild(e.getTarget());
4077         if(item){
4078             var index = this.indexOf(item);
4079             if(this.onItemClick(item, index, e) !== false){
4080                 this.fireEvent("click", this, index, item, e);
4081             }
4082         }else{
4083             this.clearSelections();
4084         }
4085     },
4086
4087     /** @ignore */
4088     onContextMenu : function(e){
4089         var item = this.findItemFromChild(e.getTarget());
4090         if(item){
4091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4092         }
4093     },
4094
4095     /** @ignore */
4096     onDblClick : function(e){
4097         var item = this.findItemFromChild(e.getTarget());
4098         if(item){
4099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4100         }
4101     },
4102
4103     onItemClick : function(item, index, e)
4104     {
4105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4106             return false;
4107         }
4108         if (this.toggleSelect) {
4109             var m = this.isSelected(item) ? 'unselect' : 'select';
4110             //Roo.log(m);
4111             var _t = this;
4112             _t[m](item, true, false);
4113             return true;
4114         }
4115         if(this.multiSelect || this.singleSelect){
4116             if(this.multiSelect && e.shiftKey && this.lastSelection){
4117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4118             }else{
4119                 this.select(item, this.multiSelect && e.ctrlKey);
4120                 this.lastSelection = item;
4121             }
4122             
4123             if(!this.tickable){
4124                 e.preventDefault();
4125             }
4126             
4127         }
4128         return true;
4129     },
4130
4131     /**
4132      * Get the number of selected nodes.
4133      * @return {Number}
4134      */
4135     getSelectionCount : function(){
4136         return this.selections.length;
4137     },
4138
4139     /**
4140      * Get the currently selected nodes.
4141      * @return {Array} An array of HTMLElements
4142      */
4143     getSelectedNodes : function(){
4144         return this.selections;
4145     },
4146
4147     /**
4148      * Get the indexes of the selected nodes.
4149      * @return {Array}
4150      */
4151     getSelectedIndexes : function(){
4152         var indexes = [], s = this.selections;
4153         for(var i = 0, len = s.length; i < len; i++){
4154             indexes.push(s[i].nodeIndex);
4155         }
4156         return indexes;
4157     },
4158
4159     /**
4160      * Clear all selections
4161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4162      */
4163     clearSelections : function(suppressEvent){
4164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4165             this.cmp.elements = this.selections;
4166             this.cmp.removeClass(this.selectedClass);
4167             this.selections = [];
4168             if(!suppressEvent){
4169                 this.fireEvent("selectionchange", this, this.selections);
4170             }
4171         }
4172     },
4173
4174     /**
4175      * Returns true if the passed node is selected
4176      * @param {HTMLElement/Number} node The node or node index
4177      * @return {Boolean}
4178      */
4179     isSelected : function(node){
4180         var s = this.selections;
4181         if(s.length < 1){
4182             return false;
4183         }
4184         node = this.getNode(node);
4185         return s.indexOf(node) !== -1;
4186     },
4187
4188     /**
4189      * Selects nodes.
4190      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4191      * @param {Boolean} keepExisting (optional) true to keep existing selections
4192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4193      */
4194     select : function(nodeInfo, keepExisting, suppressEvent){
4195         if(nodeInfo instanceof Array){
4196             if(!keepExisting){
4197                 this.clearSelections(true);
4198             }
4199             for(var i = 0, len = nodeInfo.length; i < len; i++){
4200                 this.select(nodeInfo[i], true, true);
4201             }
4202             return;
4203         } 
4204         var node = this.getNode(nodeInfo);
4205         if(!node || this.isSelected(node)){
4206             return; // already selected.
4207         }
4208         if(!keepExisting){
4209             this.clearSelections(true);
4210         }
4211         
4212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4213             Roo.fly(node).addClass(this.selectedClass);
4214             this.selections.push(node);
4215             if(!suppressEvent){
4216                 this.fireEvent("selectionchange", this, this.selections);
4217             }
4218         }
4219         
4220         
4221     },
4222       /**
4223      * Unselects nodes.
4224      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4227      */
4228     unselect : function(nodeInfo, keepExisting, suppressEvent)
4229     {
4230         if(nodeInfo instanceof Array){
4231             Roo.each(this.selections, function(s) {
4232                 this.unselect(s, nodeInfo);
4233             }, this);
4234             return;
4235         }
4236         var node = this.getNode(nodeInfo);
4237         if(!node || !this.isSelected(node)){
4238             //Roo.log("not selected");
4239             return; // not selected.
4240         }
4241         // fireevent???
4242         var ns = [];
4243         Roo.each(this.selections, function(s) {
4244             if (s == node ) {
4245                 Roo.fly(node).removeClass(this.selectedClass);
4246
4247                 return;
4248             }
4249             ns.push(s);
4250         },this);
4251         
4252         this.selections= ns;
4253         this.fireEvent("selectionchange", this, this.selections);
4254     },
4255
4256     /**
4257      * Gets a template node.
4258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4259      * @return {HTMLElement} The node or null if it wasn't found
4260      */
4261     getNode : function(nodeInfo){
4262         if(typeof nodeInfo == "string"){
4263             return document.getElementById(nodeInfo);
4264         }else if(typeof nodeInfo == "number"){
4265             return this.nodes[nodeInfo];
4266         }
4267         return nodeInfo;
4268     },
4269
4270     /**
4271      * Gets a range template nodes.
4272      * @param {Number} startIndex
4273      * @param {Number} endIndex
4274      * @return {Array} An array of nodes
4275      */
4276     getNodes : function(start, end){
4277         var ns = this.nodes;
4278         start = start || 0;
4279         end = typeof end == "undefined" ? ns.length - 1 : end;
4280         var nodes = [];
4281         if(start <= end){
4282             for(var i = start; i <= end; i++){
4283                 nodes.push(ns[i]);
4284             }
4285         } else{
4286             for(var i = start; i >= end; i--){
4287                 nodes.push(ns[i]);
4288             }
4289         }
4290         return nodes;
4291     },
4292
4293     /**
4294      * Finds the index of the passed node
4295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4296      * @return {Number} The index of the node or -1
4297      */
4298     indexOf : function(node){
4299         node = this.getNode(node);
4300         if(typeof node.nodeIndex == "number"){
4301             return node.nodeIndex;
4302         }
4303         var ns = this.nodes;
4304         for(var i = 0, len = ns.length; i < len; i++){
4305             if(ns[i] == node){
4306                 return i;
4307             }
4308         }
4309         return -1;
4310     }
4311 });
4312 /*
4313  * Based on:
4314  * Ext JS Library 1.1.1
4315  * Copyright(c) 2006-2007, Ext JS, LLC.
4316  *
4317  * Originally Released Under LGPL - original licence link has changed is not relivant.
4318  *
4319  * Fork - LGPL
4320  * <script type="text/javascript">
4321  */
4322
4323 /**
4324  * @class Roo.JsonView
4325  * @extends Roo.View
4326  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4327 <pre><code>
4328 var view = new Roo.JsonView({
4329     container: "my-element",
4330     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4331     multiSelect: true, 
4332     jsonRoot: "data" 
4333 });
4334
4335 // listen for node click?
4336 view.on("click", function(vw, index, node, e){
4337     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4338 });
4339
4340 // direct load of JSON data
4341 view.load("foobar.php");
4342
4343 // Example from my blog list
4344 var tpl = new Roo.Template(
4345     '&lt;div class="entry"&gt;' +
4346     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4347     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4348     "&lt;/div&gt;&lt;hr /&gt;"
4349 );
4350
4351 var moreView = new Roo.JsonView({
4352     container :  "entry-list", 
4353     template : tpl,
4354     jsonRoot: "posts"
4355 });
4356 moreView.on("beforerender", this.sortEntries, this);
4357 moreView.load({
4358     url: "/blog/get-posts.php",
4359     params: "allposts=true",
4360     text: "Loading Blog Entries..."
4361 });
4362 </code></pre>
4363
4364 * Note: old code is supported with arguments : (container, template, config)
4365
4366
4367  * @constructor
4368  * Create a new JsonView
4369  * 
4370  * @param {Object} config The config object
4371  * 
4372  */
4373 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4374     
4375     
4376     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4377
4378     var um = this.el.getUpdateManager();
4379     um.setRenderer(this);
4380     um.on("update", this.onLoad, this);
4381     um.on("failure", this.onLoadException, this);
4382
4383     /**
4384      * @event beforerender
4385      * Fires before rendering of the downloaded JSON data.
4386      * @param {Roo.JsonView} this
4387      * @param {Object} data The JSON data loaded
4388      */
4389     /**
4390      * @event load
4391      * Fires when data is loaded.
4392      * @param {Roo.JsonView} this
4393      * @param {Object} data The JSON data loaded
4394      * @param {Object} response The raw Connect response object
4395      */
4396     /**
4397      * @event loadexception
4398      * Fires when loading fails.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} response The raw Connect response object
4401      */
4402     this.addEvents({
4403         'beforerender' : true,
4404         'load' : true,
4405         'loadexception' : true
4406     });
4407 };
4408 Roo.extend(Roo.JsonView, Roo.View, {
4409     /**
4410      * @type {String} The root property in the loaded JSON object that contains the data
4411      */
4412     jsonRoot : "",
4413
4414     /**
4415      * Refreshes the view.
4416      */
4417     refresh : function(){
4418         this.clearSelections();
4419         this.el.update("");
4420         var html = [];
4421         var o = this.jsonData;
4422         if(o && o.length > 0){
4423             for(var i = 0, len = o.length; i < len; i++){
4424                 var data = this.prepareData(o[i], i, o);
4425                 html[html.length] = this.tpl.apply(data);
4426             }
4427         }else{
4428             html.push(this.emptyText);
4429         }
4430         this.el.update(html.join(""));
4431         this.nodes = this.el.dom.childNodes;
4432         this.updateIndexes(0);
4433     },
4434
4435     /**
4436      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4437      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4438      <pre><code>
4439      view.load({
4440          url: "your-url.php",
4441          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4442          callback: yourFunction,
4443          scope: yourObject, //(optional scope)
4444          discardUrl: false,
4445          nocache: false,
4446          text: "Loading...",
4447          timeout: 30,
4448          scripts: false
4449      });
4450      </code></pre>
4451      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4452      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4453      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4454      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4455      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4456      */
4457     load : function(){
4458         var um = this.el.getUpdateManager();
4459         um.update.apply(um, arguments);
4460     },
4461
4462     // note - render is a standard framework call...
4463     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4464     render : function(el, response){
4465         
4466         this.clearSelections();
4467         this.el.update("");
4468         var o;
4469         try{
4470             if (response != '') {
4471                 o = Roo.util.JSON.decode(response.responseText);
4472                 if(this.jsonRoot){
4473                     
4474                     o = o[this.jsonRoot];
4475                 }
4476             }
4477         } catch(e){
4478         }
4479         /**
4480          * The current JSON data or null
4481          */
4482         this.jsonData = o;
4483         this.beforeRender();
4484         this.refresh();
4485     },
4486
4487 /**
4488  * Get the number of records in the current JSON dataset
4489  * @return {Number}
4490  */
4491     getCount : function(){
4492         return this.jsonData ? this.jsonData.length : 0;
4493     },
4494
4495 /**
4496  * Returns the JSON object for the specified node(s)
4497  * @param {HTMLElement/Array} node The node or an array of nodes
4498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4499  * you get the JSON object for the node
4500  */
4501     getNodeData : function(node){
4502         if(node instanceof Array){
4503             var data = [];
4504             for(var i = 0, len = node.length; i < len; i++){
4505                 data.push(this.getNodeData(node[i]));
4506             }
4507             return data;
4508         }
4509         return this.jsonData[this.indexOf(node)] || null;
4510     },
4511
4512     beforeRender : function(){
4513         this.snapshot = this.jsonData;
4514         if(this.sortInfo){
4515             this.sort.apply(this, this.sortInfo);
4516         }
4517         this.fireEvent("beforerender", this, this.jsonData);
4518     },
4519
4520     onLoad : function(el, o){
4521         this.fireEvent("load", this, this.jsonData, o);
4522     },
4523
4524     onLoadException : function(el, o){
4525         this.fireEvent("loadexception", this, o);
4526     },
4527
4528 /**
4529  * Filter the data by a specific property.
4530  * @param {String} property A property on your JSON objects
4531  * @param {String/RegExp} value Either string that the property values
4532  * should start with, or a RegExp to test against the property
4533  */
4534     filter : function(property, value){
4535         if(this.jsonData){
4536             var data = [];
4537             var ss = this.snapshot;
4538             if(typeof value == "string"){
4539                 var vlen = value.length;
4540                 if(vlen == 0){
4541                     this.clearFilter();
4542                     return;
4543                 }
4544                 value = value.toLowerCase();
4545                 for(var i = 0, len = ss.length; i < len; i++){
4546                     var o = ss[i];
4547                     if(o[property].substr(0, vlen).toLowerCase() == value){
4548                         data.push(o);
4549                     }
4550                 }
4551             } else if(value.exec){ // regex?
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(value.test(o[property])){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else{
4559                 return;
4560             }
4561             this.jsonData = data;
4562             this.refresh();
4563         }
4564     },
4565
4566 /**
4567  * Filter by a function. The passed function will be called with each
4568  * object in the current dataset. If the function returns true the value is kept,
4569  * otherwise it is filtered.
4570  * @param {Function} fn
4571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4572  */
4573     filterBy : function(fn, scope){
4574         if(this.jsonData){
4575             var data = [];
4576             var ss = this.snapshot;
4577             for(var i = 0, len = ss.length; i < len; i++){
4578                 var o = ss[i];
4579                 if(fn.call(scope || this, o)){
4580                     data.push(o);
4581                 }
4582             }
4583             this.jsonData = data;
4584             this.refresh();
4585         }
4586     },
4587
4588 /**
4589  * Clears the current filter.
4590  */
4591     clearFilter : function(){
4592         if(this.snapshot && this.jsonData != this.snapshot){
4593             this.jsonData = this.snapshot;
4594             this.refresh();
4595         }
4596     },
4597
4598
4599 /**
4600  * Sorts the data for this view and refreshes it.
4601  * @param {String} property A property on your JSON objects to sort on
4602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4604  */
4605     sort : function(property, dir, sortType){
4606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4607         if(this.jsonData){
4608             var p = property;
4609             var dsc = dir && dir.toLowerCase() == "desc";
4610             var f = function(o1, o2){
4611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4613                 ;
4614                 if(v1 < v2){
4615                     return dsc ? +1 : -1;
4616                 } else if(v1 > v2){
4617                     return dsc ? -1 : +1;
4618                 } else{
4619                     return 0;
4620                 }
4621             };
4622             this.jsonData.sort(f);
4623             this.refresh();
4624             if(this.jsonData != this.snapshot){
4625                 this.snapshot.sort(f);
4626             }
4627         }
4628     }
4629 });/*
4630  * Based on:
4631  * Ext JS Library 1.1.1
4632  * Copyright(c) 2006-2007, Ext JS, LLC.
4633  *
4634  * Originally Released Under LGPL - original licence link has changed is not relivant.
4635  *
4636  * Fork - LGPL
4637  * <script type="text/javascript">
4638  */
4639  
4640
4641 /**
4642  * @class Roo.ColorPalette
4643  * @extends Roo.Component
4644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4645  * Here's an example of typical usage:
4646  * <pre><code>
4647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4648 cp.render('my-div');
4649
4650 cp.on('select', function(palette, selColor){
4651     // do something with selColor
4652 });
4653 </code></pre>
4654  * @constructor
4655  * Create a new ColorPalette
4656  * @param {Object} config The config object
4657  */
4658 Roo.ColorPalette = function(config){
4659     Roo.ColorPalette.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         /**
4662              * @event select
4663              * Fires when a color is selected
4664              * @param {ColorPalette} this
4665              * @param {String} color The 6-digit color hex code (without the # symbol)
4666              */
4667         select: true
4668     });
4669
4670     if(this.handler){
4671         this.on("select", this.handler, this.scope, true);
4672     }
4673 };
4674 Roo.extend(Roo.ColorPalette, Roo.Component, {
4675     /**
4676      * @cfg {String} itemCls
4677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4678      */
4679     itemCls : "x-color-palette",
4680     /**
4681      * @cfg {String} value
4682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4683      * the hex codes are case-sensitive.
4684      */
4685     value : null,
4686     clickEvent:'click',
4687     // private
4688     ctype: "Roo.ColorPalette",
4689
4690     /**
4691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4692      */
4693     allowReselect : false,
4694
4695     /**
4696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4699      * of colors with the width setting until the box is symmetrical.</p>
4700      * <p>You can override individual colors if needed:</p>
4701      * <pre><code>
4702 var cp = new Roo.ColorPalette();
4703 cp.colors[0] = "FF0000";  // change the first box to red
4704 </code></pre>
4705
4706 Or you can provide a custom array of your own for complete control:
4707 <pre><code>
4708 var cp = new Roo.ColorPalette();
4709 cp.colors = ["000000", "993300", "333300"];
4710 </code></pre>
4711      * @type Array
4712      */
4713     colors : [
4714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4719     ],
4720
4721     // private
4722     onRender : function(container, position){
4723         var t = new Roo.MasterTemplate(
4724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4725         );
4726         var c = this.colors;
4727         for(var i = 0, len = c.length; i < len; i++){
4728             t.add([c[i]]);
4729         }
4730         var el = document.createElement("div");
4731         el.className = this.itemCls;
4732         t.overwrite(el);
4733         container.dom.insertBefore(el, position);
4734         this.el = Roo.get(el);
4735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4736         if(this.clickEvent != 'click'){
4737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4738         }
4739     },
4740
4741     // private
4742     afterRender : function(){
4743         Roo.ColorPalette.superclass.afterRender.call(this);
4744         if(this.value){
4745             var s = this.value;
4746             this.value = null;
4747             this.select(s);
4748         }
4749     },
4750
4751     // private
4752     handleClick : function(e, t){
4753         e.preventDefault();
4754         if(!this.disabled){
4755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4756             this.select(c.toUpperCase());
4757         }
4758     },
4759
4760     /**
4761      * Selects the specified color in the palette (fires the select event)
4762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4763      */
4764     select : function(color){
4765         color = color.replace("#", "");
4766         if(color != this.value || this.allowReselect){
4767             var el = this.el;
4768             if(this.value){
4769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4770             }
4771             el.child("a.color-"+color).addClass("x-color-palette-sel");
4772             this.value = color;
4773             this.fireEvent("select", this, color);
4774         }
4775     }
4776 });/*
4777  * Based on:
4778  * Ext JS Library 1.1.1
4779  * Copyright(c) 2006-2007, Ext JS, LLC.
4780  *
4781  * Originally Released Under LGPL - original licence link has changed is not relivant.
4782  *
4783  * Fork - LGPL
4784  * <script type="text/javascript">
4785  */
4786  
4787 /**
4788  * @class Roo.DatePicker
4789  * @extends Roo.Component
4790  * Simple date picker class.
4791  * @constructor
4792  * Create a new DatePicker
4793  * @param {Object} config The config object
4794  */
4795 Roo.DatePicker = function(config){
4796     Roo.DatePicker.superclass.constructor.call(this, config);
4797
4798     this.value = config && config.value ?
4799                  config.value.clearTime() : new Date().clearTime();
4800
4801     this.addEvents({
4802         /**
4803              * @event select
4804              * Fires when a date is selected
4805              * @param {DatePicker} this
4806              * @param {Date} date The selected date
4807              */
4808         'select': true,
4809         /**
4810              * @event monthchange
4811              * Fires when the displayed month changes 
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected month
4814              */
4815         'monthchange': true
4816     });
4817
4818     if(this.handler){
4819         this.on("select", this.handler,  this.scope || this);
4820     }
4821     // build the disabledDatesRE
4822     if(!this.disabledDatesRE && this.disabledDates){
4823         var dd = this.disabledDates;
4824         var re = "(?:";
4825         for(var i = 0; i < dd.length; i++){
4826             re += dd[i];
4827             if(i != dd.length-1) {
4828                 re += "|";
4829             }
4830         }
4831         this.disabledDatesRE = new RegExp(re + ")");
4832     }
4833 };
4834
4835 Roo.extend(Roo.DatePicker, Roo.Component, {
4836     /**
4837      * @cfg {String} todayText
4838      * The text to display on the button that selects the current date (defaults to "Today")
4839      */
4840     todayText : "Today",
4841     /**
4842      * @cfg {String} okText
4843      * The text to display on the ok button
4844      */
4845     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4846     /**
4847      * @cfg {String} cancelText
4848      * The text to display on the cancel button
4849      */
4850     cancelText : "Cancel",
4851     /**
4852      * @cfg {String} todayTip
4853      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4854      */
4855     todayTip : "{0} (Spacebar)",
4856     /**
4857      * @cfg {Date} minDate
4858      * Minimum allowable date (JavaScript date object, defaults to null)
4859      */
4860     minDate : null,
4861     /**
4862      * @cfg {Date} maxDate
4863      * Maximum allowable date (JavaScript date object, defaults to null)
4864      */
4865     maxDate : null,
4866     /**
4867      * @cfg {String} minText
4868      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4869      */
4870     minText : "This date is before the minimum date",
4871     /**
4872      * @cfg {String} maxText
4873      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4874      */
4875     maxText : "This date is after the maximum date",
4876     /**
4877      * @cfg {String} format
4878      * The default date format string which can be overriden for localization support.  The format must be
4879      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4880      */
4881     format : "m/d/y",
4882     /**
4883      * @cfg {Array} disabledDays
4884      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4885      */
4886     disabledDays : null,
4887     /**
4888      * @cfg {String} disabledDaysText
4889      * The tooltip to display when the date falls on a disabled day (defaults to "")
4890      */
4891     disabledDaysText : "",
4892     /**
4893      * @cfg {RegExp} disabledDatesRE
4894      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4895      */
4896     disabledDatesRE : null,
4897     /**
4898      * @cfg {String} disabledDatesText
4899      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4900      */
4901     disabledDatesText : "",
4902     /**
4903      * @cfg {Boolean} constrainToViewport
4904      * True to constrain the date picker to the viewport (defaults to true)
4905      */
4906     constrainToViewport : true,
4907     /**
4908      * @cfg {Array} monthNames
4909      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4910      */
4911     monthNames : Date.monthNames,
4912     /**
4913      * @cfg {Array} dayNames
4914      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4915      */
4916     dayNames : Date.dayNames,
4917     /**
4918      * @cfg {String} nextText
4919      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4920      */
4921     nextText: 'Next Month (Control+Right)',
4922     /**
4923      * @cfg {String} prevText
4924      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4925      */
4926     prevText: 'Previous Month (Control+Left)',
4927     /**
4928      * @cfg {String} monthYearText
4929      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4930      */
4931     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4932     /**
4933      * @cfg {Number} startDay
4934      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4935      */
4936     startDay : 0,
4937     /**
4938      * @cfg {Bool} showClear
4939      * Show a clear button (usefull for date form elements that can be blank.)
4940      */
4941     
4942     showClear: false,
4943     
4944     /**
4945      * Sets the value of the date field
4946      * @param {Date} value The date to set
4947      */
4948     setValue : function(value){
4949         var old = this.value;
4950         
4951         if (typeof(value) == 'string') {
4952          
4953             value = Date.parseDate(value, this.format);
4954         }
4955         if (!value) {
4956             value = new Date();
4957         }
4958         
4959         this.value = value.clearTime(true);
4960         if(this.el){
4961             this.update(this.value);
4962         }
4963     },
4964
4965     /**
4966      * Gets the current selected value of the date field
4967      * @return {Date} The selected date
4968      */
4969     getValue : function(){
4970         return this.value;
4971     },
4972
4973     // private
4974     focus : function(){
4975         if(this.el){
4976             this.update(this.activeDate);
4977         }
4978     },
4979
4980     // privateval
4981     onRender : function(container, position){
4982         
4983         var m = [
4984              '<table cellspacing="0">',
4985                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
4986                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4987         var dn = this.dayNames;
4988         for(var i = 0; i < 7; i++){
4989             var d = this.startDay+i;
4990             if(d > 6){
4991                 d = d-7;
4992             }
4993             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4994         }
4995         m[m.length] = "</tr></thead><tbody><tr>";
4996         for(var i = 0; i < 42; i++) {
4997             if(i % 7 == 0 && i != 0){
4998                 m[m.length] = "</tr><tr>";
4999             }
5000             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5001         }
5002         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5003             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5004
5005         var el = document.createElement("div");
5006         el.className = "x-date-picker";
5007         el.innerHTML = m.join("");
5008
5009         container.dom.insertBefore(el, position);
5010
5011         this.el = Roo.get(el);
5012         this.eventEl = Roo.get(el.firstChild);
5013
5014         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5015             handler: this.showPrevMonth,
5016             scope: this,
5017             preventDefault:true,
5018             stopDefault:true
5019         });
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5022             handler: this.showNextMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5029
5030         this.monthPicker = this.el.down('div.x-date-mp');
5031         this.monthPicker.enableDisplayMode('block');
5032         
5033         var kn = new Roo.KeyNav(this.eventEl, {
5034             "left" : function(e){
5035                 e.ctrlKey ?
5036                     this.showPrevMonth() :
5037                     this.update(this.activeDate.add("d", -1));
5038             },
5039
5040             "right" : function(e){
5041                 e.ctrlKey ?
5042                     this.showNextMonth() :
5043                     this.update(this.activeDate.add("d", 1));
5044             },
5045
5046             "up" : function(e){
5047                 e.ctrlKey ?
5048                     this.showNextYear() :
5049                     this.update(this.activeDate.add("d", -7));
5050             },
5051
5052             "down" : function(e){
5053                 e.ctrlKey ?
5054                     this.showPrevYear() :
5055                     this.update(this.activeDate.add("d", 7));
5056             },
5057
5058             "pageUp" : function(e){
5059                 this.showNextMonth();
5060             },
5061
5062             "pageDown" : function(e){
5063                 this.showPrevMonth();
5064             },
5065
5066             "enter" : function(e){
5067                 e.stopPropagation();
5068                 return true;
5069             },
5070
5071             scope : this
5072         });
5073
5074         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5075
5076         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5077
5078         this.el.unselectable();
5079         
5080         this.cells = this.el.select("table.x-date-inner tbody td");
5081         this.textNodes = this.el.query("table.x-date-inner tbody span");
5082
5083         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5084             text: "&#160;",
5085             tooltip: this.monthYearText
5086         });
5087
5088         this.mbtn.on('click', this.showMonthPicker, this);
5089         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5090
5091
5092         var today = (new Date()).dateFormat(this.format);
5093         
5094         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5095         if (this.showClear) {
5096             baseTb.add( new Roo.Toolbar.Fill());
5097         }
5098         baseTb.add({
5099             text: String.format(this.todayText, today),
5100             tooltip: String.format(this.todayTip, today),
5101             handler: this.selectToday,
5102             scope: this
5103         });
5104         
5105         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5106             
5107         //});
5108         if (this.showClear) {
5109             
5110             baseTb.add( new Roo.Toolbar.Fill());
5111             baseTb.add({
5112                 text: '&#160;',
5113                 cls: 'x-btn-icon x-btn-clear',
5114                 handler: function() {
5115                     //this.value = '';
5116                     this.fireEvent("select", this, '');
5117                 },
5118                 scope: this
5119             });
5120         }
5121         
5122         
5123         if(Roo.isIE){
5124             this.el.repaint();
5125         }
5126         this.update(this.value);
5127     },
5128
5129     createMonthPicker : function(){
5130         if(!this.monthPicker.dom.firstChild){
5131             var buf = ['<table border="0" cellspacing="0">'];
5132             for(var i = 0; i < 6; i++){
5133                 buf.push(
5134                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5135                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5136                     i == 0 ?
5137                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5138                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5139                 );
5140             }
5141             buf.push(
5142                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5143                     this.okText,
5144                     '</button><button type="button" class="x-date-mp-cancel">',
5145                     this.cancelText,
5146                     '</button></td></tr>',
5147                 '</table>'
5148             );
5149             this.monthPicker.update(buf.join(''));
5150             this.monthPicker.on('click', this.onMonthClick, this);
5151             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5152
5153             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5154             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5155
5156             this.mpMonths.each(function(m, a, i){
5157                 i += 1;
5158                 if((i%2) == 0){
5159                     m.dom.xmonth = 5 + Math.round(i * .5);
5160                 }else{
5161                     m.dom.xmonth = Math.round((i-1) * .5);
5162                 }
5163             });
5164         }
5165     },
5166
5167     showMonthPicker : function(){
5168         this.createMonthPicker();
5169         var size = this.el.getSize();
5170         this.monthPicker.setSize(size);
5171         this.monthPicker.child('table').setSize(size);
5172
5173         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5174         this.updateMPMonth(this.mpSelMonth);
5175         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5176         this.updateMPYear(this.mpSelYear);
5177
5178         this.monthPicker.slideIn('t', {duration:.2});
5179     },
5180
5181     updateMPYear : function(y){
5182         this.mpyear = y;
5183         var ys = this.mpYears.elements;
5184         for(var i = 1; i <= 10; i++){
5185             var td = ys[i-1], y2;
5186             if((i%2) == 0){
5187                 y2 = y + Math.round(i * .5);
5188                 td.firstChild.innerHTML = y2;
5189                 td.xyear = y2;
5190             }else{
5191                 y2 = y - (5-Math.round(i * .5));
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }
5195             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5196         }
5197     },
5198
5199     updateMPMonth : function(sm){
5200         this.mpMonths.each(function(m, a, i){
5201             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5202         });
5203     },
5204
5205     selectMPMonth: function(m){
5206         
5207     },
5208
5209     onMonthClick : function(e, t){
5210         e.stopEvent();
5211         var el = new Roo.Element(t), pn;
5212         if(el.is('button.x-date-mp-cancel')){
5213             this.hideMonthPicker();
5214         }
5215         else if(el.is('button.x-date-mp-ok')){
5216             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5217             this.hideMonthPicker();
5218         }
5219         else if(pn = el.up('td.x-date-mp-month', 2)){
5220             this.mpMonths.removeClass('x-date-mp-sel');
5221             pn.addClass('x-date-mp-sel');
5222             this.mpSelMonth = pn.dom.xmonth;
5223         }
5224         else if(pn = el.up('td.x-date-mp-year', 2)){
5225             this.mpYears.removeClass('x-date-mp-sel');
5226             pn.addClass('x-date-mp-sel');
5227             this.mpSelYear = pn.dom.xyear;
5228         }
5229         else if(el.is('a.x-date-mp-prev')){
5230             this.updateMPYear(this.mpyear-10);
5231         }
5232         else if(el.is('a.x-date-mp-next')){
5233             this.updateMPYear(this.mpyear+10);
5234         }
5235     },
5236
5237     onMonthDblClick : function(e, t){
5238         e.stopEvent();
5239         var el = new Roo.Element(t), pn;
5240         if(pn = el.up('td.x-date-mp-month', 2)){
5241             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-year', 2)){
5245             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248     },
5249
5250     hideMonthPicker : function(disableAnim){
5251         if(this.monthPicker){
5252             if(disableAnim === true){
5253                 this.monthPicker.hide();
5254             }else{
5255                 this.monthPicker.slideOut('t', {duration:.2});
5256             }
5257         }
5258     },
5259
5260     // private
5261     showPrevMonth : function(e){
5262         this.update(this.activeDate.add("mo", -1));
5263     },
5264
5265     // private
5266     showNextMonth : function(e){
5267         this.update(this.activeDate.add("mo", 1));
5268     },
5269
5270     // private
5271     showPrevYear : function(){
5272         this.update(this.activeDate.add("y", -1));
5273     },
5274
5275     // private
5276     showNextYear : function(){
5277         this.update(this.activeDate.add("y", 1));
5278     },
5279
5280     // private
5281     handleMouseWheel : function(e){
5282         var delta = e.getWheelDelta();
5283         if(delta > 0){
5284             this.showPrevMonth();
5285             e.stopEvent();
5286         } else if(delta < 0){
5287             this.showNextMonth();
5288             e.stopEvent();
5289         }
5290     },
5291
5292     // private
5293     handleDateClick : function(e, t){
5294         e.stopEvent();
5295         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5296             this.setValue(new Date(t.dateValue));
5297             this.fireEvent("select", this, this.value);
5298         }
5299     },
5300
5301     // private
5302     selectToday : function(){
5303         this.setValue(new Date().clearTime());
5304         this.fireEvent("select", this, this.value);
5305     },
5306
5307     // private
5308     update : function(date)
5309     {
5310         var vd = this.activeDate;
5311         this.activeDate = date;
5312         if(vd && this.el){
5313             var t = date.getTime();
5314             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5315                 this.cells.removeClass("x-date-selected");
5316                 this.cells.each(function(c){
5317                    if(c.dom.firstChild.dateValue == t){
5318                        c.addClass("x-date-selected");
5319                        setTimeout(function(){
5320                             try{c.dom.firstChild.focus();}catch(e){}
5321                        }, 50);
5322                        return false;
5323                    }
5324                 });
5325                 return;
5326             }
5327         }
5328         
5329         var days = date.getDaysInMonth();
5330         var firstOfMonth = date.getFirstDateOfMonth();
5331         var startingPos = firstOfMonth.getDay()-this.startDay;
5332
5333         if(startingPos <= this.startDay){
5334             startingPos += 7;
5335         }
5336
5337         var pm = date.add("mo", -1);
5338         var prevStart = pm.getDaysInMonth()-startingPos;
5339
5340         var cells = this.cells.elements;
5341         var textEls = this.textNodes;
5342         days += startingPos;
5343
5344         // convert everything to numbers so it's fast
5345         var day = 86400000;
5346         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5347         var today = new Date().clearTime().getTime();
5348         var sel = date.clearTime().getTime();
5349         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5350         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5351         var ddMatch = this.disabledDatesRE;
5352         var ddText = this.disabledDatesText;
5353         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5354         var ddaysText = this.disabledDaysText;
5355         var format = this.format;
5356
5357         var setCellClass = function(cal, cell){
5358             cell.title = "";
5359             var t = d.getTime();
5360             cell.firstChild.dateValue = t;
5361             if(t == today){
5362                 cell.className += " x-date-today";
5363                 cell.title = cal.todayText;
5364             }
5365             if(t == sel){
5366                 cell.className += " x-date-selected";
5367                 setTimeout(function(){
5368                     try{cell.firstChild.focus();}catch(e){}
5369                 }, 50);
5370             }
5371             // disabling
5372             if(t < min) {
5373                 cell.className = " x-date-disabled";
5374                 cell.title = cal.minText;
5375                 return;
5376             }
5377             if(t > max) {
5378                 cell.className = " x-date-disabled";
5379                 cell.title = cal.maxText;
5380                 return;
5381             }
5382             if(ddays){
5383                 if(ddays.indexOf(d.getDay()) != -1){
5384                     cell.title = ddaysText;
5385                     cell.className = " x-date-disabled";
5386                 }
5387             }
5388             if(ddMatch && format){
5389                 var fvalue = d.dateFormat(format);
5390                 if(ddMatch.test(fvalue)){
5391                     cell.title = ddText.replace("%0", fvalue);
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395         };
5396
5397         var i = 0;
5398         for(; i < startingPos; i++) {
5399             textEls[i].innerHTML = (++prevStart);
5400             d.setDate(d.getDate()+1);
5401             cells[i].className = "x-date-prevday";
5402             setCellClass(this, cells[i]);
5403         }
5404         for(; i < days; i++){
5405             intDay = i - startingPos + 1;
5406             textEls[i].innerHTML = (intDay);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-active";
5409             setCellClass(this, cells[i]);
5410         }
5411         var extraDays = 0;
5412         for(; i < 42; i++) {
5413              textEls[i].innerHTML = (++extraDays);
5414              d.setDate(d.getDate()+1);
5415              cells[i].className = "x-date-nextday";
5416              setCellClass(this, cells[i]);
5417         }
5418
5419         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5420         this.fireEvent('monthchange', this, date);
5421         
5422         if(!this.internalRender){
5423             var main = this.el.dom.firstChild;
5424             var w = main.offsetWidth;
5425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5426             Roo.fly(main).setWidth(w);
5427             this.internalRender = true;
5428             // opera does not respect the auto grow header center column
5429             // then, after it gets a width opera refuses to recalculate
5430             // without a second pass
5431             if(Roo.isOpera && !this.secondPass){
5432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5433                 this.secondPass = true;
5434                 this.update.defer(10, this, [date]);
5435             }
5436         }
5437         
5438         
5439     }
5440 });        /*
5441  * Based on:
5442  * Ext JS Library 1.1.1
5443  * Copyright(c) 2006-2007, Ext JS, LLC.
5444  *
5445  * Originally Released Under LGPL - original licence link has changed is not relivant.
5446  *
5447  * Fork - LGPL
5448  * <script type="text/javascript">
5449  */
5450 /**
5451  * @class Roo.TabPanel
5452  * @extends Roo.util.Observable
5453  * A lightweight tab container.
5454  * <br><br>
5455  * Usage:
5456  * <pre><code>
5457 // basic tabs 1, built from existing content
5458 var tabs = new Roo.TabPanel("tabs1");
5459 tabs.addTab("script", "View Script");
5460 tabs.addTab("markup", "View Markup");
5461 tabs.activate("script");
5462
5463 // more advanced tabs, built from javascript
5464 var jtabs = new Roo.TabPanel("jtabs");
5465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5466
5467 // set up the UpdateManager
5468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5469 var updater = tab2.getUpdateManager();
5470 updater.setDefaultUrl("ajax1.htm");
5471 tab2.on('activate', updater.refresh, updater, true);
5472
5473 // Use setUrl for Ajax loading
5474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5475 tab3.setUrl("ajax2.htm", null, true);
5476
5477 // Disabled tab
5478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5479 tab4.disable();
5480
5481 jtabs.activate("jtabs-1");
5482  * </code></pre>
5483  * @constructor
5484  * Create a new TabPanel.
5485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5487  */
5488 Roo.TabPanel = function(container, config){
5489     /**
5490     * The container element for this TabPanel.
5491     * @type Roo.Element
5492     */
5493     this.el = Roo.get(container, true);
5494     if(config){
5495         if(typeof config == "boolean"){
5496             this.tabPosition = config ? "bottom" : "top";
5497         }else{
5498             Roo.apply(this, config);
5499         }
5500     }
5501     if(this.tabPosition == "bottom"){
5502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5503         this.el.addClass("x-tabs-bottom");
5504     }
5505     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5506     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5507     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5508     if(Roo.isIE){
5509         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5510     }
5511     if(this.tabPosition != "bottom"){
5512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5513          * @type Roo.Element
5514          */
5515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5516         this.el.addClass("x-tabs-top");
5517     }
5518     this.items = [];
5519
5520     this.bodyEl.setStyle("position", "relative");
5521
5522     this.active = null;
5523     this.activateDelegate = this.activate.createDelegate(this);
5524
5525     this.addEvents({
5526         /**
5527          * @event tabchange
5528          * Fires when the active tab changes
5529          * @param {Roo.TabPanel} this
5530          * @param {Roo.TabPanelItem} activePanel The new active tab
5531          */
5532         "tabchange": true,
5533         /**
5534          * @event beforetabchange
5535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5536          * @param {Roo.TabPanel} this
5537          * @param {Object} e Set cancel to true on this object to cancel the tab change
5538          * @param {Roo.TabPanelItem} tab The tab being changed to
5539          */
5540         "beforetabchange" : true
5541     });
5542
5543     Roo.EventManager.onWindowResize(this.onResize, this);
5544     this.cpad = this.el.getPadding("lr");
5545     this.hiddenCount = 0;
5546
5547
5548     // toolbar on the tabbar support...
5549     if (this.toolbar) {
5550         var tcfg = this.toolbar;
5551         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5552         this.toolbar = new Roo.Toolbar(tcfg);
5553         if (Roo.isSafari) {
5554             var tbl = tcfg.container.child('table', true);
5555             tbl.setAttribute('width', '100%');
5556         }
5557         
5558     }
5559    
5560
5561
5562     Roo.TabPanel.superclass.constructor.call(this);
5563 };
5564
5565 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5566     /*
5567      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5568      */
5569     tabPosition : "top",
5570     /*
5571      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5572      */
5573     currentTabWidth : 0,
5574     /*
5575      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5576      */
5577     minTabWidth : 40,
5578     /*
5579      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     maxTabWidth : 250,
5582     /*
5583      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     preferredTabWidth : 175,
5586     /*
5587      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5588      */
5589     resizeTabs : false,
5590     /*
5591      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5592      */
5593     monitorResize : true,
5594     /*
5595      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5596      */
5597     toolbar : false,
5598
5599     /**
5600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5601      * @param {String} id The id of the div to use <b>or create</b>
5602      * @param {String} text The text for the tab
5603      * @param {String} content (optional) Content to put in the TabPanelItem body
5604      * @param {Boolean} closable (optional) True to create a close icon on the tab
5605      * @return {Roo.TabPanelItem} The created TabPanelItem
5606      */
5607     addTab : function(id, text, content, closable){
5608         var item = new Roo.TabPanelItem(this, id, text, closable);
5609         this.addTabItem(item);
5610         if(content){
5611             item.setContent(content);
5612         }
5613         return item;
5614     },
5615
5616     /**
5617      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5618      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5619      * @return {Roo.TabPanelItem}
5620      */
5621     getTab : function(id){
5622         return this.items[id];
5623     },
5624
5625     /**
5626      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5627      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5628      */
5629     hideTab : function(id){
5630         var t = this.items[id];
5631         if(!t.isHidden()){
5632            t.setHidden(true);
5633            this.hiddenCount++;
5634            this.autoSizeTabs();
5635         }
5636     },
5637
5638     /**
5639      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5640      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5641      */
5642     unhideTab : function(id){
5643         var t = this.items[id];
5644         if(t.isHidden()){
5645            t.setHidden(false);
5646            this.hiddenCount--;
5647            this.autoSizeTabs();
5648         }
5649     },
5650
5651     /**
5652      * Adds an existing {@link Roo.TabPanelItem}.
5653      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5654      */
5655     addTabItem : function(item){
5656         this.items[item.id] = item;
5657         this.items.push(item);
5658         if(this.resizeTabs){
5659            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5660            this.autoSizeTabs();
5661         }else{
5662             item.autoSize();
5663         }
5664     },
5665
5666     /**
5667      * Removes a {@link Roo.TabPanelItem}.
5668      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5669      */
5670     removeTab : function(id){
5671         var items = this.items;
5672         var tab = items[id];
5673         if(!tab) { return; }
5674         var index = items.indexOf(tab);
5675         if(this.active == tab && items.length > 1){
5676             var newTab = this.getNextAvailable(index);
5677             if(newTab) {
5678                 newTab.activate();
5679             }
5680         }
5681         this.stripEl.dom.removeChild(tab.pnode.dom);
5682         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5683             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5684         }
5685         items.splice(index, 1);
5686         delete this.items[tab.id];
5687         tab.fireEvent("close", tab);
5688         tab.purgeListeners();
5689         this.autoSizeTabs();
5690     },
5691
5692     getNextAvailable : function(start){
5693         var items = this.items;
5694         var index = start;
5695         // look for a next tab that will slide over to
5696         // replace the one being removed
5697         while(index < items.length){
5698             var item = items[++index];
5699             if(item && !item.isHidden()){
5700                 return item;
5701             }
5702         }
5703         // if one isn't found select the previous tab (on the left)
5704         index = start;
5705         while(index >= 0){
5706             var item = items[--index];
5707             if(item && !item.isHidden()){
5708                 return item;
5709             }
5710         }
5711         return null;
5712     },
5713
5714     /**
5715      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5716      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5717      */
5718     disableTab : function(id){
5719         var tab = this.items[id];
5720         if(tab && this.active != tab){
5721             tab.disable();
5722         }
5723     },
5724
5725     /**
5726      * Enables a {@link Roo.TabPanelItem} that is disabled.
5727      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5728      */
5729     enableTab : function(id){
5730         var tab = this.items[id];
5731         tab.enable();
5732     },
5733
5734     /**
5735      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5736      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5737      * @return {Roo.TabPanelItem} The TabPanelItem.
5738      */
5739     activate : function(id){
5740         var tab = this.items[id];
5741         if(!tab){
5742             return null;
5743         }
5744         if(tab == this.active || tab.disabled){
5745             return tab;
5746         }
5747         var e = {};
5748         this.fireEvent("beforetabchange", this, e, tab);
5749         if(e.cancel !== true && !tab.disabled){
5750             if(this.active){
5751                 this.active.hide();
5752             }
5753             this.active = this.items[id];
5754             this.active.show();
5755             this.fireEvent("tabchange", this, this.active);
5756         }
5757         return tab;
5758     },
5759
5760     /**
5761      * Gets the active {@link Roo.TabPanelItem}.
5762      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5763      */
5764     getActiveTab : function(){
5765         return this.active;
5766     },
5767
5768     /**
5769      * Updates the tab body element to fit the height of the container element
5770      * for overflow scrolling
5771      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5772      */
5773     syncHeight : function(targetHeight){
5774         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5775         var bm = this.bodyEl.getMargins();
5776         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5777         this.bodyEl.setHeight(newHeight);
5778         return newHeight;
5779     },
5780
5781     onResize : function(){
5782         if(this.monitorResize){
5783             this.autoSizeTabs();
5784         }
5785     },
5786
5787     /**
5788      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5789      */
5790     beginUpdate : function(){
5791         this.updating = true;
5792     },
5793
5794     /**
5795      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     endUpdate : function(){
5798         this.updating = false;
5799         this.autoSizeTabs();
5800     },
5801
5802     /**
5803      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     autoSizeTabs : function(){
5806         var count = this.items.length;
5807         var vcount = count - this.hiddenCount;
5808         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5809             return;
5810         }
5811         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5812         var availWidth = Math.floor(w / vcount);
5813         var b = this.stripBody;
5814         if(b.getWidth() > w){
5815             var tabs = this.items;
5816             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5817             if(availWidth < this.minTabWidth){
5818                 /*if(!this.sleft){    // incomplete scrolling code
5819                     this.createScrollButtons();
5820                 }
5821                 this.showScroll();
5822                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5823             }
5824         }else{
5825             if(this.currentTabWidth < this.preferredTabWidth){
5826                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5827             }
5828         }
5829     },
5830
5831     /**
5832      * Returns the number of tabs in this TabPanel.
5833      * @return {Number}
5834      */
5835      getCount : function(){
5836          return this.items.length;
5837      },
5838
5839     /**
5840      * Resizes all the tabs to the passed width
5841      * @param {Number} The new width
5842      */
5843     setTabWidth : function(width){
5844         this.currentTabWidth = width;
5845         for(var i = 0, len = this.items.length; i < len; i++) {
5846                 if(!this.items[i].isHidden()) {
5847                 this.items[i].setWidth(width);
5848             }
5849         }
5850     },
5851
5852     /**
5853      * Destroys this TabPanel
5854      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5855      */
5856     destroy : function(removeEl){
5857         Roo.EventManager.removeResizeListener(this.onResize, this);
5858         for(var i = 0, len = this.items.length; i < len; i++){
5859             this.items[i].purgeListeners();
5860         }
5861         if(removeEl === true){
5862             this.el.update("");
5863             this.el.remove();
5864         }
5865     }
5866 });
5867
5868 /**
5869  * @class Roo.TabPanelItem
5870  * @extends Roo.util.Observable
5871  * Represents an individual item (tab plus body) in a TabPanel.
5872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5873  * @param {String} id The id of this TabPanelItem
5874  * @param {String} text The text for the tab of this TabPanelItem
5875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5876  */
5877 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5878     /**
5879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5880      * @type Roo.TabPanel
5881      */
5882     this.tabPanel = tabPanel;
5883     /**
5884      * The id for this TabPanelItem
5885      * @type String
5886      */
5887     this.id = id;
5888     /** @private */
5889     this.disabled = false;
5890     /** @private */
5891     this.text = text;
5892     /** @private */
5893     this.loaded = false;
5894     this.closable = closable;
5895
5896     /**
5897      * The body element for this TabPanelItem.
5898      * @type Roo.Element
5899      */
5900     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5902     this.bodyEl.setStyle("display", "block");
5903     this.bodyEl.setStyle("zoom", "1");
5904     this.hideAction();
5905
5906     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5907     /** @private */
5908     this.el = Roo.get(els.el, true);
5909     this.inner = Roo.get(els.inner, true);
5910     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5911     this.pnode = Roo.get(els.el.parentNode, true);
5912     this.el.on("mousedown", this.onTabMouseDown, this);
5913     this.el.on("click", this.onTabClick, this);
5914     /** @private */
5915     if(closable){
5916         var c = Roo.get(els.close, true);
5917         c.dom.title = this.closeText;
5918         c.addClassOnOver("close-over");
5919         c.on("click", this.closeClick, this);
5920      }
5921
5922     this.addEvents({
5923          /**
5924          * @event activate
5925          * Fires when this tab becomes the active tab.
5926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5927          * @param {Roo.TabPanelItem} this
5928          */
5929         "activate": true,
5930         /**
5931          * @event beforeclose
5932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5933          * @param {Roo.TabPanelItem} this
5934          * @param {Object} e Set cancel to true on this object to cancel the close.
5935          */
5936         "beforeclose": true,
5937         /**
5938          * @event close
5939          * Fires when this tab is closed.
5940          * @param {Roo.TabPanelItem} this
5941          */
5942          "close": true,
5943         /**
5944          * @event deactivate
5945          * Fires when this tab is no longer the active tab.
5946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "deactivate" : true
5950     });
5951     this.hidden = false;
5952
5953     Roo.TabPanelItem.superclass.constructor.call(this);
5954 };
5955
5956 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5957     purgeListeners : function(){
5958        Roo.util.Observable.prototype.purgeListeners.call(this);
5959        this.el.removeAllListeners();
5960     },
5961     /**
5962      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5963      */
5964     show : function(){
5965         this.pnode.addClass("on");
5966         this.showAction();
5967         if(Roo.isOpera){
5968             this.tabPanel.stripWrap.repaint();
5969         }
5970         this.fireEvent("activate", this.tabPanel, this);
5971     },
5972
5973     /**
5974      * Returns true if this tab is the active tab.
5975      * @return {Boolean}
5976      */
5977     isActive : function(){
5978         return this.tabPanel.getActiveTab() == this;
5979     },
5980
5981     /**
5982      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5983      */
5984     hide : function(){
5985         this.pnode.removeClass("on");
5986         this.hideAction();
5987         this.fireEvent("deactivate", this.tabPanel, this);
5988     },
5989
5990     hideAction : function(){
5991         this.bodyEl.hide();
5992         this.bodyEl.setStyle("position", "absolute");
5993         this.bodyEl.setLeft("-20000px");
5994         this.bodyEl.setTop("-20000px");
5995     },
5996
5997     showAction : function(){
5998         this.bodyEl.setStyle("position", "relative");
5999         this.bodyEl.setTop("");
6000         this.bodyEl.setLeft("");
6001         this.bodyEl.show();
6002     },
6003
6004     /**
6005      * Set the tooltip for the tab.
6006      * @param {String} tooltip The tab's tooltip
6007      */
6008     setTooltip : function(text){
6009         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6010             this.textEl.dom.qtip = text;
6011             this.textEl.dom.removeAttribute('title');
6012         }else{
6013             this.textEl.dom.title = text;
6014         }
6015     },
6016
6017     onTabClick : function(e){
6018         e.preventDefault();
6019         this.tabPanel.activate(this.id);
6020     },
6021
6022     onTabMouseDown : function(e){
6023         e.preventDefault();
6024         this.tabPanel.activate(this.id);
6025     },
6026
6027     getWidth : function(){
6028         return this.inner.getWidth();
6029     },
6030
6031     setWidth : function(width){
6032         var iwidth = width - this.pnode.getPadding("lr");
6033         this.inner.setWidth(iwidth);
6034         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6035         this.pnode.setWidth(width);
6036     },
6037
6038     /**
6039      * Show or hide the tab
6040      * @param {Boolean} hidden True to hide or false to show.
6041      */
6042     setHidden : function(hidden){
6043         this.hidden = hidden;
6044         this.pnode.setStyle("display", hidden ? "none" : "");
6045     },
6046
6047     /**
6048      * Returns true if this tab is "hidden"
6049      * @return {Boolean}
6050      */
6051     isHidden : function(){
6052         return this.hidden;
6053     },
6054
6055     /**
6056      * Returns the text for this tab
6057      * @return {String}
6058      */
6059     getText : function(){
6060         return this.text;
6061     },
6062
6063     autoSize : function(){
6064         //this.el.beginMeasure();
6065         this.textEl.setWidth(1);
6066         /*
6067          *  #2804 [new] Tabs in Roojs
6068          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6069          */
6070         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6071         //this.el.endMeasure();
6072     },
6073
6074     /**
6075      * Sets the text for the tab (Note: this also sets the tooltip text)
6076      * @param {String} text The tab's text and tooltip
6077      */
6078     setText : function(text){
6079         this.text = text;
6080         this.textEl.update(text);
6081         this.setTooltip(text);
6082         if(!this.tabPanel.resizeTabs){
6083             this.autoSize();
6084         }
6085     },
6086     /**
6087      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6088      */
6089     activate : function(){
6090         this.tabPanel.activate(this.id);
6091     },
6092
6093     /**
6094      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6095      */
6096     disable : function(){
6097         if(this.tabPanel.active != this){
6098             this.disabled = true;
6099             this.pnode.addClass("disabled");
6100         }
6101     },
6102
6103     /**
6104      * Enables this TabPanelItem if it was previously disabled.
6105      */
6106     enable : function(){
6107         this.disabled = false;
6108         this.pnode.removeClass("disabled");
6109     },
6110
6111     /**
6112      * Sets the content for this TabPanelItem.
6113      * @param {String} content The content
6114      * @param {Boolean} loadScripts true to look for and load scripts
6115      */
6116     setContent : function(content, loadScripts){
6117         this.bodyEl.update(content, loadScripts);
6118     },
6119
6120     /**
6121      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6122      * @return {Roo.UpdateManager} The UpdateManager
6123      */
6124     getUpdateManager : function(){
6125         return this.bodyEl.getUpdateManager();
6126     },
6127
6128     /**
6129      * Set a URL to be used to load the content for this TabPanelItem.
6130      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6131      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6132      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     setUrl : function(url, params, loadOnce){
6136         if(this.refreshDelegate){
6137             this.un('activate', this.refreshDelegate);
6138         }
6139         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6140         this.on("activate", this.refreshDelegate);
6141         return this.bodyEl.getUpdateManager();
6142     },
6143
6144     /** @private */
6145     _handleRefresh : function(url, params, loadOnce){
6146         if(!loadOnce || !this.loaded){
6147             var updater = this.bodyEl.getUpdateManager();
6148             updater.update(url, params, this._setLoaded.createDelegate(this));
6149         }
6150     },
6151
6152     /**
6153      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6154      *   Will fail silently if the setUrl method has not been called.
6155      *   This does not activate the panel, just updates its content.
6156      */
6157     refresh : function(){
6158         if(this.refreshDelegate){
6159            this.loaded = false;
6160            this.refreshDelegate();
6161         }
6162     },
6163
6164     /** @private */
6165     _setLoaded : function(){
6166         this.loaded = true;
6167     },
6168
6169     /** @private */
6170     closeClick : function(e){
6171         var o = {};
6172         e.stopEvent();
6173         this.fireEvent("beforeclose", this, o);
6174         if(o.cancel !== true){
6175             this.tabPanel.removeTab(this.id);
6176         }
6177     },
6178     /**
6179      * The text displayed in the tooltip for the close icon.
6180      * @type String
6181      */
6182     closeText : "Close this tab"
6183 });
6184
6185 /** @private */
6186 Roo.TabPanel.prototype.createStrip = function(container){
6187     var strip = document.createElement("div");
6188     strip.className = "x-tabs-wrap";
6189     container.appendChild(strip);
6190     return strip;
6191 };
6192 /** @private */
6193 Roo.TabPanel.prototype.createStripList = function(strip){
6194     // div wrapper for retard IE
6195     // returns the "tr" element.
6196     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6197         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6198         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6199     return strip.firstChild.firstChild.firstChild.firstChild;
6200 };
6201 /** @private */
6202 Roo.TabPanel.prototype.createBody = function(container){
6203     var body = document.createElement("div");
6204     Roo.id(body, "tab-body");
6205     Roo.fly(body).addClass("x-tabs-body");
6206     container.appendChild(body);
6207     return body;
6208 };
6209 /** @private */
6210 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6211     var body = Roo.getDom(id);
6212     if(!body){
6213         body = document.createElement("div");
6214         body.id = id;
6215     }
6216     Roo.fly(body).addClass("x-tabs-item-body");
6217     bodyEl.insertBefore(body, bodyEl.firstChild);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6222     var td = document.createElement("td");
6223     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6224     //stripEl.appendChild(td);
6225     if(closable){
6226         td.className = "x-tabs-closable";
6227         if(!this.closeTpl){
6228             this.closeTpl = new Roo.Template(
6229                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6230                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6231                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6232             );
6233         }
6234         var el = this.closeTpl.overwrite(td, {"text": text});
6235         var close = el.getElementsByTagName("div")[0];
6236         var inner = el.getElementsByTagName("em")[0];
6237         return {"el": el, "close": close, "inner": inner};
6238     } else {
6239         if(!this.tabTpl){
6240             this.tabTpl = new Roo.Template(
6241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6243             );
6244         }
6245         var el = this.tabTpl.overwrite(td, {"text": text});
6246         var inner = el.getElementsByTagName("em")[0];
6247         return {"el": el, "inner": inner};
6248     }
6249 };/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.Button
6262  * @extends Roo.util.Observable
6263  * Simple Button class
6264  * @cfg {String} text The button text
6265  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6266  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6267  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6268  * @cfg {Object} scope The scope of the handler
6269  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6270  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6271  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6272  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6273  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6274  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6275    applies if enableToggle = true)
6276  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6277  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6278   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6279  * @constructor
6280  * Create a new button
6281  * @param {Object} config The config object
6282  */
6283 Roo.Button = function(renderTo, config)
6284 {
6285     if (!config) {
6286         config = renderTo;
6287         renderTo = config.renderTo || false;
6288     }
6289     
6290     Roo.apply(this, config);
6291     this.addEvents({
6292         /**
6293              * @event click
6294              * Fires when this button is clicked
6295              * @param {Button} this
6296              * @param {EventObject} e The click event
6297              */
6298             "click" : true,
6299         /**
6300              * @event toggle
6301              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6302              * @param {Button} this
6303              * @param {Boolean} pressed
6304              */
6305             "toggle" : true,
6306         /**
6307              * @event mouseover
6308              * Fires when the mouse hovers over the button
6309              * @param {Button} this
6310              * @param {Event} e The event object
6311              */
6312         'mouseover' : true,
6313         /**
6314              * @event mouseout
6315              * Fires when the mouse exits the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseout': true,
6320          /**
6321              * @event render
6322              * Fires when the button is rendered
6323              * @param {Button} this
6324              */
6325         'render': true
6326     });
6327     if(this.menu){
6328         this.menu = Roo.menu.MenuMgr.get(this.menu);
6329     }
6330     // register listeners first!!  - so render can be captured..
6331     Roo.util.Observable.call(this);
6332     if(renderTo){
6333         this.render(renderTo);
6334     }
6335     
6336   
6337 };
6338
6339 Roo.extend(Roo.Button, Roo.util.Observable, {
6340     /**
6341      * 
6342      */
6343     
6344     /**
6345      * Read-only. True if this button is hidden
6346      * @type Boolean
6347      */
6348     hidden : false,
6349     /**
6350      * Read-only. True if this button is disabled
6351      * @type Boolean
6352      */
6353     disabled : false,
6354     /**
6355      * Read-only. True if this button is pressed (only if enableToggle = true)
6356      * @type Boolean
6357      */
6358     pressed : false,
6359
6360     /**
6361      * @cfg {Number} tabIndex 
6362      * The DOM tabIndex for this button (defaults to undefined)
6363      */
6364     tabIndex : undefined,
6365
6366     /**
6367      * @cfg {Boolean} enableToggle
6368      * True to enable pressed/not pressed toggling (defaults to false)
6369      */
6370     enableToggle: false,
6371     /**
6372      * @cfg {Roo.menu.Menu} menu
6373      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6374      */
6375     menu : undefined,
6376     /**
6377      * @cfg {String} menuAlign
6378      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6379      */
6380     menuAlign : "tl-bl?",
6381
6382     /**
6383      * @cfg {String} iconCls
6384      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6385      */
6386     iconCls : undefined,
6387     /**
6388      * @cfg {String} type
6389      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6390      */
6391     type : 'button',
6392
6393     // private
6394     menuClassTarget: 'tr',
6395
6396     /**
6397      * @cfg {String} clickEvent
6398      * The type of event to map to the button's event handler (defaults to 'click')
6399      */
6400     clickEvent : 'click',
6401
6402     /**
6403      * @cfg {Boolean} handleMouseEvents
6404      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6405      */
6406     handleMouseEvents : true,
6407
6408     /**
6409      * @cfg {String} tooltipType
6410      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6411      */
6412     tooltipType : 'qtip',
6413
6414     /**
6415      * @cfg {String} cls
6416      * A CSS class to apply to the button's main element.
6417      */
6418     
6419     /**
6420      * @cfg {Roo.Template} template (Optional)
6421      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6422      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6423      * require code modifications if required elements (e.g. a button) aren't present.
6424      */
6425
6426     // private
6427     render : function(renderTo){
6428         var btn;
6429         if(this.hideParent){
6430             this.parentEl = Roo.get(renderTo);
6431         }
6432         if(!this.dhconfig){
6433             if(!this.template){
6434                 if(!Roo.Button.buttonTemplate){
6435                     // hideous table template
6436                     Roo.Button.buttonTemplate = new Roo.Template(
6437                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6438                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6439                         "</tr></tbody></table>");
6440                 }
6441                 this.template = Roo.Button.buttonTemplate;
6442             }
6443             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6444             var btnEl = btn.child("button:first");
6445             btnEl.on('focus', this.onFocus, this);
6446             btnEl.on('blur', this.onBlur, this);
6447             if(this.cls){
6448                 btn.addClass(this.cls);
6449             }
6450             if(this.icon){
6451                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6452             }
6453             if(this.iconCls){
6454                 btnEl.addClass(this.iconCls);
6455                 if(!this.cls){
6456                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6457                 }
6458             }
6459             if(this.tabIndex !== undefined){
6460                 btnEl.dom.tabIndex = this.tabIndex;
6461             }
6462             if(this.tooltip){
6463                 if(typeof this.tooltip == 'object'){
6464                     Roo.QuickTips.tips(Roo.apply({
6465                           target: btnEl.id
6466                     }, this.tooltip));
6467                 } else {
6468                     btnEl.dom[this.tooltipType] = this.tooltip;
6469                 }
6470             }
6471         }else{
6472             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6473         }
6474         this.el = btn;
6475         if(this.id){
6476             this.el.dom.id = this.el.id = this.id;
6477         }
6478         if(this.menu){
6479             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6480             this.menu.on("show", this.onMenuShow, this);
6481             this.menu.on("hide", this.onMenuHide, this);
6482         }
6483         btn.addClass("x-btn");
6484         if(Roo.isIE && !Roo.isIE7){
6485             this.autoWidth.defer(1, this);
6486         }else{
6487             this.autoWidth();
6488         }
6489         if(this.handleMouseEvents){
6490             btn.on("mouseover", this.onMouseOver, this);
6491             btn.on("mouseout", this.onMouseOut, this);
6492             btn.on("mousedown", this.onMouseDown, this);
6493         }
6494         btn.on(this.clickEvent, this.onClick, this);
6495         //btn.on("mouseup", this.onMouseUp, this);
6496         if(this.hidden){
6497             this.hide();
6498         }
6499         if(this.disabled){
6500             this.disable();
6501         }
6502         Roo.ButtonToggleMgr.register(this);
6503         if(this.pressed){
6504             this.el.addClass("x-btn-pressed");
6505         }
6506         if(this.repeat){
6507             var repeater = new Roo.util.ClickRepeater(btn,
6508                 typeof this.repeat == "object" ? this.repeat : {}
6509             );
6510             repeater.on("click", this.onClick,  this);
6511         }
6512         
6513         this.fireEvent('render', this);
6514         
6515     },
6516     /**
6517      * Returns the button's underlying element
6518      * @return {Roo.Element} The element
6519      */
6520     getEl : function(){
6521         return this.el;  
6522     },
6523     
6524     /**
6525      * Destroys this Button and removes any listeners.
6526      */
6527     destroy : function(){
6528         Roo.ButtonToggleMgr.unregister(this);
6529         this.el.removeAllListeners();
6530         this.purgeListeners();
6531         this.el.remove();
6532     },
6533
6534     // private
6535     autoWidth : function(){
6536         if(this.el){
6537             this.el.setWidth("auto");
6538             if(Roo.isIE7 && Roo.isStrict){
6539                 var ib = this.el.child('button');
6540                 if(ib && ib.getWidth() > 20){
6541                     ib.clip();
6542                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6543                 }
6544             }
6545             if(this.minWidth){
6546                 if(this.hidden){
6547                     this.el.beginMeasure();
6548                 }
6549                 if(this.el.getWidth() < this.minWidth){
6550                     this.el.setWidth(this.minWidth);
6551                 }
6552                 if(this.hidden){
6553                     this.el.endMeasure();
6554                 }
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Assigns this button's click handler
6561      * @param {Function} handler The function to call when the button is clicked
6562      * @param {Object} scope (optional) Scope for the function passed in
6563      */
6564     setHandler : function(handler, scope){
6565         this.handler = handler;
6566         this.scope = scope;  
6567     },
6568     
6569     /**
6570      * Sets this button's text
6571      * @param {String} text The button text
6572      */
6573     setText : function(text){
6574         this.text = text;
6575         if(this.el){
6576             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6577         }
6578         this.autoWidth();
6579     },
6580     
6581     /**
6582      * Gets the text for this button
6583      * @return {String} The button text
6584      */
6585     getText : function(){
6586         return this.text;  
6587     },
6588     
6589     /**
6590      * Show this button
6591      */
6592     show: function(){
6593         this.hidden = false;
6594         if(this.el){
6595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6596         }
6597     },
6598     
6599     /**
6600      * Hide this button
6601      */
6602     hide: function(){
6603         this.hidden = true;
6604         if(this.el){
6605             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6606         }
6607     },
6608     
6609     /**
6610      * Convenience function for boolean show/hide
6611      * @param {Boolean} visible True to show, false to hide
6612      */
6613     setVisible: function(visible){
6614         if(visible) {
6615             this.show();
6616         }else{
6617             this.hide();
6618         }
6619     },
6620     
6621     /**
6622      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6623      * @param {Boolean} state (optional) Force a particular state
6624      */
6625     toggle : function(state){
6626         state = state === undefined ? !this.pressed : state;
6627         if(state != this.pressed){
6628             if(state){
6629                 this.el.addClass("x-btn-pressed");
6630                 this.pressed = true;
6631                 this.fireEvent("toggle", this, true);
6632             }else{
6633                 this.el.removeClass("x-btn-pressed");
6634                 this.pressed = false;
6635                 this.fireEvent("toggle", this, false);
6636             }
6637             if(this.toggleHandler){
6638                 this.toggleHandler.call(this.scope || this, this, state);
6639             }
6640         }
6641     },
6642     
6643     /**
6644      * Focus the button
6645      */
6646     focus : function(){
6647         this.el.child('button:first').focus();
6648     },
6649     
6650     /**
6651      * Disable this button
6652      */
6653     disable : function(){
6654         if(this.el){
6655             this.el.addClass("x-btn-disabled");
6656         }
6657         this.disabled = true;
6658     },
6659     
6660     /**
6661      * Enable this button
6662      */
6663     enable : function(){
6664         if(this.el){
6665             this.el.removeClass("x-btn-disabled");
6666         }
6667         this.disabled = false;
6668     },
6669
6670     /**
6671      * Convenience function for boolean enable/disable
6672      * @param {Boolean} enabled True to enable, false to disable
6673      */
6674     setDisabled : function(v){
6675         this[v !== true ? "enable" : "disable"]();
6676     },
6677
6678     // private
6679     onClick : function(e)
6680     {
6681         if(e){
6682             e.preventDefault();
6683         }
6684         if(e.button != 0){
6685             return;
6686         }
6687         if(!this.disabled){
6688             if(this.enableToggle){
6689                 this.toggle();
6690             }
6691             if(this.menu && !this.menu.isVisible()){
6692                 this.menu.show(this.el, this.menuAlign);
6693             }
6694             this.fireEvent("click", this, e);
6695             if(this.handler){
6696                 this.el.removeClass("x-btn-over");
6697                 this.handler.call(this.scope || this, this, e);
6698             }
6699         }
6700     },
6701     // private
6702     onMouseOver : function(e){
6703         if(!this.disabled){
6704             this.el.addClass("x-btn-over");
6705             this.fireEvent('mouseover', this, e);
6706         }
6707     },
6708     // private
6709     onMouseOut : function(e){
6710         if(!e.within(this.el,  true)){
6711             this.el.removeClass("x-btn-over");
6712             this.fireEvent('mouseout', this, e);
6713         }
6714     },
6715     // private
6716     onFocus : function(e){
6717         if(!this.disabled){
6718             this.el.addClass("x-btn-focus");
6719         }
6720     },
6721     // private
6722     onBlur : function(e){
6723         this.el.removeClass("x-btn-focus");
6724     },
6725     // private
6726     onMouseDown : function(e){
6727         if(!this.disabled && e.button == 0){
6728             this.el.addClass("x-btn-click");
6729             Roo.get(document).on('mouseup', this.onMouseUp, this);
6730         }
6731     },
6732     // private
6733     onMouseUp : function(e){
6734         if(e.button == 0){
6735             this.el.removeClass("x-btn-click");
6736             Roo.get(document).un('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMenuShow : function(e){
6741         this.el.addClass("x-btn-menu-active");
6742     },
6743     // private
6744     onMenuHide : function(e){
6745         this.el.removeClass("x-btn-menu-active");
6746     }   
6747 });
6748
6749 // Private utility class used by Button
6750 Roo.ButtonToggleMgr = function(){
6751    var groups = {};
6752    
6753    function toggleGroup(btn, state){
6754        if(state){
6755            var g = groups[btn.toggleGroup];
6756            for(var i = 0, l = g.length; i < l; i++){
6757                if(g[i] != btn){
6758                    g[i].toggle(false);
6759                }
6760            }
6761        }
6762    }
6763    
6764    return {
6765        register : function(btn){
6766            if(!btn.toggleGroup){
6767                return;
6768            }
6769            var g = groups[btn.toggleGroup];
6770            if(!g){
6771                g = groups[btn.toggleGroup] = [];
6772            }
6773            g.push(btn);
6774            btn.on("toggle", toggleGroup);
6775        },
6776        
6777        unregister : function(btn){
6778            if(!btn.toggleGroup){
6779                return;
6780            }
6781            var g = groups[btn.toggleGroup];
6782            if(g){
6783                g.remove(btn);
6784                btn.un("toggle", toggleGroup);
6785            }
6786        }
6787    };
6788 }();/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798  
6799 /**
6800  * @class Roo.SplitButton
6801  * @extends Roo.Button
6802  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6803  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6804  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6805  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6806  * @cfg {String} arrowTooltip The title attribute of the arrow
6807  * @constructor
6808  * Create a new menu button
6809  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6810  * @param {Object} config The config object
6811  */
6812 Roo.SplitButton = function(renderTo, config){
6813     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6814     /**
6815      * @event arrowclick
6816      * Fires when this button's arrow is clicked
6817      * @param {SplitButton} this
6818      * @param {EventObject} e The click event
6819      */
6820     this.addEvents({"arrowclick":true});
6821 };
6822
6823 Roo.extend(Roo.SplitButton, Roo.Button, {
6824     render : function(renderTo){
6825         // this is one sweet looking template!
6826         var tpl = new Roo.Template(
6827             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6828             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6829             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6830             "</tbody></table></td><td>",
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6832             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6833             "</tbody></table></td></tr></table>"
6834         );
6835         var btn = tpl.append(renderTo, [this.text, this.type], true);
6836         var btnEl = btn.child("button");
6837         if(this.cls){
6838             btn.addClass(this.cls);
6839         }
6840         if(this.icon){
6841             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6842         }
6843         if(this.iconCls){
6844             btnEl.addClass(this.iconCls);
6845             if(!this.cls){
6846                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6847             }
6848         }
6849         this.el = btn;
6850         if(this.handleMouseEvents){
6851             btn.on("mouseover", this.onMouseOver, this);
6852             btn.on("mouseout", this.onMouseOut, this);
6853             btn.on("mousedown", this.onMouseDown, this);
6854             btn.on("mouseup", this.onMouseUp, this);
6855         }
6856         btn.on(this.clickEvent, this.onClick, this);
6857         if(this.tooltip){
6858             if(typeof this.tooltip == 'object'){
6859                 Roo.QuickTips.tips(Roo.apply({
6860                       target: btnEl.id
6861                 }, this.tooltip));
6862             } else {
6863                 btnEl.dom[this.tooltipType] = this.tooltip;
6864             }
6865         }
6866         if(this.arrowTooltip){
6867             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6868         }
6869         if(this.hidden){
6870             this.hide();
6871         }
6872         if(this.disabled){
6873             this.disable();
6874         }
6875         if(this.pressed){
6876             this.el.addClass("x-btn-pressed");
6877         }
6878         if(Roo.isIE && !Roo.isIE7){
6879             this.autoWidth.defer(1, this);
6880         }else{
6881             this.autoWidth();
6882         }
6883         if(this.menu){
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         this.fireEvent('render', this);
6888     },
6889
6890     // private
6891     autoWidth : function(){
6892         if(this.el){
6893             var tbl = this.el.child("table:first");
6894             var tbl2 = this.el.child("table:last");
6895             this.el.setWidth("auto");
6896             tbl.setWidth("auto");
6897             if(Roo.isIE7 && Roo.isStrict){
6898                 var ib = this.el.child('button:first');
6899                 if(ib && ib.getWidth() > 20){
6900                     ib.clip();
6901                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6902                 }
6903             }
6904             if(this.minWidth){
6905                 if(this.hidden){
6906                     this.el.beginMeasure();
6907                 }
6908                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6909                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6910                 }
6911                 if(this.hidden){
6912                     this.el.endMeasure();
6913                 }
6914             }
6915             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6916         } 
6917     },
6918     /**
6919      * Sets this button's click handler
6920      * @param {Function} handler The function to call when the button is clicked
6921      * @param {Object} scope (optional) Scope for the function passed above
6922      */
6923     setHandler : function(handler, scope){
6924         this.handler = handler;
6925         this.scope = scope;  
6926     },
6927     
6928     /**
6929      * Sets this button's arrow click handler
6930      * @param {Function} handler The function to call when the arrow is clicked
6931      * @param {Object} scope (optional) Scope for the function passed above
6932      */
6933     setArrowHandler : function(handler, scope){
6934         this.arrowHandler = handler;
6935         this.scope = scope;  
6936     },
6937     
6938     /**
6939      * Focus the button
6940      */
6941     focus : function(){
6942         if(this.el){
6943             this.el.child("button:first").focus();
6944         }
6945     },
6946
6947     // private
6948     onClick : function(e){
6949         e.preventDefault();
6950         if(!this.disabled){
6951             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6952                 if(this.menu && !this.menu.isVisible()){
6953                     this.menu.show(this.el, this.menuAlign);
6954                 }
6955                 this.fireEvent("arrowclick", this, e);
6956                 if(this.arrowHandler){
6957                     this.arrowHandler.call(this.scope || this, this, e);
6958                 }
6959             }else{
6960                 this.fireEvent("click", this, e);
6961                 if(this.handler){
6962                     this.handler.call(this.scope || this, this, e);
6963                 }
6964             }
6965         }
6966     },
6967     // private
6968     onMouseDown : function(e){
6969         if(!this.disabled){
6970             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6971         }
6972     },
6973     // private
6974     onMouseUp : function(e){
6975         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6976     }   
6977 });
6978
6979
6980 // backwards compat
6981 Roo.MenuButton = Roo.SplitButton;/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.Toolbar
6994  * @children   Roo.Toolbar.Item Roo.form.Field
6995  * Basic Toolbar class.
6996  * @constructor
6997  * Creates a new Toolbar
6998  * @param {Object} container The config object
6999  */ 
7000 Roo.Toolbar = function(container, buttons, config)
7001 {
7002     /// old consturctor format still supported..
7003     if(container instanceof Array){ // omit the container for later rendering
7004         buttons = container;
7005         config = buttons;
7006         container = null;
7007     }
7008     if (typeof(container) == 'object' && container.xtype) {
7009         config = container;
7010         container = config.container;
7011         buttons = config.buttons || []; // not really - use items!!
7012     }
7013     var xitems = [];
7014     if (config && config.items) {
7015         xitems = config.items;
7016         delete config.items;
7017     }
7018     Roo.apply(this, config);
7019     this.buttons = buttons;
7020     
7021     if(container){
7022         this.render(container);
7023     }
7024     this.xitems = xitems;
7025     Roo.each(xitems, function(b) {
7026         this.add(b);
7027     }, this);
7028     
7029 };
7030
7031 Roo.Toolbar.prototype = {
7032     /**
7033      * @cfg {Array} items
7034      * array of button configs or elements to add (will be converted to a MixedCollection)
7035      */
7036     items: false,
7037     /**
7038      * @cfg {String/HTMLElement/Element} container
7039      * The id or element that will contain the toolbar
7040      */
7041     // private
7042     render : function(ct){
7043         this.el = Roo.get(ct);
7044         if(this.cls){
7045             this.el.addClass(this.cls);
7046         }
7047         // using a table allows for vertical alignment
7048         // 100% width is needed by Safari...
7049         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7050         this.tr = this.el.child("tr", true);
7051         var autoId = 0;
7052         this.items = new Roo.util.MixedCollection(false, function(o){
7053             return o.id || ("item" + (++autoId));
7054         });
7055         if(this.buttons){
7056             this.add.apply(this, this.buttons);
7057             delete this.buttons;
7058         }
7059     },
7060
7061     /**
7062      * Adds element(s) to the toolbar -- this function takes a variable number of 
7063      * arguments of mixed type and adds them to the toolbar.
7064      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7065      * <ul>
7066      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7067      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7068      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7069      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7070      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7071      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7072      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7073      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7074      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7075      * </ul>
7076      * @param {Mixed} arg2
7077      * @param {Mixed} etc.
7078      */
7079     add : function(){
7080         var a = arguments, l = a.length;
7081         for(var i = 0; i < l; i++){
7082             this._add(a[i]);
7083         }
7084     },
7085     // private..
7086     _add : function(el) {
7087         
7088         if (el.xtype) {
7089             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7090         }
7091         
7092         if (el.applyTo){ // some kind of form field
7093             return this.addField(el);
7094         } 
7095         if (el.render){ // some kind of Toolbar.Item
7096             return this.addItem(el);
7097         }
7098         if (typeof el == "string"){ // string
7099             if(el == "separator" || el == "-"){
7100                 return this.addSeparator();
7101             }
7102             if (el == " "){
7103                 return this.addSpacer();
7104             }
7105             if(el == "->"){
7106                 return this.addFill();
7107             }
7108             return this.addText(el);
7109             
7110         }
7111         if(el.tagName){ // element
7112             return this.addElement(el);
7113         }
7114         if(typeof el == "object"){ // must be button config?
7115             return this.addButton(el);
7116         }
7117         // and now what?!?!
7118         return false;
7119         
7120     },
7121     
7122     /**
7123      * Add an Xtype element
7124      * @param {Object} xtype Xtype Object
7125      * @return {Object} created Object
7126      */
7127     addxtype : function(e){
7128         return this.add(e);  
7129     },
7130     
7131     /**
7132      * Returns the Element for this toolbar.
7133      * @return {Roo.Element}
7134      */
7135     getEl : function(){
7136         return this.el;  
7137     },
7138     
7139     /**
7140      * Adds a separator
7141      * @return {Roo.Toolbar.Item} The separator item
7142      */
7143     addSeparator : function(){
7144         return this.addItem(new Roo.Toolbar.Separator());
7145     },
7146
7147     /**
7148      * Adds a spacer element
7149      * @return {Roo.Toolbar.Spacer} The spacer item
7150      */
7151     addSpacer : function(){
7152         return this.addItem(new Roo.Toolbar.Spacer());
7153     },
7154
7155     /**
7156      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7157      * @return {Roo.Toolbar.Fill} The fill item
7158      */
7159     addFill : function(){
7160         return this.addItem(new Roo.Toolbar.Fill());
7161     },
7162
7163     /**
7164      * Adds any standard HTML element to the toolbar
7165      * @param {String/HTMLElement/Element} el The element or id of the element to add
7166      * @return {Roo.Toolbar.Item} The element's item
7167      */
7168     addElement : function(el){
7169         return this.addItem(new Roo.Toolbar.Item(el));
7170     },
7171     /**
7172      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7173      * @type Roo.util.MixedCollection  
7174      */
7175     items : false,
7176      
7177     /**
7178      * Adds any Toolbar.Item or subclass
7179      * @param {Roo.Toolbar.Item} item
7180      * @return {Roo.Toolbar.Item} The item
7181      */
7182     addItem : function(item){
7183         var td = this.nextBlock();
7184         item.render(td);
7185         this.items.add(item);
7186         return item;
7187     },
7188     
7189     /**
7190      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7191      * @param {Object/Array} config A button config or array of configs
7192      * @return {Roo.Toolbar.Button/Array}
7193      */
7194     addButton : function(config){
7195         if(config instanceof Array){
7196             var buttons = [];
7197             for(var i = 0, len = config.length; i < len; i++) {
7198                 buttons.push(this.addButton(config[i]));
7199             }
7200             return buttons;
7201         }
7202         var b = config;
7203         if(!(config instanceof Roo.Toolbar.Button)){
7204             b = config.split ?
7205                 new Roo.Toolbar.SplitButton(config) :
7206                 new Roo.Toolbar.Button(config);
7207         }
7208         var td = this.nextBlock();
7209         b.render(td);
7210         this.items.add(b);
7211         return b;
7212     },
7213     
7214     /**
7215      * Adds text to the toolbar
7216      * @param {String} text The text to add
7217      * @return {Roo.Toolbar.Item} The element's item
7218      */
7219     addText : function(text){
7220         return this.addItem(new Roo.Toolbar.TextItem(text));
7221     },
7222     
7223     /**
7224      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7225      * @param {Number} index The index where the item is to be inserted
7226      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7227      * @return {Roo.Toolbar.Button/Item}
7228      */
7229     insertButton : function(index, item){
7230         if(item instanceof Array){
7231             var buttons = [];
7232             for(var i = 0, len = item.length; i < len; i++) {
7233                buttons.push(this.insertButton(index + i, item[i]));
7234             }
7235             return buttons;
7236         }
7237         if (!(item instanceof Roo.Toolbar.Button)){
7238            item = new Roo.Toolbar.Button(item);
7239         }
7240         var td = document.createElement("td");
7241         this.tr.insertBefore(td, this.tr.childNodes[index]);
7242         item.render(td);
7243         this.items.insert(index, item);
7244         return item;
7245     },
7246     
7247     /**
7248      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7249      * @param {Object} config
7250      * @return {Roo.Toolbar.Item} The element's item
7251      */
7252     addDom : function(config, returnEl){
7253         var td = this.nextBlock();
7254         Roo.DomHelper.overwrite(td, config);
7255         var ti = new Roo.Toolbar.Item(td.firstChild);
7256         ti.render(td);
7257         this.items.add(ti);
7258         return ti;
7259     },
7260
7261     /**
7262      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7263      * @type Roo.util.MixedCollection  
7264      */
7265     fields : false,
7266     
7267     /**
7268      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7269      * Note: the field should not have been rendered yet. For a field that has already been
7270      * rendered, use {@link #addElement}.
7271      * @param {Roo.form.Field} field
7272      * @return {Roo.ToolbarItem}
7273      */
7274      
7275       
7276     addField : function(field) {
7277         if (!this.fields) {
7278             var autoId = 0;
7279             this.fields = new Roo.util.MixedCollection(false, function(o){
7280                 return o.id || ("item" + (++autoId));
7281             });
7282
7283         }
7284         
7285         var td = this.nextBlock();
7286         field.render(td);
7287         var ti = new Roo.Toolbar.Item(td.firstChild);
7288         ti.render(td);
7289         this.items.add(ti);
7290         this.fields.add(field);
7291         return ti;
7292     },
7293     /**
7294      * Hide the toolbar
7295      * @method hide
7296      */
7297      
7298       
7299     hide : function()
7300     {
7301         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7302         this.el.child('div').hide();
7303     },
7304     /**
7305      * Show the toolbar
7306      * @method show
7307      */
7308     show : function()
7309     {
7310         this.el.child('div').show();
7311     },
7312       
7313     // private
7314     nextBlock : function(){
7315         var td = document.createElement("td");
7316         this.tr.appendChild(td);
7317         return td;
7318     },
7319
7320     // private
7321     destroy : function(){
7322         if(this.items){ // rendered?
7323             Roo.destroy.apply(Roo, this.items.items);
7324         }
7325         if(this.fields){ // rendered?
7326             Roo.destroy.apply(Roo, this.fields.items);
7327         }
7328         Roo.Element.uncache(this.el, this.tr);
7329     }
7330 };
7331
7332 /**
7333  * @class Roo.Toolbar.Item
7334  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7335  * @constructor
7336  * Creates a new Item
7337  * @param {HTMLElement} el 
7338  */
7339 Roo.Toolbar.Item = function(el){
7340     var cfg = {};
7341     if (typeof (el.xtype) != 'undefined') {
7342         cfg = el;
7343         el = cfg.el;
7344     }
7345     
7346     this.el = Roo.getDom(el);
7347     this.id = Roo.id(this.el);
7348     this.hidden = false;
7349     
7350     this.addEvents({
7351          /**
7352              * @event render
7353              * Fires when the button is rendered
7354              * @param {Button} this
7355              */
7356         'render': true
7357     });
7358     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7359 };
7360 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7361 //Roo.Toolbar.Item.prototype = {
7362     
7363     /**
7364      * Get this item's HTML Element
7365      * @return {HTMLElement}
7366      */
7367     getEl : function(){
7368        return this.el;  
7369     },
7370
7371     // private
7372     render : function(td){
7373         
7374          this.td = td;
7375         td.appendChild(this.el);
7376         
7377         this.fireEvent('render', this);
7378     },
7379     
7380     /**
7381      * Removes and destroys this item.
7382      */
7383     destroy : function(){
7384         this.td.parentNode.removeChild(this.td);
7385     },
7386     
7387     /**
7388      * Shows this item.
7389      */
7390     show: function(){
7391         this.hidden = false;
7392         this.td.style.display = "";
7393     },
7394     
7395     /**
7396      * Hides this item.
7397      */
7398     hide: function(){
7399         this.hidden = true;
7400         this.td.style.display = "none";
7401     },
7402     
7403     /**
7404      * Convenience function for boolean show/hide.
7405      * @param {Boolean} visible true to show/false to hide
7406      */
7407     setVisible: function(visible){
7408         if(visible) {
7409             this.show();
7410         }else{
7411             this.hide();
7412         }
7413     },
7414     
7415     /**
7416      * Try to focus this item.
7417      */
7418     focus : function(){
7419         Roo.fly(this.el).focus();
7420     },
7421     
7422     /**
7423      * Disables this item.
7424      */
7425     disable : function(){
7426         Roo.fly(this.td).addClass("x-item-disabled");
7427         this.disabled = true;
7428         this.el.disabled = true;
7429     },
7430     
7431     /**
7432      * Enables this item.
7433      */
7434     enable : function(){
7435         Roo.fly(this.td).removeClass("x-item-disabled");
7436         this.disabled = false;
7437         this.el.disabled = false;
7438     }
7439 });
7440
7441
7442 /**
7443  * @class Roo.Toolbar.Separator
7444  * @extends Roo.Toolbar.Item
7445  * A simple toolbar separator class
7446  * @constructor
7447  * Creates a new Separator
7448  */
7449 Roo.Toolbar.Separator = function(cfg){
7450     
7451     var s = document.createElement("span");
7452     s.className = "ytb-sep";
7453     if (cfg) {
7454         cfg.el = s;
7455     }
7456     
7457     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7458 };
7459 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7460     enable:Roo.emptyFn,
7461     disable:Roo.emptyFn,
7462     focus:Roo.emptyFn
7463 });
7464
7465 /**
7466  * @class Roo.Toolbar.Spacer
7467  * @extends Roo.Toolbar.Item
7468  * A simple element that adds extra horizontal space to a toolbar.
7469  * @constructor
7470  * Creates a new Spacer
7471  */
7472 Roo.Toolbar.Spacer = function(cfg){
7473     var s = document.createElement("div");
7474     s.className = "ytb-spacer";
7475     if (cfg) {
7476         cfg.el = s;
7477     }
7478     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7479 };
7480 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7481     enable:Roo.emptyFn,
7482     disable:Roo.emptyFn,
7483     focus:Roo.emptyFn
7484 });
7485
7486 /**
7487  * @class Roo.Toolbar.Fill
7488  * @extends Roo.Toolbar.Spacer
7489  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7490  * @constructor
7491  * Creates a new Spacer
7492  */
7493 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7494     // private
7495     render : function(td){
7496         td.style.width = '100%';
7497         Roo.Toolbar.Fill.superclass.render.call(this, td);
7498     }
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.TextItem
7503  * @extends Roo.Toolbar.Item
7504  * A simple class that renders text directly into a toolbar.
7505  * @constructor
7506  * Creates a new TextItem
7507  * @cfg {string} text 
7508  */
7509 Roo.Toolbar.TextItem = function(cfg){
7510     var  text = cfg || "";
7511     if (typeof(cfg) == 'object') {
7512         text = cfg.text || "";
7513     }  else {
7514         cfg = null;
7515     }
7516     var s = document.createElement("span");
7517     s.className = "ytb-text";
7518     s.innerHTML = text;
7519     if (cfg) {
7520         cfg.el  = s;
7521     }
7522     
7523     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7524 };
7525 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7526     
7527      
7528     enable:Roo.emptyFn,
7529     disable:Roo.emptyFn,
7530     focus:Roo.emptyFn
7531 });
7532
7533 /**
7534  * @class Roo.Toolbar.Button
7535  * @extends Roo.Button
7536  * A button that renders into a toolbar.
7537  * @constructor
7538  * Creates a new Button
7539  * @param {Object} config A standard {@link Roo.Button} config object
7540  */
7541 Roo.Toolbar.Button = function(config){
7542     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7543 };
7544 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7545 {
7546     
7547     
7548     render : function(td){
7549         this.td = td;
7550         Roo.Toolbar.Button.superclass.render.call(this, td);
7551     },
7552     
7553     /**
7554      * Removes and destroys this button
7555      */
7556     destroy : function(){
7557         Roo.Toolbar.Button.superclass.destroy.call(this);
7558         this.td.parentNode.removeChild(this.td);
7559     },
7560     
7561     /**
7562      * Shows this button
7563      */
7564     show: function(){
7565         this.hidden = false;
7566         this.td.style.display = "";
7567     },
7568     
7569     /**
7570      * Hides this button
7571      */
7572     hide: function(){
7573         this.hidden = true;
7574         this.td.style.display = "none";
7575     },
7576
7577     /**
7578      * Disables this item
7579      */
7580     disable : function(){
7581         Roo.fly(this.td).addClass("x-item-disabled");
7582         this.disabled = true;
7583     },
7584
7585     /**
7586      * Enables this item
7587      */
7588     enable : function(){
7589         Roo.fly(this.td).removeClass("x-item-disabled");
7590         this.disabled = false;
7591     }
7592 });
7593 // backwards compat
7594 Roo.ToolbarButton = Roo.Toolbar.Button;
7595
7596 /**
7597  * @class Roo.Toolbar.SplitButton
7598  * @extends Roo.SplitButton
7599  * A menu button that renders into a toolbar.
7600  * @constructor
7601  * Creates a new SplitButton
7602  * @param {Object} config A standard {@link Roo.SplitButton} config object
7603  */
7604 Roo.Toolbar.SplitButton = function(config){
7605     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7606 };
7607 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7608     render : function(td){
7609         this.td = td;
7610         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7611     },
7612     
7613     /**
7614      * Removes and destroys this button
7615      */
7616     destroy : function(){
7617         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7618         this.td.parentNode.removeChild(this.td);
7619     },
7620     
7621     /**
7622      * Shows this button
7623      */
7624     show: function(){
7625         this.hidden = false;
7626         this.td.style.display = "";
7627     },
7628     
7629     /**
7630      * Hides this button
7631      */
7632     hide: function(){
7633         this.hidden = true;
7634         this.td.style.display = "none";
7635     }
7636 });
7637
7638 // backwards compat
7639 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7640  * Based on:
7641  * Ext JS Library 1.1.1
7642  * Copyright(c) 2006-2007, Ext JS, LLC.
7643  *
7644  * Originally Released Under LGPL - original licence link has changed is not relivant.
7645  *
7646  * Fork - LGPL
7647  * <script type="text/javascript">
7648  */
7649  
7650 /**
7651  * @class Roo.PagingToolbar
7652  * @extends Roo.Toolbar
7653  * @children   Roo.Toolbar.Item Roo.form.Field
7654  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7655  * @constructor
7656  * Create a new PagingToolbar
7657  * @param {Object} config The config object
7658  */
7659 Roo.PagingToolbar = function(el, ds, config)
7660 {
7661     // old args format still supported... - xtype is prefered..
7662     if (typeof(el) == 'object' && el.xtype) {
7663         // created from xtype...
7664         config = el;
7665         ds = el.dataSource;
7666         el = config.container;
7667     }
7668     var items = [];
7669     if (config.items) {
7670         items = config.items;
7671         config.items = [];
7672     }
7673     
7674     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7675     this.ds = ds;
7676     this.cursor = 0;
7677     this.renderButtons(this.el);
7678     this.bind(ds);
7679     
7680     // supprot items array.
7681    
7682     Roo.each(items, function(e) {
7683         this.add(Roo.factory(e));
7684     },this);
7685     
7686 };
7687
7688 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7689     /**
7690      * @cfg {Roo.data.Store} dataSource
7691      * The underlying data store providing the paged data
7692      */
7693     /**
7694      * @cfg {String/HTMLElement/Element} container
7695      * container The id or element that will contain the toolbar
7696      */
7697     /**
7698      * @cfg {Boolean} displayInfo
7699      * True to display the displayMsg (defaults to false)
7700      */
7701     /**
7702      * @cfg {Number} pageSize
7703      * The number of records to display per page (defaults to 20)
7704      */
7705     pageSize: 20,
7706     /**
7707      * @cfg {String} displayMsg
7708      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7709      */
7710     displayMsg : 'Displaying {0} - {1} of {2}',
7711     /**
7712      * @cfg {String} emptyMsg
7713      * The message to display when no records are found (defaults to "No data to display")
7714      */
7715     emptyMsg : 'No data to display',
7716     /**
7717      * Customizable piece of the default paging text (defaults to "Page")
7718      * @type String
7719      */
7720     beforePageText : "Page",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "of %0")
7723      * @type String
7724      */
7725     afterPageText : "of {0}",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "First Page")
7728      * @type String
7729      */
7730     firstText : "First Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Previous Page")
7733      * @type String
7734      */
7735     prevText : "Previous Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Next Page")
7738      * @type String
7739      */
7740     nextText : "Next Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Last Page")
7743      * @type String
7744      */
7745     lastText : "Last Page",
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Refresh")
7748      * @type String
7749      */
7750     refreshText : "Refresh",
7751
7752     // private
7753     renderButtons : function(el){
7754         Roo.PagingToolbar.superclass.render.call(this, el);
7755         this.first = this.addButton({
7756             tooltip: this.firstText,
7757             cls: "x-btn-icon x-grid-page-first",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["first"])
7760         });
7761         this.prev = this.addButton({
7762             tooltip: this.prevText,
7763             cls: "x-btn-icon x-grid-page-prev",
7764             disabled: true,
7765             handler: this.onClick.createDelegate(this, ["prev"])
7766         });
7767         //this.addSeparator();
7768         this.add(this.beforePageText);
7769         this.field = Roo.get(this.addDom({
7770            tag: "input",
7771            type: "text",
7772            size: "3",
7773            value: "1",
7774            cls: "x-grid-page-number"
7775         }).el);
7776         this.field.on("keydown", this.onPagingKeydown, this);
7777         this.field.on("focus", function(){this.dom.select();});
7778         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7779         this.field.setHeight(18);
7780         //this.addSeparator();
7781         this.next = this.addButton({
7782             tooltip: this.nextText,
7783             cls: "x-btn-icon x-grid-page-next",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["next"])
7786         });
7787         this.last = this.addButton({
7788             tooltip: this.lastText,
7789             cls: "x-btn-icon x-grid-page-last",
7790             disabled: true,
7791             handler: this.onClick.createDelegate(this, ["last"])
7792         });
7793         //this.addSeparator();
7794         this.loading = this.addButton({
7795             tooltip: this.refreshText,
7796             cls: "x-btn-icon x-grid-loading",
7797             handler: this.onClick.createDelegate(this, ["refresh"])
7798         });
7799
7800         if(this.displayInfo){
7801             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7802         }
7803     },
7804
7805     // private
7806     updateInfo : function(){
7807         if(this.displayEl){
7808             var count = this.ds.getCount();
7809             var msg = count == 0 ?
7810                 this.emptyMsg :
7811                 String.format(
7812                     this.displayMsg,
7813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7814                 );
7815             this.displayEl.update(msg);
7816         }
7817     },
7818
7819     // private
7820     onLoad : function(ds, r, o){
7821        this.cursor = o.params ? o.params.start : 0;
7822        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7823
7824        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7825        this.field.dom.value = ap;
7826        this.first.setDisabled(ap == 1);
7827        this.prev.setDisabled(ap == 1);
7828        this.next.setDisabled(ap == ps);
7829        this.last.setDisabled(ap == ps);
7830        this.loading.enable();
7831        this.updateInfo();
7832     },
7833
7834     // private
7835     getPageData : function(){
7836         var total = this.ds.getTotalCount();
7837         return {
7838             total : total,
7839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7841         };
7842     },
7843
7844     // private
7845     onLoadError : function(){
7846         this.loading.enable();
7847     },
7848
7849     // private
7850     onPagingKeydown : function(e){
7851         var k = e.getKey();
7852         var d = this.getPageData();
7853         if(k == e.RETURN){
7854             var v = this.field.dom.value, pageNum;
7855             if(!v || isNaN(pageNum = parseInt(v, 10))){
7856                 this.field.dom.value = d.activePage;
7857                 return;
7858             }
7859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7861             e.stopEvent();
7862         }
7863         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))
7864         {
7865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7866           this.field.dom.value = pageNum;
7867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7868           e.stopEvent();
7869         }
7870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7871         {
7872           var v = this.field.dom.value, pageNum; 
7873           var increment = (e.shiftKey) ? 10 : 1;
7874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7875             increment *= -1;
7876           }
7877           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7878             this.field.dom.value = d.activePage;
7879             return;
7880           }
7881           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7882           {
7883             this.field.dom.value = parseInt(v, 10) + increment;
7884             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7885             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7886           }
7887           e.stopEvent();
7888         }
7889     },
7890
7891     // private
7892     beforeLoad : function(){
7893         if(this.loading){
7894             this.loading.disable();
7895         }
7896     },
7897
7898     // private
7899     onClick : function(which){
7900         var ds = this.ds;
7901         switch(which){
7902             case "first":
7903                 ds.load({params:{start: 0, limit: this.pageSize}});
7904             break;
7905             case "prev":
7906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7907             break;
7908             case "next":
7909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7910             break;
7911             case "last":
7912                 var total = ds.getTotalCount();
7913                 var extra = total % this.pageSize;
7914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7916             break;
7917             case "refresh":
7918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7919             break;
7920         }
7921     },
7922
7923     /**
7924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7925      * @param {Roo.data.Store} store The data store to unbind
7926      */
7927     unbind : function(ds){
7928         ds.un("beforeload", this.beforeLoad, this);
7929         ds.un("load", this.onLoad, this);
7930         ds.un("loadexception", this.onLoadError, this);
7931         ds.un("remove", this.updateInfo, this);
7932         ds.un("add", this.updateInfo, this);
7933         this.ds = undefined;
7934     },
7935
7936     /**
7937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7938      * @param {Roo.data.Store} store The data store to bind
7939      */
7940     bind : function(ds){
7941         ds.on("beforeload", this.beforeLoad, this);
7942         ds.on("load", this.onLoad, this);
7943         ds.on("loadexception", this.onLoadError, this);
7944         ds.on("remove", this.updateInfo, this);
7945         ds.on("add", this.updateInfo, this);
7946         this.ds = ds;
7947     }
7948 });/*
7949  * Based on:
7950  * Ext JS Library 1.1.1
7951  * Copyright(c) 2006-2007, Ext JS, LLC.
7952  *
7953  * Originally Released Under LGPL - original licence link has changed is not relivant.
7954  *
7955  * Fork - LGPL
7956  * <script type="text/javascript">
7957  */
7958
7959 /**
7960  * @class Roo.Resizable
7961  * @extends Roo.util.Observable
7962  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7963  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7964  * 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
7965  * the element will be wrapped for you automatically.</p>
7966  * <p>Here is the list of valid resize handles:</p>
7967  * <pre>
7968 Value   Description
7969 ------  -------------------
7970  'n'     north
7971  's'     south
7972  'e'     east
7973  'w'     west
7974  'nw'    northwest
7975  'sw'    southwest
7976  'se'    southeast
7977  'ne'    northeast
7978  'hd'    horizontal drag
7979  'all'   all
7980 </pre>
7981  * <p>Here's an example showing the creation of a typical Resizable:</p>
7982  * <pre><code>
7983 var resizer = new Roo.Resizable("element-id", {
7984     handles: 'all',
7985     minWidth: 200,
7986     minHeight: 100,
7987     maxWidth: 500,
7988     maxHeight: 400,
7989     pinned: true
7990 });
7991 resizer.on("resize", myHandler);
7992 </code></pre>
7993  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7994  * resizer.east.setDisplayed(false);</p>
7995  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7996  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7997  * resize operation's new size (defaults to [0, 0])
7998  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7999  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8000  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8001  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8002  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8003  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8004  * @cfg {Number} width The width of the element in pixels (defaults to null)
8005  * @cfg {Number} height The height of the element in pixels (defaults to null)
8006  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8007  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8008  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8009  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8010  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8011  * in favor of the handles config option (defaults to false)
8012  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8013  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8014  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8015  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8016  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8017  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8018  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8019  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8020  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8021  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8022  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8023  * @constructor
8024  * Create a new resizable component
8025  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8026  * @param {Object} config configuration options
8027   */
8028 Roo.Resizable = function(el, config)
8029 {
8030     this.el = Roo.get(el);
8031
8032     if(config && config.wrap){
8033         config.resizeChild = this.el;
8034         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8035         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8036         this.el.setStyle("overflow", "hidden");
8037         this.el.setPositioning(config.resizeChild.getPositioning());
8038         config.resizeChild.clearPositioning();
8039         if(!config.width || !config.height){
8040             var csize = config.resizeChild.getSize();
8041             this.el.setSize(csize.width, csize.height);
8042         }
8043         if(config.pinned && !config.adjustments){
8044             config.adjustments = "auto";
8045         }
8046     }
8047
8048     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8049     this.proxy.unselectable();
8050     this.proxy.enableDisplayMode('block');
8051
8052     Roo.apply(this, config);
8053
8054     if(this.pinned){
8055         this.disableTrackOver = true;
8056         this.el.addClass("x-resizable-pinned");
8057     }
8058     // if the element isn't positioned, make it relative
8059     var position = this.el.getStyle("position");
8060     if(position != "absolute" && position != "fixed"){
8061         this.el.setStyle("position", "relative");
8062     }
8063     if(!this.handles){ // no handles passed, must be legacy style
8064         this.handles = 's,e,se';
8065         if(this.multiDirectional){
8066             this.handles += ',n,w';
8067         }
8068     }
8069     if(this.handles == "all"){
8070         this.handles = "n s e w ne nw se sw";
8071     }
8072     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8073     var ps = Roo.Resizable.positions;
8074     for(var i = 0, len = hs.length; i < len; i++){
8075         if(hs[i] && ps[hs[i]]){
8076             var pos = ps[hs[i]];
8077             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8078         }
8079     }
8080     // legacy
8081     this.corner = this.southeast;
8082     
8083     // updateBox = the box can move..
8084     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8085         this.updateBox = true;
8086     }
8087
8088     this.activeHandle = null;
8089
8090     if(this.resizeChild){
8091         if(typeof this.resizeChild == "boolean"){
8092             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8093         }else{
8094             this.resizeChild = Roo.get(this.resizeChild, true);
8095         }
8096     }
8097     
8098     if(this.adjustments == "auto"){
8099         var rc = this.resizeChild;
8100         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8101         if(rc && (hw || hn)){
8102             rc.position("relative");
8103             rc.setLeft(hw ? hw.el.getWidth() : 0);
8104             rc.setTop(hn ? hn.el.getHeight() : 0);
8105         }
8106         this.adjustments = [
8107             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8108             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8109         ];
8110     }
8111
8112     if(this.draggable){
8113         this.dd = this.dynamic ?
8114             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8115         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8116     }
8117
8118     // public events
8119     this.addEvents({
8120         /**
8121          * @event beforeresize
8122          * Fired before resize is allowed. Set enabled to false to cancel resize.
8123          * @param {Roo.Resizable} this
8124          * @param {Roo.EventObject} e The mousedown event
8125          */
8126         "beforeresize" : true,
8127         /**
8128          * @event resizing
8129          * Fired a resizing.
8130          * @param {Roo.Resizable} this
8131          * @param {Number} x The new x position
8132          * @param {Number} y The new y position
8133          * @param {Number} w The new w width
8134          * @param {Number} h The new h hight
8135          * @param {Roo.EventObject} e The mouseup event
8136          */
8137         "resizing" : true,
8138         /**
8139          * @event resize
8140          * Fired after a resize.
8141          * @param {Roo.Resizable} this
8142          * @param {Number} width The new width
8143          * @param {Number} height The new height
8144          * @param {Roo.EventObject} e The mouseup event
8145          */
8146         "resize" : true
8147     });
8148
8149     if(this.width !== null && this.height !== null){
8150         this.resizeTo(this.width, this.height);
8151     }else{
8152         this.updateChildSize();
8153     }
8154     if(Roo.isIE){
8155         this.el.dom.style.zoom = 1;
8156     }
8157     Roo.Resizable.superclass.constructor.call(this);
8158 };
8159
8160 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8161         resizeChild : false,
8162         adjustments : [0, 0],
8163         minWidth : 5,
8164         minHeight : 5,
8165         maxWidth : 10000,
8166         maxHeight : 10000,
8167         enabled : true,
8168         animate : false,
8169         duration : .35,
8170         dynamic : false,
8171         handles : false,
8172         multiDirectional : false,
8173         disableTrackOver : false,
8174         easing : 'easeOutStrong',
8175         widthIncrement : 0,
8176         heightIncrement : 0,
8177         pinned : false,
8178         width : null,
8179         height : null,
8180         preserveRatio : false,
8181         transparent: false,
8182         minX: 0,
8183         minY: 0,
8184         draggable: false,
8185
8186         /**
8187          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8188          */
8189         constrainTo: undefined,
8190         /**
8191          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8192          */
8193         resizeRegion: undefined,
8194
8195
8196     /**
8197      * Perform a manual resize
8198      * @param {Number} width
8199      * @param {Number} height
8200      */
8201     resizeTo : function(width, height){
8202         this.el.setSize(width, height);
8203         this.updateChildSize();
8204         this.fireEvent("resize", this, width, height, null);
8205     },
8206
8207     // private
8208     startSizing : function(e, handle){
8209         this.fireEvent("beforeresize", this, e);
8210         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8211
8212             if(!this.overlay){
8213                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8214                 this.overlay.unselectable();
8215                 this.overlay.enableDisplayMode("block");
8216                 this.overlay.on("mousemove", this.onMouseMove, this);
8217                 this.overlay.on("mouseup", this.onMouseUp, this);
8218             }
8219             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8220
8221             this.resizing = true;
8222             this.startBox = this.el.getBox();
8223             this.startPoint = e.getXY();
8224             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8225                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8226
8227             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8228             this.overlay.show();
8229
8230             if(this.constrainTo) {
8231                 var ct = Roo.get(this.constrainTo);
8232                 this.resizeRegion = ct.getRegion().adjust(
8233                     ct.getFrameWidth('t'),
8234                     ct.getFrameWidth('l'),
8235                     -ct.getFrameWidth('b'),
8236                     -ct.getFrameWidth('r')
8237                 );
8238             }
8239
8240             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8241             this.proxy.show();
8242             this.proxy.setBox(this.startBox);
8243             if(!this.dynamic){
8244                 this.proxy.setStyle('visibility', 'visible');
8245             }
8246         }
8247     },
8248
8249     // private
8250     onMouseDown : function(handle, e){
8251         if(this.enabled){
8252             e.stopEvent();
8253             this.activeHandle = handle;
8254             this.startSizing(e, handle);
8255         }
8256     },
8257
8258     // private
8259     onMouseUp : function(e){
8260         var size = this.resizeElement();
8261         this.resizing = false;
8262         this.handleOut();
8263         this.overlay.hide();
8264         this.proxy.hide();
8265         this.fireEvent("resize", this, size.width, size.height, e);
8266     },
8267
8268     // private
8269     updateChildSize : function(){
8270         
8271         if(this.resizeChild){
8272             var el = this.el;
8273             var child = this.resizeChild;
8274             var adj = this.adjustments;
8275             if(el.dom.offsetWidth){
8276                 var b = el.getSize(true);
8277                 child.setSize(b.width+adj[0], b.height+adj[1]);
8278             }
8279             // Second call here for IE
8280             // The first call enables instant resizing and
8281             // the second call corrects scroll bars if they
8282             // exist
8283             if(Roo.isIE){
8284                 setTimeout(function(){
8285                     if(el.dom.offsetWidth){
8286                         var b = el.getSize(true);
8287                         child.setSize(b.width+adj[0], b.height+adj[1]);
8288                     }
8289                 }, 10);
8290             }
8291         }
8292     },
8293
8294     // private
8295     snap : function(value, inc, min){
8296         if(!inc || !value) {
8297             return value;
8298         }
8299         var newValue = value;
8300         var m = value % inc;
8301         if(m > 0){
8302             if(m > (inc/2)){
8303                 newValue = value + (inc-m);
8304             }else{
8305                 newValue = value - m;
8306             }
8307         }
8308         return Math.max(min, newValue);
8309     },
8310
8311     // private
8312     resizeElement : function(){
8313         var box = this.proxy.getBox();
8314         if(this.updateBox){
8315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8316         }else{
8317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8318         }
8319         this.updateChildSize();
8320         if(!this.dynamic){
8321             this.proxy.hide();
8322         }
8323         return box;
8324     },
8325
8326     // private
8327     constrain : function(v, diff, m, mx){
8328         if(v - diff < m){
8329             diff = v - m;
8330         }else if(v - diff > mx){
8331             diff = mx - v;
8332         }
8333         return diff;
8334     },
8335
8336     // private
8337     onMouseMove : function(e){
8338         
8339         if(this.enabled){
8340             try{// try catch so if something goes wrong the user doesn't get hung
8341
8342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8343                 return;
8344             }
8345
8346             //var curXY = this.startPoint;
8347             var curSize = this.curSize || this.startBox;
8348             var x = this.startBox.x, y = this.startBox.y;
8349             var ox = x, oy = y;
8350             var w = curSize.width, h = curSize.height;
8351             var ow = w, oh = h;
8352             var mw = this.minWidth, mh = this.minHeight;
8353             var mxw = this.maxWidth, mxh = this.maxHeight;
8354             var wi = this.widthIncrement;
8355             var hi = this.heightIncrement;
8356
8357             var eventXY = e.getXY();
8358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8360
8361             var pos = this.activeHandle.position;
8362
8363             switch(pos){
8364                 case "east":
8365                     w += diffX;
8366                     w = Math.min(Math.max(mw, w), mxw);
8367                     break;
8368              
8369                 case "south":
8370                     h += diffY;
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "southeast":
8374                     w += diffX;
8375                     h += diffY;
8376                     w = Math.min(Math.max(mw, w), mxw);
8377                     h = Math.min(Math.max(mh, h), mxh);
8378                     break;
8379                 case "north":
8380                     diffY = this.constrain(h, diffY, mh, mxh);
8381                     y += diffY;
8382                     h -= diffY;
8383                     break;
8384                 case "hdrag":
8385                     
8386                     if (wi) {
8387                         var adiffX = Math.abs(diffX);
8388                         var sub = (adiffX % wi); // how much 
8389                         if (sub > (wi/2)) { // far enough to snap
8390                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8391                         } else {
8392                             // remove difference.. 
8393                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8394                         }
8395                     }
8396                     x += diffX;
8397                     x = Math.max(this.minX, x);
8398                     break;
8399                 case "west":
8400                     diffX = this.constrain(w, diffX, mw, mxw);
8401                     x += diffX;
8402                     w -= diffX;
8403                     break;
8404                 case "northeast":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     break;
8411                 case "northwest":
8412                     diffX = this.constrain(w, diffX, mw, mxw);
8413                     diffY = this.constrain(h, diffY, mh, mxh);
8414                     y += diffY;
8415                     h -= diffY;
8416                     x += diffX;
8417                     w -= diffX;
8418                     break;
8419                case "southwest":
8420                     diffX = this.constrain(w, diffX, mw, mxw);
8421                     h += diffY;
8422                     h = Math.min(Math.max(mh, h), mxh);
8423                     x += diffX;
8424                     w -= diffX;
8425                     break;
8426             }
8427
8428             var sw = this.snap(w, wi, mw);
8429             var sh = this.snap(h, hi, mh);
8430             if(sw != w || sh != h){
8431                 switch(pos){
8432                     case "northeast":
8433                         y -= sh - h;
8434                     break;
8435                     case "north":
8436                         y -= sh - h;
8437                         break;
8438                     case "southwest":
8439                         x -= sw - w;
8440                     break;
8441                     case "west":
8442                         x -= sw - w;
8443                         break;
8444                     case "northwest":
8445                         x -= sw - w;
8446                         y -= sh - h;
8447                     break;
8448                 }
8449                 w = sw;
8450                 h = sh;
8451             }
8452
8453             if(this.preserveRatio){
8454                 switch(pos){
8455                     case "southeast":
8456                     case "east":
8457                         h = oh * (w/ow);
8458                         h = Math.min(Math.max(mh, h), mxh);
8459                         w = ow * (h/oh);
8460                        break;
8461                     case "south":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                         break;
8466                     case "northeast":
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                     break;
8471                     case "north":
8472                         var tw = w;
8473                         w = ow * (h/oh);
8474                         w = Math.min(Math.max(mw, w), mxw);
8475                         h = oh * (w/ow);
8476                         x += (tw - w) / 2;
8477                         break;
8478                     case "southwest":
8479                         h = oh * (w/ow);
8480                         h = Math.min(Math.max(mh, h), mxh);
8481                         var tw = w;
8482                         w = ow * (h/oh);
8483                         x += tw - w;
8484                         break;
8485                     case "west":
8486                         var th = h;
8487                         h = oh * (w/ow);
8488                         h = Math.min(Math.max(mh, h), mxh);
8489                         y += (th - h) / 2;
8490                         var tw = w;
8491                         w = ow * (h/oh);
8492                         x += tw - w;
8493                        break;
8494                     case "northwest":
8495                         var tw = w;
8496                         var th = h;
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                         y += th - h;
8501                         x += tw - w;
8502                        break;
8503
8504                 }
8505             }
8506             if (pos == 'hdrag') {
8507                 w = ow;
8508             }
8509             this.proxy.setBounds(x, y, w, h);
8510             if(this.dynamic){
8511                 this.resizeElement();
8512             }
8513             }catch(e){}
8514         }
8515         this.fireEvent("resizing", this, x, y, w, h, e);
8516     },
8517
8518     // private
8519     handleOver : function(){
8520         if(this.enabled){
8521             this.el.addClass("x-resizable-over");
8522         }
8523     },
8524
8525     // private
8526     handleOut : function(){
8527         if(!this.resizing){
8528             this.el.removeClass("x-resizable-over");
8529         }
8530     },
8531
8532     /**
8533      * Returns the element this component is bound to.
8534      * @return {Roo.Element}
8535      */
8536     getEl : function(){
8537         return this.el;
8538     },
8539
8540     /**
8541      * Returns the resizeChild element (or null).
8542      * @return {Roo.Element}
8543      */
8544     getResizeChild : function(){
8545         return this.resizeChild;
8546     },
8547     groupHandler : function()
8548     {
8549         
8550     },
8551     /**
8552      * Destroys this resizable. If the element was wrapped and
8553      * removeEl is not true then the element remains.
8554      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8555      */
8556     destroy : function(removeEl){
8557         this.proxy.remove();
8558         if(this.overlay){
8559             this.overlay.removeAllListeners();
8560             this.overlay.remove();
8561         }
8562         var ps = Roo.Resizable.positions;
8563         for(var k in ps){
8564             if(typeof ps[k] != "function" && this[ps[k]]){
8565                 var h = this[ps[k]];
8566                 h.el.removeAllListeners();
8567                 h.el.remove();
8568             }
8569         }
8570         if(removeEl){
8571             this.el.update("");
8572             this.el.remove();
8573         }
8574     }
8575 });
8576
8577 // private
8578 // hash to map config positions to true positions
8579 Roo.Resizable.positions = {
8580     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8581     hd: "hdrag"
8582 };
8583
8584 // private
8585 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8586     if(!this.tpl){
8587         // only initialize the template if resizable is used
8588         var tpl = Roo.DomHelper.createTemplate(
8589             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8590         );
8591         tpl.compile();
8592         Roo.Resizable.Handle.prototype.tpl = tpl;
8593     }
8594     this.position = pos;
8595     this.rz = rz;
8596     // show north drag fro topdra
8597     var handlepos = pos == 'hdrag' ? 'north' : pos;
8598     
8599     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8600     if (pos == 'hdrag') {
8601         this.el.setStyle('cursor', 'pointer');
8602     }
8603     this.el.unselectable();
8604     if(transparent){
8605         this.el.setOpacity(0);
8606     }
8607     this.el.on("mousedown", this.onMouseDown, this);
8608     if(!disableTrackOver){
8609         this.el.on("mouseover", this.onMouseOver, this);
8610         this.el.on("mouseout", this.onMouseOut, this);
8611     }
8612 };
8613
8614 // private
8615 Roo.Resizable.Handle.prototype = {
8616     afterResize : function(rz){
8617         Roo.log('after?');
8618         // do nothing
8619     },
8620     // private
8621     onMouseDown : function(e){
8622         this.rz.onMouseDown(this, e);
8623     },
8624     // private
8625     onMouseOver : function(e){
8626         this.rz.handleOver(this, e);
8627     },
8628     // private
8629     onMouseOut : function(e){
8630         this.rz.handleOut(this, e);
8631     }
8632 };/*
8633  * Based on:
8634  * Ext JS Library 1.1.1
8635  * Copyright(c) 2006-2007, Ext JS, LLC.
8636  *
8637  * Originally Released Under LGPL - original licence link has changed is not relivant.
8638  *
8639  * Fork - LGPL
8640  * <script type="text/javascript">
8641  */
8642
8643 /**
8644  * @class Roo.Editor
8645  * @extends Roo.Component
8646  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8647  * @constructor
8648  * Create a new Editor
8649  * @param {Roo.form.Field} field The Field object (or descendant)
8650  * @param {Object} config The config object
8651  */
8652 Roo.Editor = function(field, config){
8653     Roo.Editor.superclass.constructor.call(this, config);
8654     this.field = field;
8655     this.addEvents({
8656         /**
8657              * @event beforestartedit
8658              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8659              * false from the handler of this event.
8660              * @param {Editor} this
8661              * @param {Roo.Element} boundEl The underlying element bound to this editor
8662              * @param {Mixed} value The field value being set
8663              */
8664         "beforestartedit" : true,
8665         /**
8666              * @event startedit
8667              * Fires when this editor is displayed
8668              * @param {Roo.Element} boundEl The underlying element bound to this editor
8669              * @param {Mixed} value The starting field value
8670              */
8671         "startedit" : true,
8672         /**
8673              * @event beforecomplete
8674              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8675              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8676              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8677              * event will not fire since no edit actually occurred.
8678              * @param {Editor} this
8679              * @param {Mixed} value The current field value
8680              * @param {Mixed} startValue The original field value
8681              */
8682         "beforecomplete" : true,
8683         /**
8684              * @event complete
8685              * Fires after editing is complete and any changed value has been written to the underlying field.
8686              * @param {Editor} this
8687              * @param {Mixed} value The current field value
8688              * @param {Mixed} startValue The original field value
8689              */
8690         "complete" : true,
8691         /**
8692          * @event specialkey
8693          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8694          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8695          * @param {Roo.form.Field} this
8696          * @param {Roo.EventObject} e The event object
8697          */
8698         "specialkey" : true
8699     });
8700 };
8701
8702 Roo.extend(Roo.Editor, Roo.Component, {
8703     /**
8704      * @cfg {Boolean/String} autosize
8705      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8706      * or "height" to adopt the height only (defaults to false)
8707      */
8708     /**
8709      * @cfg {Boolean} revertInvalid
8710      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8711      * validation fails (defaults to true)
8712      */
8713     /**
8714      * @cfg {Boolean} ignoreNoChange
8715      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8716      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8717      * will never be ignored.
8718      */
8719     /**
8720      * @cfg {Boolean} hideEl
8721      * False to keep the bound element visible while the editor is displayed (defaults to true)
8722      */
8723     /**
8724      * @cfg {Mixed} value
8725      * The data value of the underlying field (defaults to "")
8726      */
8727     value : "",
8728     /**
8729      * @cfg {String} alignment
8730      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8731      */
8732     alignment: "c-c?",
8733     /**
8734      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8735      * for bottom-right shadow (defaults to "frame")
8736      */
8737     shadow : "frame",
8738     /**
8739      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8740      */
8741     constrain : false,
8742     /**
8743      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8744      */
8745     completeOnEnter : false,
8746     /**
8747      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8748      */
8749     cancelOnEsc : false,
8750     /**
8751      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8752      */
8753     updateEl : false,
8754
8755     // private
8756     onRender : function(ct, position){
8757         this.el = new Roo.Layer({
8758             shadow: this.shadow,
8759             cls: "x-editor",
8760             parentEl : ct,
8761             shim : this.shim,
8762             shadowOffset:4,
8763             id: this.id,
8764             constrain: this.constrain
8765         });
8766         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8767         if(this.field.msgTarget != 'title'){
8768             this.field.msgTarget = 'qtip';
8769         }
8770         this.field.render(this.el);
8771         if(Roo.isGecko){
8772             this.field.el.dom.setAttribute('autocomplete', 'off');
8773         }
8774         this.field.on("specialkey", this.onSpecialKey, this);
8775         if(this.swallowKeys){
8776             this.field.el.swallowEvent(['keydown','keypress']);
8777         }
8778         this.field.show();
8779         this.field.on("blur", this.onBlur, this);
8780         if(this.field.grow){
8781             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8782         }
8783     },
8784
8785     onSpecialKey : function(field, e)
8786     {
8787         //Roo.log('editor onSpecialKey');
8788         if(this.completeOnEnter && e.getKey() == e.ENTER){
8789             e.stopEvent();
8790             this.completeEdit();
8791             return;
8792         }
8793         // do not fire special key otherwise it might hide close the editor...
8794         if(e.getKey() == e.ENTER){    
8795             return;
8796         }
8797         if(this.cancelOnEsc && e.getKey() == e.ESC){
8798             this.cancelEdit();
8799             return;
8800         } 
8801         this.fireEvent('specialkey', field, e);
8802     
8803     },
8804
8805     /**
8806      * Starts the editing process and shows the editor.
8807      * @param {String/HTMLElement/Element} el The element to edit
8808      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8809       * to the innerHTML of el.
8810      */
8811     startEdit : function(el, value){
8812         if(this.editing){
8813             this.completeEdit();
8814         }
8815         this.boundEl = Roo.get(el);
8816         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8817         if(!this.rendered){
8818             this.render(this.parentEl || document.body);
8819         }
8820         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8821             return;
8822         }
8823         this.startValue = v;
8824         this.field.setValue(v);
8825         if(this.autoSize){
8826             var sz = this.boundEl.getSize();
8827             switch(this.autoSize){
8828                 case "width":
8829                 this.setSize(sz.width,  "");
8830                 break;
8831                 case "height":
8832                 this.setSize("",  sz.height);
8833                 break;
8834                 default:
8835                 this.setSize(sz.width,  sz.height);
8836             }
8837         }
8838         this.el.alignTo(this.boundEl, this.alignment);
8839         this.editing = true;
8840         if(Roo.QuickTips){
8841             Roo.QuickTips.disable();
8842         }
8843         this.show();
8844     },
8845
8846     /**
8847      * Sets the height and width of this editor.
8848      * @param {Number} width The new width
8849      * @param {Number} height The new height
8850      */
8851     setSize : function(w, h){
8852         this.field.setSize(w, h);
8853         if(this.el){
8854             this.el.sync();
8855         }
8856     },
8857
8858     /**
8859      * Realigns the editor to the bound field based on the current alignment config value.
8860      */
8861     realign : function(){
8862         this.el.alignTo(this.boundEl, this.alignment);
8863     },
8864
8865     /**
8866      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8867      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8868      */
8869     completeEdit : function(remainVisible){
8870         if(!this.editing){
8871             return;
8872         }
8873         var v = this.getValue();
8874         if(this.revertInvalid !== false && !this.field.isValid()){
8875             v = this.startValue;
8876             this.cancelEdit(true);
8877         }
8878         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8879             this.editing = false;
8880             this.hide();
8881             return;
8882         }
8883         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8884             this.editing = false;
8885             if(this.updateEl && this.boundEl){
8886                 this.boundEl.update(v);
8887             }
8888             if(remainVisible !== true){
8889                 this.hide();
8890             }
8891             this.fireEvent("complete", this, v, this.startValue);
8892         }
8893     },
8894
8895     // private
8896     onShow : function(){
8897         this.el.show();
8898         if(this.hideEl !== false){
8899             this.boundEl.hide();
8900         }
8901         this.field.show();
8902         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8903             this.fixIEFocus = true;
8904             this.deferredFocus.defer(50, this);
8905         }else{
8906             this.field.focus();
8907         }
8908         this.fireEvent("startedit", this.boundEl, this.startValue);
8909     },
8910
8911     deferredFocus : function(){
8912         if(this.editing){
8913             this.field.focus();
8914         }
8915     },
8916
8917     /**
8918      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8919      * reverted to the original starting value.
8920      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8921      * cancel (defaults to false)
8922      */
8923     cancelEdit : function(remainVisible){
8924         if(this.editing){
8925             this.setValue(this.startValue);
8926             if(remainVisible !== true){
8927                 this.hide();
8928             }
8929         }
8930     },
8931
8932     // private
8933     onBlur : function(){
8934         if(this.allowBlur !== true && this.editing){
8935             this.completeEdit();
8936         }
8937     },
8938
8939     // private
8940     onHide : function(){
8941         if(this.editing){
8942             this.completeEdit();
8943             return;
8944         }
8945         this.field.blur();
8946         if(this.field.collapse){
8947             this.field.collapse();
8948         }
8949         this.el.hide();
8950         if(this.hideEl !== false){
8951             this.boundEl.show();
8952         }
8953         if(Roo.QuickTips){
8954             Roo.QuickTips.enable();
8955         }
8956     },
8957
8958     /**
8959      * Sets the data value of the editor
8960      * @param {Mixed} value Any valid value supported by the underlying field
8961      */
8962     setValue : function(v){
8963         this.field.setValue(v);
8964     },
8965
8966     /**
8967      * Gets the data value of the editor
8968      * @return {Mixed} The data value
8969      */
8970     getValue : function(){
8971         return this.field.getValue();
8972     }
8973 });/*
8974  * Based on:
8975  * Ext JS Library 1.1.1
8976  * Copyright(c) 2006-2007, Ext JS, LLC.
8977  *
8978  * Originally Released Under LGPL - original licence link has changed is not relivant.
8979  *
8980  * Fork - LGPL
8981  * <script type="text/javascript">
8982  */
8983  
8984 /**
8985  * @class Roo.BasicDialog
8986  * @extends Roo.util.Observable
8987  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8988  * <pre><code>
8989 var dlg = new Roo.BasicDialog("my-dlg", {
8990     height: 200,
8991     width: 300,
8992     minHeight: 100,
8993     minWidth: 150,
8994     modal: true,
8995     proxyDrag: true,
8996     shadow: true
8997 });
8998 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8999 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9000 dlg.addButton('Cancel', dlg.hide, dlg);
9001 dlg.show();
9002 </code></pre>
9003   <b>A Dialog should always be a direct child of the body element.</b>
9004  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9005  * @cfg {String} title Default text to display in the title bar (defaults to null)
9006  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9007  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9008  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9009  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9010  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9011  * (defaults to null with no animation)
9012  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9013  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9014  * property for valid values (defaults to 'all')
9015  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9016  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9017  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9018  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9019  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9020  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9021  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9022  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9023  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9024  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9025  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9026  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9027  * draggable = true (defaults to false)
9028  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9029  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9030  * shadow (defaults to false)
9031  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9032  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9033  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9034  * @cfg {Array} buttons Array of buttons
9035  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9036  * @constructor
9037  * Create a new BasicDialog.
9038  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9039  * @param {Object} config Configuration options
9040  */
9041 Roo.BasicDialog = function(el, config){
9042     this.el = Roo.get(el);
9043     var dh = Roo.DomHelper;
9044     if(!this.el && config && config.autoCreate){
9045         if(typeof config.autoCreate == "object"){
9046             if(!config.autoCreate.id){
9047                 config.autoCreate.id = el;
9048             }
9049             this.el = dh.append(document.body,
9050                         config.autoCreate, true);
9051         }else{
9052             this.el = dh.append(document.body,
9053                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9054         }
9055     }
9056     el = this.el;
9057     el.setDisplayed(true);
9058     el.hide = this.hideAction;
9059     this.id = el.id;
9060     el.addClass("x-dlg");
9061
9062     Roo.apply(this, config);
9063
9064     this.proxy = el.createProxy("x-dlg-proxy");
9065     this.proxy.hide = this.hideAction;
9066     this.proxy.setOpacity(.5);
9067     this.proxy.hide();
9068
9069     if(config.width){
9070         el.setWidth(config.width);
9071     }
9072     if(config.height){
9073         el.setHeight(config.height);
9074     }
9075     this.size = el.getSize();
9076     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9077         this.xy = [config.x,config.y];
9078     }else{
9079         this.xy = el.getCenterXY(true);
9080     }
9081     /** The header element @type Roo.Element */
9082     this.header = el.child("> .x-dlg-hd");
9083     /** The body element @type Roo.Element */
9084     this.body = el.child("> .x-dlg-bd");
9085     /** The footer element @type Roo.Element */
9086     this.footer = el.child("> .x-dlg-ft");
9087
9088     if(!this.header){
9089         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9090     }
9091     if(!this.body){
9092         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9093     }
9094
9095     this.header.unselectable();
9096     if(this.title){
9097         this.header.update(this.title);
9098     }
9099     // this element allows the dialog to be focused for keyboard event
9100     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9101     this.focusEl.swallowEvent("click", true);
9102
9103     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9104
9105     // wrap the body and footer for special rendering
9106     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9107     if(this.footer){
9108         this.bwrap.dom.appendChild(this.footer.dom);
9109     }
9110
9111     this.bg = this.el.createChild({
9112         tag: "div", cls:"x-dlg-bg",
9113         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9114     });
9115     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9116
9117
9118     if(this.autoScroll !== false && !this.autoTabs){
9119         this.body.setStyle("overflow", "auto");
9120     }
9121
9122     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9123
9124     if(this.closable !== false){
9125         this.el.addClass("x-dlg-closable");
9126         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9127         this.close.on("click", this.closeClick, this);
9128         this.close.addClassOnOver("x-dlg-close-over");
9129     }
9130     if(this.collapsible !== false){
9131         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9132         this.collapseBtn.on("click", this.collapseClick, this);
9133         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9134         this.header.on("dblclick", this.collapseClick, this);
9135     }
9136     if(this.resizable !== false){
9137         this.el.addClass("x-dlg-resizable");
9138         this.resizer = new Roo.Resizable(el, {
9139             minWidth: this.minWidth || 80,
9140             minHeight:this.minHeight || 80,
9141             handles: this.resizeHandles || "all",
9142             pinned: true
9143         });
9144         this.resizer.on("beforeresize", this.beforeResize, this);
9145         this.resizer.on("resize", this.onResize, this);
9146     }
9147     if(this.draggable !== false){
9148         el.addClass("x-dlg-draggable");
9149         if (!this.proxyDrag) {
9150             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9151         }
9152         else {
9153             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9154         }
9155         dd.setHandleElId(this.header.id);
9156         dd.endDrag = this.endMove.createDelegate(this);
9157         dd.startDrag = this.startMove.createDelegate(this);
9158         dd.onDrag = this.onDrag.createDelegate(this);
9159         dd.scroll = false;
9160         this.dd = dd;
9161     }
9162     if(this.modal){
9163         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9164         this.mask.enableDisplayMode("block");
9165         this.mask.hide();
9166         this.el.addClass("x-dlg-modal");
9167     }
9168     if(this.shadow){
9169         this.shadow = new Roo.Shadow({
9170             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9171             offset : this.shadowOffset
9172         });
9173     }else{
9174         this.shadowOffset = 0;
9175     }
9176     if(Roo.useShims && this.shim !== false){
9177         this.shim = this.el.createShim();
9178         this.shim.hide = this.hideAction;
9179         this.shim.hide();
9180     }else{
9181         this.shim = false;
9182     }
9183     if(this.autoTabs){
9184         this.initTabs();
9185     }
9186     if (this.buttons) { 
9187         var bts= this.buttons;
9188         this.buttons = [];
9189         Roo.each(bts, function(b) {
9190             this.addButton(b);
9191         }, this);
9192     }
9193     
9194     
9195     this.addEvents({
9196         /**
9197          * @event keydown
9198          * Fires when a key is pressed
9199          * @param {Roo.BasicDialog} this
9200          * @param {Roo.EventObject} e
9201          */
9202         "keydown" : true,
9203         /**
9204          * @event move
9205          * Fires when this dialog is moved by the user.
9206          * @param {Roo.BasicDialog} this
9207          * @param {Number} x The new page X
9208          * @param {Number} y The new page Y
9209          */
9210         "move" : true,
9211         /**
9212          * @event resize
9213          * Fires when this dialog is resized by the user.
9214          * @param {Roo.BasicDialog} this
9215          * @param {Number} width The new width
9216          * @param {Number} height The new height
9217          */
9218         "resize" : true,
9219         /**
9220          * @event beforehide
9221          * Fires before this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "beforehide" : true,
9225         /**
9226          * @event hide
9227          * Fires when this dialog is hidden.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "hide" : true,
9231         /**
9232          * @event beforeshow
9233          * Fires before this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "beforeshow" : true,
9237         /**
9238          * @event show
9239          * Fires when this dialog is shown.
9240          * @param {Roo.BasicDialog} this
9241          */
9242         "show" : true
9243     });
9244     el.on("keydown", this.onKeyDown, this);
9245     el.on("mousedown", this.toFront, this);
9246     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9247     this.el.hide();
9248     Roo.DialogManager.register(this);
9249     Roo.BasicDialog.superclass.constructor.call(this);
9250 };
9251
9252 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9253     shadowOffset: Roo.isIE ? 6 : 5,
9254     minHeight: 80,
9255     minWidth: 200,
9256     minButtonWidth: 75,
9257     defaultButton: null,
9258     buttonAlign: "right",
9259     tabTag: 'div',
9260     firstShow: true,
9261
9262     /**
9263      * Sets the dialog title text
9264      * @param {String} text The title text to display
9265      * @return {Roo.BasicDialog} this
9266      */
9267     setTitle : function(text){
9268         this.header.update(text);
9269         return this;
9270     },
9271
9272     // private
9273     closeClick : function(){
9274         this.hide();
9275     },
9276
9277     // private
9278     collapseClick : function(){
9279         this[this.collapsed ? "expand" : "collapse"]();
9280     },
9281
9282     /**
9283      * Collapses the dialog to its minimized state (only the title bar is visible).
9284      * Equivalent to the user clicking the collapse dialog button.
9285      */
9286     collapse : function(){
9287         if(!this.collapsed){
9288             this.collapsed = true;
9289             this.el.addClass("x-dlg-collapsed");
9290             this.restoreHeight = this.el.getHeight();
9291             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9292         }
9293     },
9294
9295     /**
9296      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9297      * clicking the expand dialog button.
9298      */
9299     expand : function(){
9300         if(this.collapsed){
9301             this.collapsed = false;
9302             this.el.removeClass("x-dlg-collapsed");
9303             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9304         }
9305     },
9306
9307     /**
9308      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9309      * @return {Roo.TabPanel} The tabs component
9310      */
9311     initTabs : function(){
9312         var tabs = this.getTabs();
9313         while(tabs.getTab(0)){
9314             tabs.removeTab(0);
9315         }
9316         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9317             var dom = el.dom;
9318             tabs.addTab(Roo.id(dom), dom.title);
9319             dom.title = "";
9320         });
9321         tabs.activate(0);
9322         return tabs;
9323     },
9324
9325     // private
9326     beforeResize : function(){
9327         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9328     },
9329
9330     // private
9331     onResize : function(){
9332         this.refreshSize();
9333         this.syncBodyHeight();
9334         this.adjustAssets();
9335         this.focus();
9336         this.fireEvent("resize", this, this.size.width, this.size.height);
9337     },
9338
9339     // private
9340     onKeyDown : function(e){
9341         if(this.isVisible()){
9342             this.fireEvent("keydown", this, e);
9343         }
9344     },
9345
9346     /**
9347      * Resizes the dialog.
9348      * @param {Number} width
9349      * @param {Number} height
9350      * @return {Roo.BasicDialog} this
9351      */
9352     resizeTo : function(width, height){
9353         this.el.setSize(width, height);
9354         this.size = {width: width, height: height};
9355         this.syncBodyHeight();
9356         if(this.fixedcenter){
9357             this.center();
9358         }
9359         if(this.isVisible()){
9360             this.constrainXY();
9361             this.adjustAssets();
9362         }
9363         this.fireEvent("resize", this, width, height);
9364         return this;
9365     },
9366
9367
9368     /**
9369      * Resizes the dialog to fit the specified content size.
9370      * @param {Number} width
9371      * @param {Number} height
9372      * @return {Roo.BasicDialog} this
9373      */
9374     setContentSize : function(w, h){
9375         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9376         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9377         //if(!this.el.isBorderBox()){
9378             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9379             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9380         //}
9381         if(this.tabs){
9382             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9383             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9384         }
9385         this.resizeTo(w, h);
9386         return this;
9387     },
9388
9389     /**
9390      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9391      * executed in response to a particular key being pressed while the dialog is active.
9392      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9393      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394      * @param {Function} fn The function to call
9395      * @param {Object} scope (optional) The scope of the function
9396      * @return {Roo.BasicDialog} this
9397      */
9398     addKeyListener : function(key, fn, scope){
9399         var keyCode, shift, ctrl, alt;
9400         if(typeof key == "object" && !(key instanceof Array)){
9401             keyCode = key["key"];
9402             shift = key["shift"];
9403             ctrl = key["ctrl"];
9404             alt = key["alt"];
9405         }else{
9406             keyCode = key;
9407         }
9408         var handler = function(dlg, e){
9409             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9410                 var k = e.getKey();
9411                 if(keyCode instanceof Array){
9412                     for(var i = 0, len = keyCode.length; i < len; i++){
9413                         if(keyCode[i] == k){
9414                           fn.call(scope || window, dlg, k, e);
9415                           return;
9416                         }
9417                     }
9418                 }else{
9419                     if(k == keyCode){
9420                         fn.call(scope || window, dlg, k, e);
9421                     }
9422                 }
9423             }
9424         };
9425         this.on("keydown", handler);
9426         return this;
9427     },
9428
9429     /**
9430      * Returns the TabPanel component (creates it if it doesn't exist).
9431      * Note: If you wish to simply check for the existence of tabs without creating them,
9432      * check for a null 'tabs' property.
9433      * @return {Roo.TabPanel} The tabs component
9434      */
9435     getTabs : function(){
9436         if(!this.tabs){
9437             this.el.addClass("x-dlg-auto-tabs");
9438             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9439             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9440         }
9441         return this.tabs;
9442     },
9443
9444     /**
9445      * Adds a button to the footer section of the dialog.
9446      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9447      * object or a valid Roo.DomHelper element config
9448      * @param {Function} handler The function called when the button is clicked
9449      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9450      * @return {Roo.Button} The new button
9451      */
9452     addButton : function(config, handler, scope){
9453         var dh = Roo.DomHelper;
9454         if(!this.footer){
9455             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9456         }
9457         if(!this.btnContainer){
9458             var tb = this.footer.createChild({
9459
9460                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9462             }, null, true);
9463             this.btnContainer = tb.firstChild.firstChild.firstChild;
9464         }
9465         var bconfig = {
9466             handler: handler,
9467             scope: scope,
9468             minWidth: this.minButtonWidth,
9469             hideParent:true
9470         };
9471         if(typeof config == "string"){
9472             bconfig.text = config;
9473         }else{
9474             if(config.tag){
9475                 bconfig.dhconfig = config;
9476             }else{
9477                 Roo.apply(bconfig, config);
9478             }
9479         }
9480         var fc = false;
9481         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9482             bconfig.position = Math.max(0, bconfig.position);
9483             fc = this.btnContainer.childNodes[bconfig.position];
9484         }
9485          
9486         var btn = new Roo.Button(
9487             fc ? 
9488                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9489                 : this.btnContainer.appendChild(document.createElement("td")),
9490             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9491             bconfig
9492         );
9493         this.syncBodyHeight();
9494         if(!this.buttons){
9495             /**
9496              * Array of all the buttons that have been added to this dialog via addButton
9497              * @type Array
9498              */
9499             this.buttons = [];
9500         }
9501         this.buttons.push(btn);
9502         return btn;
9503     },
9504
9505     /**
9506      * Sets the default button to be focused when the dialog is displayed.
9507      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9508      * @return {Roo.BasicDialog} this
9509      */
9510     setDefaultButton : function(btn){
9511         this.defaultButton = btn;
9512         return this;
9513     },
9514
9515     // private
9516     getHeaderFooterHeight : function(safe){
9517         var height = 0;
9518         if(this.header){
9519            height += this.header.getHeight();
9520         }
9521         if(this.footer){
9522            var fm = this.footer.getMargins();
9523             height += (this.footer.getHeight()+fm.top+fm.bottom);
9524         }
9525         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9526         height += this.centerBg.getPadding("tb");
9527         return height;
9528     },
9529
9530     // private
9531     syncBodyHeight : function()
9532     {
9533         var bd = this.body, // the text
9534             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9535             bw = this.bwrap;
9536         var height = this.size.height - this.getHeaderFooterHeight(false);
9537         bd.setHeight(height-bd.getMargins("tb"));
9538         var hh = this.header.getHeight();
9539         var h = this.size.height-hh;
9540         cb.setHeight(h);
9541         
9542         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9543         bw.setHeight(h-cb.getPadding("tb"));
9544         
9545         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9546         bd.setWidth(bw.getWidth(true));
9547         if(this.tabs){
9548             this.tabs.syncHeight();
9549             if(Roo.isIE){
9550                 this.tabs.el.repaint();
9551             }
9552         }
9553     },
9554
9555     /**
9556      * Restores the previous state of the dialog if Roo.state is configured.
9557      * @return {Roo.BasicDialog} this
9558      */
9559     restoreState : function(){
9560         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9561         if(box && box.width){
9562             this.xy = [box.x, box.y];
9563             this.resizeTo(box.width, box.height);
9564         }
9565         return this;
9566     },
9567
9568     // private
9569     beforeShow : function(){
9570         this.expand();
9571         if(this.fixedcenter){
9572             this.xy = this.el.getCenterXY(true);
9573         }
9574         if(this.modal){
9575             Roo.get(document.body).addClass("x-body-masked");
9576             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9577             this.mask.show();
9578         }
9579         this.constrainXY();
9580     },
9581
9582     // private
9583     animShow : function(){
9584         var b = Roo.get(this.animateTarget).getBox();
9585         this.proxy.setSize(b.width, b.height);
9586         this.proxy.setLocation(b.x, b.y);
9587         this.proxy.show();
9588         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9589                     true, .35, this.showEl.createDelegate(this));
9590     },
9591
9592     /**
9593      * Shows the dialog.
9594      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9595      * @return {Roo.BasicDialog} this
9596      */
9597     show : function(animateTarget){
9598         if (this.fireEvent("beforeshow", this) === false){
9599             return;
9600         }
9601         if(this.syncHeightBeforeShow){
9602             this.syncBodyHeight();
9603         }else if(this.firstShow){
9604             this.firstShow = false;
9605             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9606         }
9607         this.animateTarget = animateTarget || this.animateTarget;
9608         if(!this.el.isVisible()){
9609             this.beforeShow();
9610             if(this.animateTarget && Roo.get(this.animateTarget)){
9611                 this.animShow();
9612             }else{
9613                 this.showEl();
9614             }
9615         }
9616         return this;
9617     },
9618
9619     // private
9620     showEl : function(){
9621         this.proxy.hide();
9622         this.el.setXY(this.xy);
9623         this.el.show();
9624         this.adjustAssets(true);
9625         this.toFront();
9626         this.focus();
9627         // IE peekaboo bug - fix found by Dave Fenwick
9628         if(Roo.isIE){
9629             this.el.repaint();
9630         }
9631         this.fireEvent("show", this);
9632     },
9633
9634     /**
9635      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9636      * dialog itself will receive focus.
9637      */
9638     focus : function(){
9639         if(this.defaultButton){
9640             this.defaultButton.focus();
9641         }else{
9642             this.focusEl.focus();
9643         }
9644     },
9645
9646     // private
9647     constrainXY : function(){
9648         if(this.constraintoviewport !== false){
9649             if(!this.viewSize){
9650                 if(this.container){
9651                     var s = this.container.getSize();
9652                     this.viewSize = [s.width, s.height];
9653                 }else{
9654                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9655                 }
9656             }
9657             var s = Roo.get(this.container||document).getScroll();
9658
9659             var x = this.xy[0], y = this.xy[1];
9660             var w = this.size.width, h = this.size.height;
9661             var vw = this.viewSize[0], vh = this.viewSize[1];
9662             // only move it if it needs it
9663             var moved = false;
9664             // first validate right/bottom
9665             if(x + w > vw+s.left){
9666                 x = vw - w;
9667                 moved = true;
9668             }
9669             if(y + h > vh+s.top){
9670                 y = vh - h;
9671                 moved = true;
9672             }
9673             // then make sure top/left isn't negative
9674             if(x < s.left){
9675                 x = s.left;
9676                 moved = true;
9677             }
9678             if(y < s.top){
9679                 y = s.top;
9680                 moved = true;
9681             }
9682             if(moved){
9683                 // cache xy
9684                 this.xy = [x, y];
9685                 if(this.isVisible()){
9686                     this.el.setLocation(x, y);
9687                     this.adjustAssets();
9688                 }
9689             }
9690         }
9691     },
9692
9693     // private
9694     onDrag : function(){
9695         if(!this.proxyDrag){
9696             this.xy = this.el.getXY();
9697             this.adjustAssets();
9698         }
9699     },
9700
9701     // private
9702     adjustAssets : function(doShow){
9703         var x = this.xy[0], y = this.xy[1];
9704         var w = this.size.width, h = this.size.height;
9705         if(doShow === true){
9706             if(this.shadow){
9707                 this.shadow.show(this.el);
9708             }
9709             if(this.shim){
9710                 this.shim.show();
9711             }
9712         }
9713         if(this.shadow && this.shadow.isVisible()){
9714             this.shadow.show(this.el);
9715         }
9716         if(this.shim && this.shim.isVisible()){
9717             this.shim.setBounds(x, y, w, h);
9718         }
9719     },
9720
9721     // private
9722     adjustViewport : function(w, h){
9723         if(!w || !h){
9724             w = Roo.lib.Dom.getViewWidth();
9725             h = Roo.lib.Dom.getViewHeight();
9726         }
9727         // cache the size
9728         this.viewSize = [w, h];
9729         if(this.modal && this.mask.isVisible()){
9730             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9731             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9732         }
9733         if(this.isVisible()){
9734             this.constrainXY();
9735         }
9736     },
9737
9738     /**
9739      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9740      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9741      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9742      */
9743     destroy : function(removeEl){
9744         if(this.isVisible()){
9745             this.animateTarget = null;
9746             this.hide();
9747         }
9748         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9749         if(this.tabs){
9750             this.tabs.destroy(removeEl);
9751         }
9752         Roo.destroy(
9753              this.shim,
9754              this.proxy,
9755              this.resizer,
9756              this.close,
9757              this.mask
9758         );
9759         if(this.dd){
9760             this.dd.unreg();
9761         }
9762         if(this.buttons){
9763            for(var i = 0, len = this.buttons.length; i < len; i++){
9764                this.buttons[i].destroy();
9765            }
9766         }
9767         this.el.removeAllListeners();
9768         if(removeEl === true){
9769             this.el.update("");
9770             this.el.remove();
9771         }
9772         Roo.DialogManager.unregister(this);
9773     },
9774
9775     // private
9776     startMove : function(){
9777         if(this.proxyDrag){
9778             this.proxy.show();
9779         }
9780         if(this.constraintoviewport !== false){
9781             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9782         }
9783     },
9784
9785     // private
9786     endMove : function(){
9787         if(!this.proxyDrag){
9788             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9789         }else{
9790             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9791             this.proxy.hide();
9792         }
9793         this.refreshSize();
9794         this.adjustAssets();
9795         this.focus();
9796         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9797     },
9798
9799     /**
9800      * Brings this dialog to the front of any other visible dialogs
9801      * @return {Roo.BasicDialog} this
9802      */
9803     toFront : function(){
9804         Roo.DialogManager.bringToFront(this);
9805         return this;
9806     },
9807
9808     /**
9809      * Sends this dialog to the back (under) of any other visible dialogs
9810      * @return {Roo.BasicDialog} this
9811      */
9812     toBack : function(){
9813         Roo.DialogManager.sendToBack(this);
9814         return this;
9815     },
9816
9817     /**
9818      * Centers this dialog in the viewport
9819      * @return {Roo.BasicDialog} this
9820      */
9821     center : function(){
9822         var xy = this.el.getCenterXY(true);
9823         this.moveTo(xy[0], xy[1]);
9824         return this;
9825     },
9826
9827     /**
9828      * Moves the dialog's top-left corner to the specified point
9829      * @param {Number} x
9830      * @param {Number} y
9831      * @return {Roo.BasicDialog} this
9832      */
9833     moveTo : function(x, y){
9834         this.xy = [x,y];
9835         if(this.isVisible()){
9836             this.el.setXY(this.xy);
9837             this.adjustAssets();
9838         }
9839         return this;
9840     },
9841
9842     /**
9843      * Aligns the dialog to the specified element
9844      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9845      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9846      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9847      * @return {Roo.BasicDialog} this
9848      */
9849     alignTo : function(element, position, offsets){
9850         this.xy = this.el.getAlignToXY(element, position, offsets);
9851         if(this.isVisible()){
9852             this.el.setXY(this.xy);
9853             this.adjustAssets();
9854         }
9855         return this;
9856     },
9857
9858     /**
9859      * Anchors an element to another element and realigns it when the window is resized.
9860      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9861      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9862      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9863      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9864      * is a number, it is used as the buffer delay (defaults to 50ms).
9865      * @return {Roo.BasicDialog} this
9866      */
9867     anchorTo : function(el, alignment, offsets, monitorScroll){
9868         var action = function(){
9869             this.alignTo(el, alignment, offsets);
9870         };
9871         Roo.EventManager.onWindowResize(action, this);
9872         var tm = typeof monitorScroll;
9873         if(tm != 'undefined'){
9874             Roo.EventManager.on(window, 'scroll', action, this,
9875                 {buffer: tm == 'number' ? monitorScroll : 50});
9876         }
9877         action.call(this);
9878         return this;
9879     },
9880
9881     /**
9882      * Returns true if the dialog is visible
9883      * @return {Boolean}
9884      */
9885     isVisible : function(){
9886         return this.el.isVisible();
9887     },
9888
9889     // private
9890     animHide : function(callback){
9891         var b = Roo.get(this.animateTarget).getBox();
9892         this.proxy.show();
9893         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9894         this.el.hide();
9895         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9896                     this.hideEl.createDelegate(this, [callback]));
9897     },
9898
9899     /**
9900      * Hides the dialog.
9901      * @param {Function} callback (optional) Function to call when the dialog is hidden
9902      * @return {Roo.BasicDialog} this
9903      */
9904     hide : function(callback){
9905         if (this.fireEvent("beforehide", this) === false){
9906             return;
9907         }
9908         if(this.shadow){
9909             this.shadow.hide();
9910         }
9911         if(this.shim) {
9912           this.shim.hide();
9913         }
9914         // sometimes animateTarget seems to get set.. causing problems...
9915         // this just double checks..
9916         if(this.animateTarget && Roo.get(this.animateTarget)) {
9917            this.animHide(callback);
9918         }else{
9919             this.el.hide();
9920             this.hideEl(callback);
9921         }
9922         return this;
9923     },
9924
9925     // private
9926     hideEl : function(callback){
9927         this.proxy.hide();
9928         if(this.modal){
9929             this.mask.hide();
9930             Roo.get(document.body).removeClass("x-body-masked");
9931         }
9932         this.fireEvent("hide", this);
9933         if(typeof callback == "function"){
9934             callback();
9935         }
9936     },
9937
9938     // private
9939     hideAction : function(){
9940         this.setLeft("-10000px");
9941         this.setTop("-10000px");
9942         this.setStyle("visibility", "hidden");
9943     },
9944
9945     // private
9946     refreshSize : function(){
9947         this.size = this.el.getSize();
9948         this.xy = this.el.getXY();
9949         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9950     },
9951
9952     // private
9953     // z-index is managed by the DialogManager and may be overwritten at any time
9954     setZIndex : function(index){
9955         if(this.modal){
9956             this.mask.setStyle("z-index", index);
9957         }
9958         if(this.shim){
9959             this.shim.setStyle("z-index", ++index);
9960         }
9961         if(this.shadow){
9962             this.shadow.setZIndex(++index);
9963         }
9964         this.el.setStyle("z-index", ++index);
9965         if(this.proxy){
9966             this.proxy.setStyle("z-index", ++index);
9967         }
9968         if(this.resizer){
9969             this.resizer.proxy.setStyle("z-index", ++index);
9970         }
9971
9972         this.lastZIndex = index;
9973     },
9974
9975     /**
9976      * Returns the element for this dialog
9977      * @return {Roo.Element} The underlying dialog Element
9978      */
9979     getEl : function(){
9980         return this.el;
9981     }
9982 });
9983
9984 /**
9985  * @class Roo.DialogManager
9986  * Provides global access to BasicDialogs that have been created and
9987  * support for z-indexing (layering) multiple open dialogs.
9988  */
9989 Roo.DialogManager = function(){
9990     var list = {};
9991     var accessList = [];
9992     var front = null;
9993
9994     // private
9995     var sortDialogs = function(d1, d2){
9996         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9997     };
9998
9999     // private
10000     var orderDialogs = function(){
10001         accessList.sort(sortDialogs);
10002         var seed = Roo.DialogManager.zseed;
10003         for(var i = 0, len = accessList.length; i < len; i++){
10004             var dlg = accessList[i];
10005             if(dlg){
10006                 dlg.setZIndex(seed + (i*10));
10007             }
10008         }
10009     };
10010
10011     return {
10012         /**
10013          * The starting z-index for BasicDialogs (defaults to 9000)
10014          * @type Number The z-index value
10015          */
10016         zseed : 9000,
10017
10018         // private
10019         register : function(dlg){
10020             list[dlg.id] = dlg;
10021             accessList.push(dlg);
10022         },
10023
10024         // private
10025         unregister : function(dlg){
10026             delete list[dlg.id];
10027             var i=0;
10028             var len=0;
10029             if(!accessList.indexOf){
10030                 for(  i = 0, len = accessList.length; i < len; i++){
10031                     if(accessList[i] == dlg){
10032                         accessList.splice(i, 1);
10033                         return;
10034                     }
10035                 }
10036             }else{
10037                  i = accessList.indexOf(dlg);
10038                 if(i != -1){
10039                     accessList.splice(i, 1);
10040                 }
10041             }
10042         },
10043
10044         /**
10045          * Gets a registered dialog by id
10046          * @param {String/Object} id The id of the dialog or a dialog
10047          * @return {Roo.BasicDialog} this
10048          */
10049         get : function(id){
10050             return typeof id == "object" ? id : list[id];
10051         },
10052
10053         /**
10054          * Brings the specified dialog to the front
10055          * @param {String/Object} dlg The id of the dialog or a dialog
10056          * @return {Roo.BasicDialog} this
10057          */
10058         bringToFront : function(dlg){
10059             dlg = this.get(dlg);
10060             if(dlg != front){
10061                 front = dlg;
10062                 dlg._lastAccess = new Date().getTime();
10063                 orderDialogs();
10064             }
10065             return dlg;
10066         },
10067
10068         /**
10069          * Sends the specified dialog to the back
10070          * @param {String/Object} dlg The id of the dialog or a dialog
10071          * @return {Roo.BasicDialog} this
10072          */
10073         sendToBack : function(dlg){
10074             dlg = this.get(dlg);
10075             dlg._lastAccess = -(new Date().getTime());
10076             orderDialogs();
10077             return dlg;
10078         },
10079
10080         /**
10081          * Hides all dialogs
10082          */
10083         hideAll : function(){
10084             for(var id in list){
10085                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10086                     list[id].hide();
10087                 }
10088             }
10089         }
10090     };
10091 }();
10092
10093 /**
10094  * @class Roo.LayoutDialog
10095  * @extends Roo.BasicDialog
10096  * @children Roo.ContentPanel
10097  * Dialog which provides adjustments for working with a layout in a Dialog.
10098  * Add your necessary layout config options to the dialog's config.<br>
10099  * Example usage (including a nested layout):
10100  * <pre><code>
10101 if(!dialog){
10102     dialog = new Roo.LayoutDialog("download-dlg", {
10103         modal: true,
10104         width:600,
10105         height:450,
10106         shadow:true,
10107         minWidth:500,
10108         minHeight:350,
10109         autoTabs:true,
10110         proxyDrag:true,
10111         // layout config merges with the dialog config
10112         center:{
10113             tabPosition: "top",
10114             alwaysShowTabs: true
10115         }
10116     });
10117     dialog.addKeyListener(27, dialog.hide, dialog);
10118     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10119     dialog.addButton("Build It!", this.getDownload, this);
10120
10121     // we can even add nested layouts
10122     var innerLayout = new Roo.BorderLayout("dl-inner", {
10123         east: {
10124             initialSize: 200,
10125             autoScroll:true,
10126             split:true
10127         },
10128         center: {
10129             autoScroll:true
10130         }
10131     });
10132     innerLayout.beginUpdate();
10133     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10134     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10135     innerLayout.endUpdate(true);
10136
10137     var layout = dialog.getLayout();
10138     layout.beginUpdate();
10139     layout.add("center", new Roo.ContentPanel("standard-panel",
10140                         {title: "Download the Source", fitToFrame:true}));
10141     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10142                {title: "Build your own roo.js"}));
10143     layout.getRegion("center").showPanel(sp);
10144     layout.endUpdate();
10145 }
10146 </code></pre>
10147     * @constructor
10148     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10149     * @param {Object} config configuration options
10150   */
10151 Roo.LayoutDialog = function(el, cfg){
10152     
10153     var config=  cfg;
10154     if (typeof(cfg) == 'undefined') {
10155         config = Roo.apply({}, el);
10156         // not sure why we use documentElement here.. - it should always be body.
10157         // IE7 borks horribly if we use documentElement.
10158         // webkit also does not like documentElement - it creates a body element...
10159         el = Roo.get( document.body || document.documentElement ).createChild();
10160         //config.autoCreate = true;
10161     }
10162     
10163     
10164     config.autoTabs = false;
10165     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10166     this.body.setStyle({overflow:"hidden", position:"relative"});
10167     this.layout = new Roo.BorderLayout(this.body.dom, config);
10168     this.layout.monitorWindowResize = false;
10169     this.el.addClass("x-dlg-auto-layout");
10170     // fix case when center region overwrites center function
10171     this.center = Roo.BasicDialog.prototype.center;
10172     this.on("show", this.layout.layout, this.layout, true);
10173     if (config.items) {
10174         var xitems = config.items;
10175         delete config.items;
10176         Roo.each(xitems, this.addxtype, this);
10177     }
10178     
10179     
10180 };
10181 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10182     
10183     
10184     /**
10185      * @cfg {Roo.LayoutRegion} east  
10186      */
10187     /**
10188      * @cfg {Roo.LayoutRegion} west
10189      */
10190     /**
10191      * @cfg {Roo.LayoutRegion} south
10192      */
10193     /**
10194      * @cfg {Roo.LayoutRegion} north
10195      */
10196     /**
10197      * @cfg {Roo.LayoutRegion} center
10198      */
10199     /**
10200      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10201      */
10202     
10203     
10204     /**
10205      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10206      * @deprecated
10207      */
10208     endUpdate : function(){
10209         this.layout.endUpdate();
10210     },
10211
10212     /**
10213      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10214      *  @deprecated
10215      */
10216     beginUpdate : function(){
10217         this.layout.beginUpdate();
10218     },
10219
10220     /**
10221      * Get the BorderLayout for this dialog
10222      * @return {Roo.BorderLayout}
10223      */
10224     getLayout : function(){
10225         return this.layout;
10226     },
10227
10228     showEl : function(){
10229         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10230         if(Roo.isIE7){
10231             this.layout.layout();
10232         }
10233     },
10234
10235     // private
10236     // Use the syncHeightBeforeShow config option to control this automatically
10237     syncBodyHeight : function(){
10238         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10239         if(this.layout){this.layout.layout();}
10240     },
10241     
10242       /**
10243      * Add an xtype element (actually adds to the layout.)
10244      * @return {Object} xdata xtype object data.
10245      */
10246     
10247     addxtype : function(c) {
10248         return this.layout.addxtype(c);
10249     }
10250 });/*
10251  * Based on:
10252  * Ext JS Library 1.1.1
10253  * Copyright(c) 2006-2007, Ext JS, LLC.
10254  *
10255  * Originally Released Under LGPL - original licence link has changed is not relivant.
10256  *
10257  * Fork - LGPL
10258  * <script type="text/javascript">
10259  */
10260  
10261 /**
10262  * @class Roo.MessageBox
10263  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10264  * Example usage:
10265  *<pre><code>
10266 // Basic alert:
10267 Roo.Msg.alert('Status', 'Changes saved successfully.');
10268
10269 // Prompt for user data:
10270 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10271     if (btn == 'ok'){
10272         // process text value...
10273     }
10274 });
10275
10276 // Show a dialog using config options:
10277 Roo.Msg.show({
10278    title:'Save Changes?',
10279    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10280    buttons: Roo.Msg.YESNOCANCEL,
10281    fn: processResult,
10282    animEl: 'elId'
10283 });
10284 </code></pre>
10285  * @singleton
10286  */
10287 Roo.MessageBox = function(){
10288     var dlg, opt, mask, waitTimer;
10289     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10290     var buttons, activeTextEl, bwidth;
10291
10292     // private
10293     var handleButton = function(button){
10294         dlg.hide();
10295         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10296     };
10297
10298     // private
10299     var handleHide = function(){
10300         if(opt && opt.cls){
10301             dlg.el.removeClass(opt.cls);
10302         }
10303         if(waitTimer){
10304             Roo.TaskMgr.stop(waitTimer);
10305             waitTimer = null;
10306         }
10307     };
10308
10309     // private
10310     var updateButtons = function(b){
10311         var width = 0;
10312         if(!b){
10313             buttons["ok"].hide();
10314             buttons["cancel"].hide();
10315             buttons["yes"].hide();
10316             buttons["no"].hide();
10317             dlg.footer.dom.style.display = 'none';
10318             return width;
10319         }
10320         dlg.footer.dom.style.display = '';
10321         for(var k in buttons){
10322             if(typeof buttons[k] != "function"){
10323                 if(b[k]){
10324                     buttons[k].show();
10325                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10326                     width += buttons[k].el.getWidth()+15;
10327                 }else{
10328                     buttons[k].hide();
10329                 }
10330             }
10331         }
10332         return width;
10333     };
10334
10335     // private
10336     var handleEsc = function(d, k, e){
10337         if(opt && opt.closable !== false){
10338             dlg.hide();
10339         }
10340         if(e){
10341             e.stopEvent();
10342         }
10343     };
10344
10345     return {
10346         /**
10347          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10348          * @return {Roo.BasicDialog} The BasicDialog element
10349          */
10350         getDialog : function(){
10351            if(!dlg){
10352                 dlg = new Roo.BasicDialog("x-msg-box", {
10353                     autoCreate : true,
10354                     shadow: true,
10355                     draggable: true,
10356                     resizable:false,
10357                     constraintoviewport:false,
10358                     fixedcenter:true,
10359                     collapsible : false,
10360                     shim:true,
10361                     modal: true,
10362                     width:400, height:100,
10363                     buttonAlign:"center",
10364                     closeClick : function(){
10365                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10366                             handleButton("no");
10367                         }else{
10368                             handleButton("cancel");
10369                         }
10370                     }
10371                 });
10372                 dlg.on("hide", handleHide);
10373                 mask = dlg.mask;
10374                 dlg.addKeyListener(27, handleEsc);
10375                 buttons = {};
10376                 var bt = this.buttonText;
10377                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10378                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10379                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10380                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10381                 bodyEl = dlg.body.createChild({
10382
10383                     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>'
10384                 });
10385                 msgEl = bodyEl.dom.firstChild;
10386                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10387                 textboxEl.enableDisplayMode();
10388                 textboxEl.addKeyListener([10,13], function(){
10389                     if(dlg.isVisible() && opt && opt.buttons){
10390                         if(opt.buttons.ok){
10391                             handleButton("ok");
10392                         }else if(opt.buttons.yes){
10393                             handleButton("yes");
10394                         }
10395                     }
10396                 });
10397                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10398                 textareaEl.enableDisplayMode();
10399                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10400                 progressEl.enableDisplayMode();
10401                 var pf = progressEl.dom.firstChild;
10402                 if (pf) {
10403                     pp = Roo.get(pf.firstChild);
10404                     pp.setHeight(pf.offsetHeight);
10405                 }
10406                 
10407             }
10408             return dlg;
10409         },
10410
10411         /**
10412          * Updates the message box body text
10413          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10414          * the XHTML-compliant non-breaking space character '&amp;#160;')
10415          * @return {Roo.MessageBox} This message box
10416          */
10417         updateText : function(text){
10418             if(!dlg.isVisible() && !opt.width){
10419                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10420             }
10421             msgEl.innerHTML = text || '&#160;';
10422       
10423             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10424             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10425             var w = Math.max(
10426                     Math.min(opt.width || cw , this.maxWidth), 
10427                     Math.max(opt.minWidth || this.minWidth, bwidth)
10428             );
10429             if(opt.prompt){
10430                 activeTextEl.setWidth(w);
10431             }
10432             if(dlg.isVisible()){
10433                 dlg.fixedcenter = false;
10434             }
10435             // to big, make it scroll. = But as usual stupid IE does not support
10436             // !important..
10437             
10438             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10439                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10440                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10441             } else {
10442                 bodyEl.dom.style.height = '';
10443                 bodyEl.dom.style.overflowY = '';
10444             }
10445             if (cw > w) {
10446                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10447             } else {
10448                 bodyEl.dom.style.overflowX = '';
10449             }
10450             
10451             dlg.setContentSize(w, bodyEl.getHeight());
10452             if(dlg.isVisible()){
10453                 dlg.fixedcenter = true;
10454             }
10455             return this;
10456         },
10457
10458         /**
10459          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10460          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10461          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10462          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateProgress : function(value, text){
10466             if(text){
10467                 this.updateText(text);
10468             }
10469             if (pp) { // weird bug on my firefox - for some reason this is not defined
10470                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10471             }
10472             return this;
10473         },        
10474
10475         /**
10476          * Returns true if the message box is currently displayed
10477          * @return {Boolean} True if the message box is visible, else false
10478          */
10479         isVisible : function(){
10480             return dlg && dlg.isVisible();  
10481         },
10482
10483         /**
10484          * Hides the message box if it is displayed
10485          */
10486         hide : function(){
10487             if(this.isVisible()){
10488                 dlg.hide();
10489             }  
10490         },
10491
10492         /**
10493          * Displays a new message box, or reinitializes an existing message box, based on the config options
10494          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10495          * The following config object properties are supported:
10496          * <pre>
10497 Property    Type             Description
10498 ----------  ---------------  ------------------------------------------------------------------------------------
10499 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10500                                    closes (defaults to undefined)
10501 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10502                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10503 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10504                                    progress and wait dialogs will ignore this property and always hide the
10505                                    close button as they can only be closed programmatically.
10506 cls               String           A custom CSS class to apply to the message box element
10507 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10508                                    displayed (defaults to 75)
10509 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10510                                    function will be btn (the name of the button that was clicked, if applicable,
10511                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10512                                    Progress and wait dialogs will ignore this option since they do not respond to
10513                                    user actions and can only be closed programmatically, so any required function
10514                                    should be called by the same code after it closes the dialog.
10515 icon              String           A CSS class that provides a background image to be used as an icon for
10516                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10517 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10518 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10519 modal             Boolean          False to allow user interaction with the page while the message box is
10520                                    displayed (defaults to true)
10521 msg               String           A string that will replace the existing message box body text (defaults
10522                                    to the XHTML-compliant non-breaking space character '&#160;')
10523 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10524 progress          Boolean          True to display a progress bar (defaults to false)
10525 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10526 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10527 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10528 title             String           The title text
10529 value             String           The string value to set into the active textbox element if displayed
10530 wait              Boolean          True to display a progress bar (defaults to false)
10531 width             Number           The width of the dialog in pixels
10532 </pre>
10533          *
10534          * Example usage:
10535          * <pre><code>
10536 Roo.Msg.show({
10537    title: 'Address',
10538    msg: 'Please enter your address:',
10539    width: 300,
10540    buttons: Roo.MessageBox.OKCANCEL,
10541    multiline: true,
10542    fn: saveAddress,
10543    animEl: 'addAddressBtn'
10544 });
10545 </code></pre>
10546          * @param {Object} config Configuration options
10547          * @return {Roo.MessageBox} This message box
10548          */
10549         show : function(options)
10550         {
10551             
10552             // this causes nightmares if you show one dialog after another
10553             // especially on callbacks..
10554              
10555             if(this.isVisible()){
10556                 
10557                 this.hide();
10558                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10559                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10560                 Roo.log("New Dialog Message:" +  options.msg )
10561                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10562                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10563                 
10564             }
10565             var d = this.getDialog();
10566             opt = options;
10567             d.setTitle(opt.title || "&#160;");
10568             d.close.setDisplayed(opt.closable !== false);
10569             activeTextEl = textboxEl;
10570             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10571             if(opt.prompt){
10572                 if(opt.multiline){
10573                     textboxEl.hide();
10574                     textareaEl.show();
10575                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10576                         opt.multiline : this.defaultTextHeight);
10577                     activeTextEl = textareaEl;
10578                 }else{
10579                     textboxEl.show();
10580                     textareaEl.hide();
10581                 }
10582             }else{
10583                 textboxEl.hide();
10584                 textareaEl.hide();
10585             }
10586             progressEl.setDisplayed(opt.progress === true);
10587             this.updateProgress(0);
10588             activeTextEl.dom.value = opt.value || "";
10589             if(opt.prompt){
10590                 dlg.setDefaultButton(activeTextEl);
10591             }else{
10592                 var bs = opt.buttons;
10593                 var db = null;
10594                 if(bs && bs.ok){
10595                     db = buttons["ok"];
10596                 }else if(bs && bs.yes){
10597                     db = buttons["yes"];
10598                 }
10599                 dlg.setDefaultButton(db);
10600             }
10601             bwidth = updateButtons(opt.buttons);
10602             this.updateText(opt.msg);
10603             if(opt.cls){
10604                 d.el.addClass(opt.cls);
10605             }
10606             d.proxyDrag = opt.proxyDrag === true;
10607             d.modal = opt.modal !== false;
10608             d.mask = opt.modal !== false ? mask : false;
10609             if(!d.isVisible()){
10610                 // force it to the end of the z-index stack so it gets a cursor in FF
10611                 document.body.appendChild(dlg.el.dom);
10612                 d.animateTarget = null;
10613                 d.show(options.animEl);
10614             }
10615             return this;
10616         },
10617
10618         /**
10619          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10620          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10621          * and closing the message box when the process is complete.
10622          * @param {String} title The title bar text
10623          * @param {String} msg The message box body text
10624          * @return {Roo.MessageBox} This message box
10625          */
10626         progress : function(title, msg){
10627             this.show({
10628                 title : title,
10629                 msg : msg,
10630                 buttons: false,
10631                 progress:true,
10632                 closable:false,
10633                 minWidth: this.minProgressWidth,
10634                 modal : true
10635             });
10636             return this;
10637         },
10638
10639         /**
10640          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10641          * If a callback function is passed it will be called after the user clicks the button, and the
10642          * id of the button that was clicked will be passed as the only parameter to the callback
10643          * (could also be the top-right close button).
10644          * @param {String} title The title bar text
10645          * @param {String} msg The message box body text
10646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10647          * @param {Object} scope (optional) The scope of the callback function
10648          * @return {Roo.MessageBox} This message box
10649          */
10650         alert : function(title, msg, fn, scope){
10651             this.show({
10652                 title : title,
10653                 msg : msg,
10654                 buttons: this.OK,
10655                 fn: fn,
10656                 scope : scope,
10657                 modal : true
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10664          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10665          * You are responsible for closing the message box when the process is complete.
10666          * @param {String} msg The message box body text
10667          * @param {String} title (optional) The title bar text
10668          * @return {Roo.MessageBox} This message box
10669          */
10670         wait : function(msg, title){
10671             this.show({
10672                 title : title,
10673                 msg : msg,
10674                 buttons: false,
10675                 closable:false,
10676                 progress:true,
10677                 modal:true,
10678                 width:300,
10679                 wait:true
10680             });
10681             waitTimer = Roo.TaskMgr.start({
10682                 run: function(i){
10683                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10684                 },
10685                 interval: 1000
10686             });
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10692          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10693          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10697          * @param {Object} scope (optional) The scope of the callback function
10698          * @return {Roo.MessageBox} This message box
10699          */
10700         confirm : function(title, msg, fn, scope){
10701             this.show({
10702                 title : title,
10703                 msg : msg,
10704                 buttons: this.YESNO,
10705                 fn: fn,
10706                 scope : scope,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10714          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10715          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10716          * (could also be the top-right close button) and the text that was entered will be passed as the two
10717          * parameters to the callback.
10718          * @param {String} title The title bar text
10719          * @param {String} msg The message box body text
10720          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10721          * @param {Object} scope (optional) The scope of the callback function
10722          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10723          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10724          * @return {Roo.MessageBox} This message box
10725          */
10726         prompt : function(title, msg, fn, scope, multiline){
10727             this.show({
10728                 title : title,
10729                 msg : msg,
10730                 buttons: this.OKCANCEL,
10731                 fn: fn,
10732                 minWidth:250,
10733                 scope : scope,
10734                 prompt:true,
10735                 multiline: multiline,
10736                 modal : true
10737             });
10738             return this;
10739         },
10740
10741         /**
10742          * Button config that displays a single OK button
10743          * @type Object
10744          */
10745         OK : {ok:true},
10746         /**
10747          * Button config that displays Yes and No buttons
10748          * @type Object
10749          */
10750         YESNO : {yes:true, no:true},
10751         /**
10752          * Button config that displays OK and Cancel buttons
10753          * @type Object
10754          */
10755         OKCANCEL : {ok:true, cancel:true},
10756         /**
10757          * Button config that displays Yes, No and Cancel buttons
10758          * @type Object
10759          */
10760         YESNOCANCEL : {yes:true, no:true, cancel:true},
10761
10762         /**
10763          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10764          * @type Number
10765          */
10766         defaultTextHeight : 75,
10767         /**
10768          * The maximum width in pixels of the message box (defaults to 600)
10769          * @type Number
10770          */
10771         maxWidth : 600,
10772         /**
10773          * The minimum width in pixels of the message box (defaults to 100)
10774          * @type Number
10775          */
10776         minWidth : 100,
10777         /**
10778          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10779          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10780          * @type Number
10781          */
10782         minProgressWidth : 250,
10783         /**
10784          * An object containing the default button text strings that can be overriden for localized language support.
10785          * Supported properties are: ok, cancel, yes and no.
10786          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10787          * @type Object
10788          */
10789         buttonText : {
10790             ok : "OK",
10791             cancel : "Cancel",
10792             yes : "Yes",
10793             no : "No"
10794         }
10795     };
10796 }();
10797
10798 /**
10799  * Shorthand for {@link Roo.MessageBox}
10800  */
10801 Roo.Msg = Roo.MessageBox;/*
10802  * Based on:
10803  * Ext JS Library 1.1.1
10804  * Copyright(c) 2006-2007, Ext JS, LLC.
10805  *
10806  * Originally Released Under LGPL - original licence link has changed is not relivant.
10807  *
10808  * Fork - LGPL
10809  * <script type="text/javascript">
10810  */
10811 /**
10812  * @class Roo.QuickTips
10813  * Provides attractive and customizable tooltips for any element.
10814  * @singleton
10815  */
10816 Roo.QuickTips = function(){
10817     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10818     var ce, bd, xy, dd;
10819     var visible = false, disabled = true, inited = false;
10820     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10821     
10822     var onOver = function(e){
10823         if(disabled){
10824             return;
10825         }
10826         var t = e.getTarget();
10827         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10828             return;
10829         }
10830         if(ce && t == ce.el){
10831             clearTimeout(hideProc);
10832             return;
10833         }
10834         if(t && tagEls[t.id]){
10835             tagEls[t.id].el = t;
10836             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10837             return;
10838         }
10839         var ttp, et = Roo.fly(t);
10840         var ns = cfg.namespace;
10841         if(tm.interceptTitles && t.title){
10842             ttp = t.title;
10843             t.qtip = ttp;
10844             t.removeAttribute("title");
10845             e.preventDefault();
10846         }else{
10847             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10848         }
10849         if(ttp){
10850             showProc = show.defer(tm.showDelay, tm, [{
10851                 el: t, 
10852                 text: ttp.replace(/\\n/g,'<br/>'),
10853                 width: et.getAttributeNS(ns, cfg.width),
10854                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10855                 title: et.getAttributeNS(ns, cfg.title),
10856                     cls: et.getAttributeNS(ns, cfg.cls)
10857             }]);
10858         }
10859     };
10860     
10861     var onOut = function(e){
10862         clearTimeout(showProc);
10863         var t = e.getTarget();
10864         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10865             hideProc = setTimeout(hide, tm.hideDelay);
10866         }
10867     };
10868     
10869     var onMove = function(e){
10870         if(disabled){
10871             return;
10872         }
10873         xy = e.getXY();
10874         xy[1] += 18;
10875         if(tm.trackMouse && ce){
10876             el.setXY(xy);
10877         }
10878     };
10879     
10880     var onDown = function(e){
10881         clearTimeout(showProc);
10882         clearTimeout(hideProc);
10883         if(!e.within(el)){
10884             if(tm.hideOnClick){
10885                 hide();
10886                 tm.disable();
10887                 tm.enable.defer(100, tm);
10888             }
10889         }
10890     };
10891     
10892     var getPad = function(){
10893         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10894     };
10895
10896     var show = function(o){
10897         if(disabled){
10898             return;
10899         }
10900         clearTimeout(dismissProc);
10901         ce = o;
10902         if(removeCls){ // in case manually hidden
10903             el.removeClass(removeCls);
10904             removeCls = null;
10905         }
10906         if(ce.cls){
10907             el.addClass(ce.cls);
10908             removeCls = ce.cls;
10909         }
10910         if(ce.title){
10911             tipTitle.update(ce.title);
10912             tipTitle.show();
10913         }else{
10914             tipTitle.update('');
10915             tipTitle.hide();
10916         }
10917         el.dom.style.width  = tm.maxWidth+'px';
10918         //tipBody.dom.style.width = '';
10919         tipBodyText.update(o.text);
10920         var p = getPad(), w = ce.width;
10921         if(!w){
10922             var td = tipBodyText.dom;
10923             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10924             if(aw > tm.maxWidth){
10925                 w = tm.maxWidth;
10926             }else if(aw < tm.minWidth){
10927                 w = tm.minWidth;
10928             }else{
10929                 w = aw;
10930             }
10931         }
10932         //tipBody.setWidth(w);
10933         el.setWidth(parseInt(w, 10) + p);
10934         if(ce.autoHide === false){
10935             close.setDisplayed(true);
10936             if(dd){
10937                 dd.unlock();
10938             }
10939         }else{
10940             close.setDisplayed(false);
10941             if(dd){
10942                 dd.lock();
10943             }
10944         }
10945         if(xy){
10946             el.avoidY = xy[1]-18;
10947             el.setXY(xy);
10948         }
10949         if(tm.animate){
10950             el.setOpacity(.1);
10951             el.setStyle("visibility", "visible");
10952             el.fadeIn({callback: afterShow});
10953         }else{
10954             afterShow();
10955         }
10956     };
10957     
10958     var afterShow = function(){
10959         if(ce){
10960             el.show();
10961             esc.enable();
10962             if(tm.autoDismiss && ce.autoHide !== false){
10963                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10964             }
10965         }
10966     };
10967     
10968     var hide = function(noanim){
10969         clearTimeout(dismissProc);
10970         clearTimeout(hideProc);
10971         ce = null;
10972         if(el.isVisible()){
10973             esc.disable();
10974             if(noanim !== true && tm.animate){
10975                 el.fadeOut({callback: afterHide});
10976             }else{
10977                 afterHide();
10978             } 
10979         }
10980     };
10981     
10982     var afterHide = function(){
10983         el.hide();
10984         if(removeCls){
10985             el.removeClass(removeCls);
10986             removeCls = null;
10987         }
10988     };
10989     
10990     return {
10991         /**
10992         * @cfg {Number} minWidth
10993         * The minimum width of the quick tip (defaults to 40)
10994         */
10995        minWidth : 40,
10996         /**
10997         * @cfg {Number} maxWidth
10998         * The maximum width of the quick tip (defaults to 300)
10999         */
11000        maxWidth : 300,
11001         /**
11002         * @cfg {Boolean} interceptTitles
11003         * True to automatically use the element's DOM title value if available (defaults to false)
11004         */
11005        interceptTitles : false,
11006         /**
11007         * @cfg {Boolean} trackMouse
11008         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11009         */
11010        trackMouse : false,
11011         /**
11012         * @cfg {Boolean} hideOnClick
11013         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11014         */
11015        hideOnClick : true,
11016         /**
11017         * @cfg {Number} showDelay
11018         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11019         */
11020        showDelay : 500,
11021         /**
11022         * @cfg {Number} hideDelay
11023         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11024         */
11025        hideDelay : 200,
11026         /**
11027         * @cfg {Boolean} autoHide
11028         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11029         * Used in conjunction with hideDelay.
11030         */
11031        autoHide : true,
11032         /**
11033         * @cfg {Boolean}
11034         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11035         * (defaults to true).  Used in conjunction with autoDismissDelay.
11036         */
11037        autoDismiss : true,
11038         /**
11039         * @cfg {Number}
11040         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11041         */
11042        autoDismissDelay : 5000,
11043        /**
11044         * @cfg {Boolean} animate
11045         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11046         */
11047        animate : false,
11048
11049        /**
11050         * @cfg {String} title
11051         * Title text to display (defaults to '').  This can be any valid HTML markup.
11052         */
11053         title: '',
11054        /**
11055         * @cfg {String} text
11056         * Body text to display (defaults to '').  This can be any valid HTML markup.
11057         */
11058         text : '',
11059        /**
11060         * @cfg {String} cls
11061         * A CSS class to apply to the base quick tip element (defaults to '').
11062         */
11063         cls : '',
11064        /**
11065         * @cfg {Number} width
11066         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11067         * minWidth or maxWidth.
11068         */
11069         width : null,
11070
11071     /**
11072      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11073      * or display QuickTips in a page.
11074      */
11075        init : function(){
11076           tm = Roo.QuickTips;
11077           cfg = tm.tagConfig;
11078           if(!inited){
11079               if(!Roo.isReady){ // allow calling of init() before onReady
11080                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11081                   return;
11082               }
11083               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11084               el.fxDefaults = {stopFx: true};
11085               // maximum custom styling
11086               //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>');
11087               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>');              
11088               tipTitle = el.child('h3');
11089               tipTitle.enableDisplayMode("block");
11090               tipBody = el.child('div.x-tip-bd');
11091               tipBodyText = el.child('div.x-tip-bd-inner');
11092               //bdLeft = el.child('div.x-tip-bd-left');
11093               //bdRight = el.child('div.x-tip-bd-right');
11094               close = el.child('div.x-tip-close');
11095               close.enableDisplayMode("block");
11096               close.on("click", hide);
11097               var d = Roo.get(document);
11098               d.on("mousedown", onDown);
11099               d.on("mouseover", onOver);
11100               d.on("mouseout", onOut);
11101               d.on("mousemove", onMove);
11102               esc = d.addKeyListener(27, hide);
11103               esc.disable();
11104               if(Roo.dd.DD){
11105                   dd = el.initDD("default", null, {
11106                       onDrag : function(){
11107                           el.sync();  
11108                       }
11109                   });
11110                   dd.setHandleElId(tipTitle.id);
11111                   dd.lock();
11112               }
11113               inited = true;
11114           }
11115           this.enable(); 
11116        },
11117
11118     /**
11119      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11120      * are supported:
11121      * <pre>
11122 Property    Type                   Description
11123 ----------  ---------------------  ------------------------------------------------------------------------
11124 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11125      * </ul>
11126      * @param {Object} config The config object
11127      */
11128        register : function(config){
11129            var cs = config instanceof Array ? config : arguments;
11130            for(var i = 0, len = cs.length; i < len; i++) {
11131                var c = cs[i];
11132                var target = c.target;
11133                if(target){
11134                    if(target instanceof Array){
11135                        for(var j = 0, jlen = target.length; j < jlen; j++){
11136                            tagEls[target[j]] = c;
11137                        }
11138                    }else{
11139                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11140                    }
11141                }
11142            }
11143        },
11144
11145     /**
11146      * Removes this quick tip from its element and destroys it.
11147      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11148      */
11149        unregister : function(el){
11150            delete tagEls[Roo.id(el)];
11151        },
11152
11153     /**
11154      * Enable this quick tip.
11155      */
11156        enable : function(){
11157            if(inited && disabled){
11158                locks.pop();
11159                if(locks.length < 1){
11160                    disabled = false;
11161                }
11162            }
11163        },
11164
11165     /**
11166      * Disable this quick tip.
11167      */
11168        disable : function(){
11169           disabled = true;
11170           clearTimeout(showProc);
11171           clearTimeout(hideProc);
11172           clearTimeout(dismissProc);
11173           if(ce){
11174               hide(true);
11175           }
11176           locks.push(1);
11177        },
11178
11179     /**
11180      * Returns true if the quick tip is enabled, else false.
11181      */
11182        isEnabled : function(){
11183             return !disabled;
11184        },
11185
11186         // private
11187        tagConfig : {
11188            namespace : "roo", // was ext?? this may break..
11189            alt_namespace : "ext",
11190            attribute : "qtip",
11191            width : "width",
11192            target : "target",
11193            title : "qtitle",
11194            hide : "hide",
11195            cls : "qclass"
11196        }
11197    };
11198 }();
11199
11200 // backwards compat
11201 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211  
11212
11213 /**
11214  * @class Roo.tree.TreePanel
11215  * @extends Roo.data.Tree
11216
11217  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11218  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11219  * @cfg {Boolean} enableDD true to enable drag and drop
11220  * @cfg {Boolean} enableDrag true to enable just drag
11221  * @cfg {Boolean} enableDrop true to enable just drop
11222  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11223  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11224  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11225  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11226  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11227  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11228  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11229  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11230  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11231  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11232  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11233  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11234  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11235  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11236  * @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>
11237  * @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>
11238  * 
11239  * @constructor
11240  * @param {String/HTMLElement/Element} el The container element
11241  * @param {Object} config
11242  */
11243 Roo.tree.TreePanel = function(el, config){
11244     var root = false;
11245     var loader = false;
11246     if (config.root) {
11247         root = config.root;
11248         delete config.root;
11249     }
11250     if (config.loader) {
11251         loader = config.loader;
11252         delete config.loader;
11253     }
11254     
11255     Roo.apply(this, config);
11256     Roo.tree.TreePanel.superclass.constructor.call(this);
11257     this.el = Roo.get(el);
11258     this.el.addClass('x-tree');
11259     //console.log(root);
11260     if (root) {
11261         this.setRootNode( Roo.factory(root, Roo.tree));
11262     }
11263     if (loader) {
11264         this.loader = Roo.factory(loader, Roo.tree);
11265     }
11266    /**
11267     * Read-only. The id of the container element becomes this TreePanel's id.
11268     */
11269     this.id = this.el.id;
11270     this.addEvents({
11271         /**
11272         * @event beforeload
11273         * Fires before a node is loaded, return false to cancel
11274         * @param {Node} node The node being loaded
11275         */
11276         "beforeload" : true,
11277         /**
11278         * @event load
11279         * Fires when a node is loaded
11280         * @param {Node} node The node that was loaded
11281         */
11282         "load" : true,
11283         /**
11284         * @event textchange
11285         * Fires when the text for a node is changed
11286         * @param {Node} node The node
11287         * @param {String} text The new text
11288         * @param {String} oldText The old text
11289         */
11290         "textchange" : true,
11291         /**
11292         * @event beforeexpand
11293         * Fires before a node is expanded, return false to cancel.
11294         * @param {Node} node The node
11295         * @param {Boolean} deep
11296         * @param {Boolean} anim
11297         */
11298         "beforeexpand" : true,
11299         /**
11300         * @event beforecollapse
11301         * Fires before a node is collapsed, return false to cancel.
11302         * @param {Node} node The node
11303         * @param {Boolean} deep
11304         * @param {Boolean} anim
11305         */
11306         "beforecollapse" : true,
11307         /**
11308         * @event expand
11309         * Fires when a node is expanded
11310         * @param {Node} node The node
11311         */
11312         "expand" : true,
11313         /**
11314         * @event disabledchange
11315         * Fires when the disabled status of a node changes
11316         * @param {Node} node The node
11317         * @param {Boolean} disabled
11318         */
11319         "disabledchange" : true,
11320         /**
11321         * @event collapse
11322         * Fires when a node is collapsed
11323         * @param {Node} node The node
11324         */
11325         "collapse" : true,
11326         /**
11327         * @event beforeclick
11328         * Fires before click processing on a node. Return false to cancel the default action.
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "beforeclick":true,
11333         /**
11334         * @event checkchange
11335         * Fires when a node with a checkbox's checked property changes
11336         * @param {Node} this This node
11337         * @param {Boolean} checked
11338         */
11339         "checkchange":true,
11340         /**
11341         * @event click
11342         * Fires when a node is clicked
11343         * @param {Node} node The node
11344         * @param {Roo.EventObject} e The event object
11345         */
11346         "click":true,
11347         /**
11348         * @event dblclick
11349         * Fires when a node is double clicked
11350         * @param {Node} node The node
11351         * @param {Roo.EventObject} e The event object
11352         */
11353         "dblclick":true,
11354         /**
11355         * @event contextmenu
11356         * Fires when a node is right clicked
11357         * @param {Node} node The node
11358         * @param {Roo.EventObject} e The event object
11359         */
11360         "contextmenu":true,
11361         /**
11362         * @event beforechildrenrendered
11363         * Fires right before the child nodes for a node are rendered
11364         * @param {Node} node The node
11365         */
11366         "beforechildrenrendered":true,
11367         /**
11368         * @event startdrag
11369         * Fires when a node starts being dragged
11370         * @param {Roo.tree.TreePanel} this
11371         * @param {Roo.tree.TreeNode} node
11372         * @param {event} e The raw browser event
11373         */ 
11374        "startdrag" : true,
11375        /**
11376         * @event enddrag
11377         * Fires when a drag operation is complete
11378         * @param {Roo.tree.TreePanel} this
11379         * @param {Roo.tree.TreeNode} node
11380         * @param {event} e The raw browser event
11381         */
11382        "enddrag" : true,
11383        /**
11384         * @event dragdrop
11385         * Fires when a dragged node is dropped on a valid DD target
11386         * @param {Roo.tree.TreePanel} this
11387         * @param {Roo.tree.TreeNode} node
11388         * @param {DD} dd The dd it was dropped on
11389         * @param {event} e The raw browser event
11390         */
11391        "dragdrop" : true,
11392        /**
11393         * @event beforenodedrop
11394         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11395         * passed to handlers has the following properties:<br />
11396         * <ul style="padding:5px;padding-left:16px;">
11397         * <li>tree - The TreePanel</li>
11398         * <li>target - The node being targeted for the drop</li>
11399         * <li>data - The drag data from the drag source</li>
11400         * <li>point - The point of the drop - append, above or below</li>
11401         * <li>source - The drag source</li>
11402         * <li>rawEvent - Raw mouse event</li>
11403         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11404         * to be inserted by setting them on this object.</li>
11405         * <li>cancel - Set this to true to cancel the drop.</li>
11406         * </ul>
11407         * @param {Object} dropEvent
11408         */
11409        "beforenodedrop" : true,
11410        /**
11411         * @event nodedrop
11412         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11413         * passed to handlers has the following properties:<br />
11414         * <ul style="padding:5px;padding-left:16px;">
11415         * <li>tree - The TreePanel</li>
11416         * <li>target - The node being targeted for the drop</li>
11417         * <li>data - The drag data from the drag source</li>
11418         * <li>point - The point of the drop - append, above or below</li>
11419         * <li>source - The drag source</li>
11420         * <li>rawEvent - Raw mouse event</li>
11421         * <li>dropNode - Dropped node(s).</li>
11422         * </ul>
11423         * @param {Object} dropEvent
11424         */
11425        "nodedrop" : true,
11426         /**
11427         * @event nodedragover
11428         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11429         * passed to handlers has the following properties:<br />
11430         * <ul style="padding:5px;padding-left:16px;">
11431         * <li>tree - The TreePanel</li>
11432         * <li>target - The node being targeted for the drop</li>
11433         * <li>data - The drag data from the drag source</li>
11434         * <li>point - The point of the drop - append, above or below</li>
11435         * <li>source - The drag source</li>
11436         * <li>rawEvent - Raw mouse event</li>
11437         * <li>dropNode - Drop node(s) provided by the source.</li>
11438         * <li>cancel - Set this to true to signal drop not allowed.</li>
11439         * </ul>
11440         * @param {Object} dragOverEvent
11441         */
11442        "nodedragover" : true,
11443        /**
11444         * @event appendnode
11445         * Fires when append node to the tree
11446         * @param {Roo.tree.TreePanel} this
11447         * @param {Roo.tree.TreeNode} node
11448         * @param {Number} index The index of the newly appended node
11449         */
11450        "appendnode" : true
11451         
11452     });
11453     if(this.singleExpand){
11454        this.on("beforeexpand", this.restrictExpand, this);
11455     }
11456     if (this.editor) {
11457         this.editor.tree = this;
11458         this.editor = Roo.factory(this.editor, Roo.tree);
11459     }
11460     
11461     if (this.selModel) {
11462         this.selModel = Roo.factory(this.selModel, Roo.tree);
11463     }
11464    
11465 };
11466 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11467     rootVisible : true,
11468     animate: Roo.enableFx,
11469     lines : true,
11470     enableDD : false,
11471     hlDrop : Roo.enableFx,
11472   
11473     renderer: false,
11474     
11475     rendererTip: false,
11476     // private
11477     restrictExpand : function(node){
11478         var p = node.parentNode;
11479         if(p){
11480             if(p.expandedChild && p.expandedChild.parentNode == p){
11481                 p.expandedChild.collapse();
11482             }
11483             p.expandedChild = node;
11484         }
11485     },
11486
11487     // private override
11488     setRootNode : function(node){
11489         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11490         if(!this.rootVisible){
11491             node.ui = new Roo.tree.RootTreeNodeUI(node);
11492         }
11493         return node;
11494     },
11495
11496     /**
11497      * Returns the container element for this TreePanel
11498      */
11499     getEl : function(){
11500         return this.el;
11501     },
11502
11503     /**
11504      * Returns the default TreeLoader for this TreePanel
11505      */
11506     getLoader : function(){
11507         return this.loader;
11508     },
11509
11510     /**
11511      * Expand all nodes
11512      */
11513     expandAll : function(){
11514         this.root.expand(true);
11515     },
11516
11517     /**
11518      * Collapse all nodes
11519      */
11520     collapseAll : function(){
11521         this.root.collapse(true);
11522     },
11523
11524     /**
11525      * Returns the selection model used by this TreePanel
11526      */
11527     getSelectionModel : function(){
11528         if(!this.selModel){
11529             this.selModel = new Roo.tree.DefaultSelectionModel();
11530         }
11531         return this.selModel;
11532     },
11533
11534     /**
11535      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11536      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11537      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11538      * @return {Array}
11539      */
11540     getChecked : function(a, startNode){
11541         startNode = startNode || this.root;
11542         var r = [];
11543         var f = function(){
11544             if(this.attributes.checked){
11545                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11546             }
11547         }
11548         startNode.cascade(f);
11549         return r;
11550     },
11551
11552     /**
11553      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11554      * @param {String} path
11555      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11556      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11557      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11558      */
11559     expandPath : function(path, attr, callback){
11560         attr = attr || "id";
11561         var keys = path.split(this.pathSeparator);
11562         var curNode = this.root;
11563         if(curNode.attributes[attr] != keys[1]){ // invalid root
11564             if(callback){
11565                 callback(false, null);
11566             }
11567             return;
11568         }
11569         var index = 1;
11570         var f = function(){
11571             if(++index == keys.length){
11572                 if(callback){
11573                     callback(true, curNode);
11574                 }
11575                 return;
11576             }
11577             var c = curNode.findChild(attr, keys[index]);
11578             if(!c){
11579                 if(callback){
11580                     callback(false, curNode);
11581                 }
11582                 return;
11583             }
11584             curNode = c;
11585             c.expand(false, false, f);
11586         };
11587         curNode.expand(false, false, f);
11588     },
11589
11590     /**
11591      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11592      * @param {String} path
11593      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11594      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11595      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11596      */
11597     selectPath : function(path, attr, callback){
11598         attr = attr || "id";
11599         var keys = path.split(this.pathSeparator);
11600         var v = keys.pop();
11601         if(keys.length > 0){
11602             var f = function(success, node){
11603                 if(success && node){
11604                     var n = node.findChild(attr, v);
11605                     if(n){
11606                         n.select();
11607                         if(callback){
11608                             callback(true, n);
11609                         }
11610                     }else if(callback){
11611                         callback(false, n);
11612                     }
11613                 }else{
11614                     if(callback){
11615                         callback(false, n);
11616                     }
11617                 }
11618             };
11619             this.expandPath(keys.join(this.pathSeparator), attr, f);
11620         }else{
11621             this.root.select();
11622             if(callback){
11623                 callback(true, this.root);
11624             }
11625         }
11626     },
11627
11628     getTreeEl : function(){
11629         return this.el;
11630     },
11631
11632     /**
11633      * Trigger rendering of this TreePanel
11634      */
11635     render : function(){
11636         if (this.innerCt) {
11637             return this; // stop it rendering more than once!!
11638         }
11639         
11640         this.innerCt = this.el.createChild({tag:"ul",
11641                cls:"x-tree-root-ct " +
11642                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11643
11644         if(this.containerScroll){
11645             Roo.dd.ScrollManager.register(this.el);
11646         }
11647         if((this.enableDD || this.enableDrop) && !this.dropZone){
11648            /**
11649             * The dropZone used by this tree if drop is enabled
11650             * @type Roo.tree.TreeDropZone
11651             */
11652              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11653                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11654            });
11655         }
11656         if((this.enableDD || this.enableDrag) && !this.dragZone){
11657            /**
11658             * The dragZone used by this tree if drag is enabled
11659             * @type Roo.tree.TreeDragZone
11660             */
11661             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11662                ddGroup: this.ddGroup || "TreeDD",
11663                scroll: this.ddScroll
11664            });
11665         }
11666         this.getSelectionModel().init(this);
11667         if (!this.root) {
11668             Roo.log("ROOT not set in tree");
11669             return this;
11670         }
11671         this.root.render();
11672         if(!this.rootVisible){
11673             this.root.renderChildren();
11674         }
11675         return this;
11676     }
11677 });/*
11678  * Based on:
11679  * Ext JS Library 1.1.1
11680  * Copyright(c) 2006-2007, Ext JS, LLC.
11681  *
11682  * Originally Released Under LGPL - original licence link has changed is not relivant.
11683  *
11684  * Fork - LGPL
11685  * <script type="text/javascript">
11686  */
11687  
11688
11689 /**
11690  * @class Roo.tree.DefaultSelectionModel
11691  * @extends Roo.util.Observable
11692  * The default single selection for a TreePanel.
11693  * @param {Object} cfg Configuration
11694  */
11695 Roo.tree.DefaultSelectionModel = function(cfg){
11696    this.selNode = null;
11697    
11698    
11699    
11700    this.addEvents({
11701        /**
11702         * @event selectionchange
11703         * Fires when the selected node changes
11704         * @param {DefaultSelectionModel} this
11705         * @param {TreeNode} node the new selection
11706         */
11707        "selectionchange" : true,
11708
11709        /**
11710         * @event beforeselect
11711         * Fires before the selected node changes, return false to cancel the change
11712         * @param {DefaultSelectionModel} this
11713         * @param {TreeNode} node the new selection
11714         * @param {TreeNode} node the old selection
11715         */
11716        "beforeselect" : true
11717    });
11718    
11719     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11720 };
11721
11722 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11723     init : function(tree){
11724         this.tree = tree;
11725         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11726         tree.on("click", this.onNodeClick, this);
11727     },
11728     
11729     onNodeClick : function(node, e){
11730         if (e.ctrlKey && this.selNode == node)  {
11731             this.unselect(node);
11732             return;
11733         }
11734         this.select(node);
11735     },
11736     
11737     /**
11738      * Select a node.
11739      * @param {TreeNode} node The node to select
11740      * @return {TreeNode} The selected node
11741      */
11742     select : function(node){
11743         var last = this.selNode;
11744         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11745             if(last){
11746                 last.ui.onSelectedChange(false);
11747             }
11748             this.selNode = node;
11749             node.ui.onSelectedChange(true);
11750             this.fireEvent("selectionchange", this, node, last);
11751         }
11752         return node;
11753     },
11754     
11755     /**
11756      * Deselect a node.
11757      * @param {TreeNode} node The node to unselect
11758      */
11759     unselect : function(node){
11760         if(this.selNode == node){
11761             this.clearSelections();
11762         }    
11763     },
11764     
11765     /**
11766      * Clear all selections
11767      */
11768     clearSelections : function(){
11769         var n = this.selNode;
11770         if(n){
11771             n.ui.onSelectedChange(false);
11772             this.selNode = null;
11773             this.fireEvent("selectionchange", this, null);
11774         }
11775         return n;
11776     },
11777     
11778     /**
11779      * Get the selected node
11780      * @return {TreeNode} The selected node
11781      */
11782     getSelectedNode : function(){
11783         return this.selNode;    
11784     },
11785     
11786     /**
11787      * Returns true if the node is selected
11788      * @param {TreeNode} node The node to check
11789      * @return {Boolean}
11790      */
11791     isSelected : function(node){
11792         return this.selNode == node;  
11793     },
11794
11795     /**
11796      * Selects the node above the selected node in the tree, intelligently walking the nodes
11797      * @return TreeNode The new selection
11798      */
11799     selectPrevious : function(){
11800         var s = this.selNode || this.lastSelNode;
11801         if(!s){
11802             return null;
11803         }
11804         var ps = s.previousSibling;
11805         if(ps){
11806             if(!ps.isExpanded() || ps.childNodes.length < 1){
11807                 return this.select(ps);
11808             } else{
11809                 var lc = ps.lastChild;
11810                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11811                     lc = lc.lastChild;
11812                 }
11813                 return this.select(lc);
11814             }
11815         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11816             return this.select(s.parentNode);
11817         }
11818         return null;
11819     },
11820
11821     /**
11822      * Selects the node above the selected node in the tree, intelligently walking the nodes
11823      * @return TreeNode The new selection
11824      */
11825     selectNext : function(){
11826         var s = this.selNode || this.lastSelNode;
11827         if(!s){
11828             return null;
11829         }
11830         if(s.firstChild && s.isExpanded()){
11831              return this.select(s.firstChild);
11832          }else if(s.nextSibling){
11833              return this.select(s.nextSibling);
11834          }else if(s.parentNode){
11835             var newS = null;
11836             s.parentNode.bubble(function(){
11837                 if(this.nextSibling){
11838                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11839                     return false;
11840                 }
11841             });
11842             return newS;
11843          }
11844         return null;
11845     },
11846
11847     onKeyDown : function(e){
11848         var s = this.selNode || this.lastSelNode;
11849         // undesirable, but required
11850         var sm = this;
11851         if(!s){
11852             return;
11853         }
11854         var k = e.getKey();
11855         switch(k){
11856              case e.DOWN:
11857                  e.stopEvent();
11858                  this.selectNext();
11859              break;
11860              case e.UP:
11861                  e.stopEvent();
11862                  this.selectPrevious();
11863              break;
11864              case e.RIGHT:
11865                  e.preventDefault();
11866                  if(s.hasChildNodes()){
11867                      if(!s.isExpanded()){
11868                          s.expand();
11869                      }else if(s.firstChild){
11870                          this.select(s.firstChild, e);
11871                      }
11872                  }
11873              break;
11874              case e.LEFT:
11875                  e.preventDefault();
11876                  if(s.hasChildNodes() && s.isExpanded()){
11877                      s.collapse();
11878                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11879                      this.select(s.parentNode, e);
11880                  }
11881              break;
11882         };
11883     }
11884 });
11885
11886 /**
11887  * @class Roo.tree.MultiSelectionModel
11888  * @extends Roo.util.Observable
11889  * Multi selection for a TreePanel.
11890  * @param {Object} cfg Configuration
11891  */
11892 Roo.tree.MultiSelectionModel = function(){
11893    this.selNodes = [];
11894    this.selMap = {};
11895    this.addEvents({
11896        /**
11897         * @event selectionchange
11898         * Fires when the selected nodes change
11899         * @param {MultiSelectionModel} this
11900         * @param {Array} nodes Array of the selected nodes
11901         */
11902        "selectionchange" : true
11903    });
11904    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11905    
11906 };
11907
11908 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11909     init : function(tree){
11910         this.tree = tree;
11911         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11912         tree.on("click", this.onNodeClick, this);
11913     },
11914     
11915     onNodeClick : function(node, e){
11916         this.select(node, e, e.ctrlKey);
11917     },
11918     
11919     /**
11920      * Select a node.
11921      * @param {TreeNode} node The node to select
11922      * @param {EventObject} e (optional) An event associated with the selection
11923      * @param {Boolean} keepExisting True to retain existing selections
11924      * @return {TreeNode} The selected node
11925      */
11926     select : function(node, e, keepExisting){
11927         if(keepExisting !== true){
11928             this.clearSelections(true);
11929         }
11930         if(this.isSelected(node)){
11931             this.lastSelNode = node;
11932             return node;
11933         }
11934         this.selNodes.push(node);
11935         this.selMap[node.id] = node;
11936         this.lastSelNode = node;
11937         node.ui.onSelectedChange(true);
11938         this.fireEvent("selectionchange", this, this.selNodes);
11939         return node;
11940     },
11941     
11942     /**
11943      * Deselect a node.
11944      * @param {TreeNode} node The node to unselect
11945      */
11946     unselect : function(node){
11947         if(this.selMap[node.id]){
11948             node.ui.onSelectedChange(false);
11949             var sn = this.selNodes;
11950             var index = -1;
11951             if(sn.indexOf){
11952                 index = sn.indexOf(node);
11953             }else{
11954                 for(var i = 0, len = sn.length; i < len; i++){
11955                     if(sn[i] == node){
11956                         index = i;
11957                         break;
11958                     }
11959                 }
11960             }
11961             if(index != -1){
11962                 this.selNodes.splice(index, 1);
11963             }
11964             delete this.selMap[node.id];
11965             this.fireEvent("selectionchange", this, this.selNodes);
11966         }
11967     },
11968     
11969     /**
11970      * Clear all selections
11971      */
11972     clearSelections : function(suppressEvent){
11973         var sn = this.selNodes;
11974         if(sn.length > 0){
11975             for(var i = 0, len = sn.length; i < len; i++){
11976                 sn[i].ui.onSelectedChange(false);
11977             }
11978             this.selNodes = [];
11979             this.selMap = {};
11980             if(suppressEvent !== true){
11981                 this.fireEvent("selectionchange", this, this.selNodes);
11982             }
11983         }
11984     },
11985     
11986     /**
11987      * Returns true if the node is selected
11988      * @param {TreeNode} node The node to check
11989      * @return {Boolean}
11990      */
11991     isSelected : function(node){
11992         return this.selMap[node.id] ? true : false;  
11993     },
11994     
11995     /**
11996      * Returns an array of the selected nodes
11997      * @return {Array}
11998      */
11999     getSelectedNodes : function(){
12000         return this.selNodes;    
12001     },
12002
12003     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12004
12005     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12006
12007     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12008 });/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018  
12019 /**
12020  * @class Roo.tree.TreeNode
12021  * @extends Roo.data.Node
12022  * @cfg {String} text The text for this node
12023  * @cfg {Boolean} expanded true to start the node expanded
12024  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12025  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12026  * @cfg {Boolean} disabled true to start the node disabled
12027  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12028  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12029  * @cfg {String} cls A css class to be added to the node
12030  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12031  * @cfg {String} href URL of the link used for the node (defaults to #)
12032  * @cfg {String} hrefTarget target frame for the link
12033  * @cfg {String} qtip An Ext QuickTip for the node
12034  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12035  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12036  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12037  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12038  * (defaults to undefined with no checkbox rendered)
12039  * @constructor
12040  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12041  */
12042 Roo.tree.TreeNode = function(attributes){
12043     attributes = attributes || {};
12044     if(typeof attributes == "string"){
12045         attributes = {text: attributes};
12046     }
12047     this.childrenRendered = false;
12048     this.rendered = false;
12049     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12050     this.expanded = attributes.expanded === true;
12051     this.isTarget = attributes.isTarget !== false;
12052     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12053     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12054
12055     /**
12056      * Read-only. The text for this node. To change it use setText().
12057      * @type String
12058      */
12059     this.text = attributes.text;
12060     /**
12061      * True if this node is disabled.
12062      * @type Boolean
12063      */
12064     this.disabled = attributes.disabled === true;
12065
12066     this.addEvents({
12067         /**
12068         * @event textchange
12069         * Fires when the text for this node is changed
12070         * @param {Node} this This node
12071         * @param {String} text The new text
12072         * @param {String} oldText The old text
12073         */
12074         "textchange" : true,
12075         /**
12076         * @event beforeexpand
12077         * Fires before this node is expanded, return false to cancel.
12078         * @param {Node} this This node
12079         * @param {Boolean} deep
12080         * @param {Boolean} anim
12081         */
12082         "beforeexpand" : true,
12083         /**
12084         * @event beforecollapse
12085         * Fires before this node is collapsed, return false to cancel.
12086         * @param {Node} this This node
12087         * @param {Boolean} deep
12088         * @param {Boolean} anim
12089         */
12090         "beforecollapse" : true,
12091         /**
12092         * @event expand
12093         * Fires when this node is expanded
12094         * @param {Node} this This node
12095         */
12096         "expand" : true,
12097         /**
12098         * @event disabledchange
12099         * Fires when the disabled status of this node changes
12100         * @param {Node} this This node
12101         * @param {Boolean} disabled
12102         */
12103         "disabledchange" : true,
12104         /**
12105         * @event collapse
12106         * Fires when this node is collapsed
12107         * @param {Node} this This node
12108         */
12109         "collapse" : true,
12110         /**
12111         * @event beforeclick
12112         * Fires before click processing. Return false to cancel the default action.
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "beforeclick":true,
12117         /**
12118         * @event checkchange
12119         * Fires when a node with a checkbox's checked property changes
12120         * @param {Node} this This node
12121         * @param {Boolean} checked
12122         */
12123         "checkchange":true,
12124         /**
12125         * @event click
12126         * Fires when this node is clicked
12127         * @param {Node} this This node
12128         * @param {Roo.EventObject} e The event object
12129         */
12130         "click":true,
12131         /**
12132         * @event dblclick
12133         * Fires when this node is double clicked
12134         * @param {Node} this This node
12135         * @param {Roo.EventObject} e The event object
12136         */
12137         "dblclick":true,
12138         /**
12139         * @event contextmenu
12140         * Fires when this node is right clicked
12141         * @param {Node} this This node
12142         * @param {Roo.EventObject} e The event object
12143         */
12144         "contextmenu":true,
12145         /**
12146         * @event beforechildrenrendered
12147         * Fires right before the child nodes for this node are rendered
12148         * @param {Node} this This node
12149         */
12150         "beforechildrenrendered":true
12151     });
12152
12153     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12154
12155     /**
12156      * Read-only. The UI for this node
12157      * @type TreeNodeUI
12158      */
12159     this.ui = new uiClass(this);
12160     
12161     // finally support items[]
12162     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12163         return;
12164     }
12165     
12166     
12167     Roo.each(this.attributes.items, function(c) {
12168         this.appendChild(Roo.factory(c,Roo.Tree));
12169     }, this);
12170     delete this.attributes.items;
12171     
12172     
12173     
12174 };
12175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12176     preventHScroll: true,
12177     /**
12178      * Returns true if this node is expanded
12179      * @return {Boolean}
12180      */
12181     isExpanded : function(){
12182         return this.expanded;
12183     },
12184
12185     /**
12186      * Returns the UI object for this node
12187      * @return {TreeNodeUI}
12188      */
12189     getUI : function(){
12190         return this.ui;
12191     },
12192
12193     // private override
12194     setFirstChild : function(node){
12195         var of = this.firstChild;
12196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12197         if(this.childrenRendered && of && node != of){
12198             of.renderIndent(true, true);
12199         }
12200         if(this.rendered){
12201             this.renderIndent(true, true);
12202         }
12203     },
12204
12205     // private override
12206     setLastChild : function(node){
12207         var ol = this.lastChild;
12208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12209         if(this.childrenRendered && ol && node != ol){
12210             ol.renderIndent(true, true);
12211         }
12212         if(this.rendered){
12213             this.renderIndent(true, true);
12214         }
12215     },
12216
12217     // these methods are overridden to provide lazy rendering support
12218     // private override
12219     appendChild : function()
12220     {
12221         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12222         if(node && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return node;
12227     },
12228
12229     // private override
12230     removeChild : function(node){
12231         this.ownerTree.getSelectionModel().unselect(node);
12232         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12233         // if it's been rendered remove dom node
12234         if(this.childrenRendered){
12235             node.ui.remove();
12236         }
12237         if(this.childNodes.length < 1){
12238             this.collapse(false, false);
12239         }else{
12240             this.ui.updateExpandIcon();
12241         }
12242         if(!this.firstChild) {
12243             this.childrenRendered = false;
12244         }
12245         return node;
12246     },
12247
12248     // private override
12249     insertBefore : function(node, refNode){
12250         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12251         if(newNode && refNode && this.childrenRendered){
12252             node.render();
12253         }
12254         this.ui.updateExpandIcon();
12255         return newNode;
12256     },
12257
12258     /**
12259      * Sets the text for this node
12260      * @param {String} text
12261      */
12262     setText : function(text){
12263         var oldText = this.text;
12264         this.text = text;
12265         this.attributes.text = text;
12266         if(this.rendered){ // event without subscribing
12267             this.ui.onTextChange(this, text, oldText);
12268         }
12269         this.fireEvent("textchange", this, text, oldText);
12270     },
12271
12272     /**
12273      * Triggers selection of this node
12274      */
12275     select : function(){
12276         this.getOwnerTree().getSelectionModel().select(this);
12277     },
12278
12279     /**
12280      * Triggers deselection of this node
12281      */
12282     unselect : function(){
12283         this.getOwnerTree().getSelectionModel().unselect(this);
12284     },
12285
12286     /**
12287      * Returns true if this node is selected
12288      * @return {Boolean}
12289      */
12290     isSelected : function(){
12291         return this.getOwnerTree().getSelectionModel().isSelected(this);
12292     },
12293
12294     /**
12295      * Expand this node.
12296      * @param {Boolean} deep (optional) True to expand all children as well
12297      * @param {Boolean} anim (optional) false to cancel the default animation
12298      * @param {Function} callback (optional) A callback to be called when
12299      * expanding this node completes (does not wait for deep expand to complete).
12300      * Called with 1 parameter, this node.
12301      */
12302     expand : function(deep, anim, callback){
12303         if(!this.expanded){
12304             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12305                 return;
12306             }
12307             if(!this.childrenRendered){
12308                 this.renderChildren();
12309             }
12310             this.expanded = true;
12311             
12312             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12313                 this.ui.animExpand(function(){
12314                     this.fireEvent("expand", this);
12315                     if(typeof callback == "function"){
12316                         callback(this);
12317                     }
12318                     if(deep === true){
12319                         this.expandChildNodes(true);
12320                     }
12321                 }.createDelegate(this));
12322                 return;
12323             }else{
12324                 this.ui.expand();
12325                 this.fireEvent("expand", this);
12326                 if(typeof callback == "function"){
12327                     callback(this);
12328                 }
12329             }
12330         }else{
12331            if(typeof callback == "function"){
12332                callback(this);
12333            }
12334         }
12335         if(deep === true){
12336             this.expandChildNodes(true);
12337         }
12338     },
12339
12340     isHiddenRoot : function(){
12341         return this.isRoot && !this.getOwnerTree().rootVisible;
12342     },
12343
12344     /**
12345      * Collapse this node.
12346      * @param {Boolean} deep (optional) True to collapse all children as well
12347      * @param {Boolean} anim (optional) false to cancel the default animation
12348      */
12349     collapse : function(deep, anim){
12350         if(this.expanded && !this.isHiddenRoot()){
12351             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12352                 return;
12353             }
12354             this.expanded = false;
12355             if((this.getOwnerTree().animate && anim !== false) || anim){
12356                 this.ui.animCollapse(function(){
12357                     this.fireEvent("collapse", this);
12358                     if(deep === true){
12359                         this.collapseChildNodes(true);
12360                     }
12361                 }.createDelegate(this));
12362                 return;
12363             }else{
12364                 this.ui.collapse();
12365                 this.fireEvent("collapse", this);
12366             }
12367         }
12368         if(deep === true){
12369             var cs = this.childNodes;
12370             for(var i = 0, len = cs.length; i < len; i++) {
12371                 cs[i].collapse(true, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     delayedExpand : function(delay){
12378         if(!this.expandProcId){
12379             this.expandProcId = this.expand.defer(delay, this);
12380         }
12381     },
12382
12383     // private
12384     cancelExpand : function(){
12385         if(this.expandProcId){
12386             clearTimeout(this.expandProcId);
12387         }
12388         this.expandProcId = false;
12389     },
12390
12391     /**
12392      * Toggles expanded/collapsed state of the node
12393      */
12394     toggle : function(){
12395         if(this.expanded){
12396             this.collapse();
12397         }else{
12398             this.expand();
12399         }
12400     },
12401
12402     /**
12403      * Ensures all parent nodes are expanded
12404      */
12405     ensureVisible : function(callback){
12406         var tree = this.getOwnerTree();
12407         tree.expandPath(this.parentNode.getPath(), false, function(){
12408             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12409             Roo.callback(callback);
12410         }.createDelegate(this));
12411     },
12412
12413     /**
12414      * Expand all child nodes
12415      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12416      */
12417     expandChildNodes : function(deep){
12418         var cs = this.childNodes;
12419         for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].expand(deep);
12421         }
12422     },
12423
12424     /**
12425      * Collapse all child nodes
12426      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12427      */
12428     collapseChildNodes : function(deep){
12429         var cs = this.childNodes;
12430         for(var i = 0, len = cs.length; i < len; i++) {
12431                 cs[i].collapse(deep);
12432         }
12433     },
12434
12435     /**
12436      * Disables this node
12437      */
12438     disable : function(){
12439         this.disabled = true;
12440         this.unselect();
12441         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12442             this.ui.onDisableChange(this, true);
12443         }
12444         this.fireEvent("disabledchange", this, true);
12445     },
12446
12447     /**
12448      * Enables this node
12449      */
12450     enable : function(){
12451         this.disabled = false;
12452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453             this.ui.onDisableChange(this, false);
12454         }
12455         this.fireEvent("disabledchange", this, false);
12456     },
12457
12458     // private
12459     renderChildren : function(suppressEvent){
12460         if(suppressEvent !== false){
12461             this.fireEvent("beforechildrenrendered", this);
12462         }
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++){
12465             cs[i].render(true);
12466         }
12467         this.childrenRendered = true;
12468     },
12469
12470     // private
12471     sort : function(fn, scope){
12472         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12473         if(this.childrenRendered){
12474             var cs = this.childNodes;
12475             for(var i = 0, len = cs.length; i < len; i++){
12476                 cs[i].render(true);
12477             }
12478         }
12479     },
12480
12481     // private
12482     render : function(bulkRender){
12483         this.ui.render(bulkRender);
12484         if(!this.rendered){
12485             this.rendered = true;
12486             if(this.expanded){
12487                 this.expanded = false;
12488                 this.expand(false, false);
12489             }
12490         }
12491     },
12492
12493     // private
12494     renderIndent : function(deep, refresh){
12495         if(refresh){
12496             this.ui.childIndent = null;
12497         }
12498         this.ui.renderIndent();
12499         if(deep === true && this.childrenRendered){
12500             var cs = this.childNodes;
12501             for(var i = 0, len = cs.length; i < len; i++){
12502                 cs[i].renderIndent(true, refresh);
12503             }
12504         }
12505     }
12506 });/*
12507  * Based on:
12508  * Ext JS Library 1.1.1
12509  * Copyright(c) 2006-2007, Ext JS, LLC.
12510  *
12511  * Originally Released Under LGPL - original licence link has changed is not relivant.
12512  *
12513  * Fork - LGPL
12514  * <script type="text/javascript">
12515  */
12516  
12517 /**
12518  * @class Roo.tree.AsyncTreeNode
12519  * @extends Roo.tree.TreeNode
12520  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12521  * @constructor
12522  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12523  */
12524  Roo.tree.AsyncTreeNode = function(config){
12525     this.loaded = false;
12526     this.loading = false;
12527     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12528     /**
12529     * @event beforeload
12530     * Fires before this node is loaded, return false to cancel
12531     * @param {Node} this This node
12532     */
12533     this.addEvents({'beforeload':true, 'load': true});
12534     /**
12535     * @event load
12536     * Fires when this node is loaded
12537     * @param {Node} this This node
12538     */
12539     /**
12540      * The loader used by this node (defaults to using the tree's defined loader)
12541      * @type TreeLoader
12542      * @property loader
12543      */
12544 };
12545 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12546     expand : function(deep, anim, callback){
12547         if(this.loading){ // if an async load is already running, waiting til it's done
12548             var timer;
12549             var f = function(){
12550                 if(!this.loading){ // done loading
12551                     clearInterval(timer);
12552                     this.expand(deep, anim, callback);
12553                 }
12554             }.createDelegate(this);
12555             timer = setInterval(f, 200);
12556             return;
12557         }
12558         if(!this.loaded){
12559             if(this.fireEvent("beforeload", this) === false){
12560                 return;
12561             }
12562             this.loading = true;
12563             this.ui.beforeLoad(this);
12564             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12565             if(loader){
12566                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12567                 return;
12568             }
12569         }
12570         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12571     },
12572     
12573     /**
12574      * Returns true if this node is currently loading
12575      * @return {Boolean}
12576      */
12577     isLoading : function(){
12578         return this.loading;  
12579     },
12580     
12581     loadComplete : function(deep, anim, callback){
12582         this.loading = false;
12583         this.loaded = true;
12584         this.ui.afterLoad(this);
12585         this.fireEvent("load", this);
12586         this.expand(deep, anim, callback);
12587     },
12588     
12589     /**
12590      * Returns true if this node has been loaded
12591      * @return {Boolean}
12592      */
12593     isLoaded : function(){
12594         return this.loaded;
12595     },
12596     
12597     hasChildNodes : function(){
12598         if(!this.isLeaf() && !this.loaded){
12599             return true;
12600         }else{
12601             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12602         }
12603     },
12604
12605     /**
12606      * Trigger a reload for this node
12607      * @param {Function} callback
12608      */
12609     reload : function(callback){
12610         this.collapse(false, false);
12611         while(this.firstChild){
12612             this.removeChild(this.firstChild);
12613         }
12614         this.childrenRendered = false;
12615         this.loaded = false;
12616         if(this.isHiddenRoot()){
12617             this.expanded = false;
12618         }
12619         this.expand(false, false, callback);
12620     }
12621 });/*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631  
12632 /**
12633  * @class Roo.tree.TreeNodeUI
12634  * @constructor
12635  * @param {Object} node The node to render
12636  * The TreeNode UI implementation is separate from the
12637  * tree implementation. Unless you are customizing the tree UI,
12638  * you should never have to use this directly.
12639  */
12640 Roo.tree.TreeNodeUI = function(node){
12641     this.node = node;
12642     this.rendered = false;
12643     this.animating = false;
12644     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12645 };
12646
12647 Roo.tree.TreeNodeUI.prototype = {
12648     removeChild : function(node){
12649         if(this.rendered){
12650             this.ctNode.removeChild(node.ui.getEl());
12651         }
12652     },
12653
12654     beforeLoad : function(){
12655          this.addClass("x-tree-node-loading");
12656     },
12657
12658     afterLoad : function(){
12659          this.removeClass("x-tree-node-loading");
12660     },
12661
12662     onTextChange : function(node, text, oldText){
12663         if(this.rendered){
12664             this.textNode.innerHTML = text;
12665         }
12666     },
12667
12668     onDisableChange : function(node, state){
12669         this.disabled = state;
12670         if(state){
12671             this.addClass("x-tree-node-disabled");
12672         }else{
12673             this.removeClass("x-tree-node-disabled");
12674         }
12675     },
12676
12677     onSelectedChange : function(state){
12678         if(state){
12679             this.focus();
12680             this.addClass("x-tree-selected");
12681         }else{
12682             //this.blur();
12683             this.removeClass("x-tree-selected");
12684         }
12685     },
12686
12687     onMove : function(tree, node, oldParent, newParent, index, refNode){
12688         this.childIndent = null;
12689         if(this.rendered){
12690             var targetNode = newParent.ui.getContainer();
12691             if(!targetNode){//target not rendered
12692                 this.holder = document.createElement("div");
12693                 this.holder.appendChild(this.wrap);
12694                 return;
12695             }
12696             var insertBefore = refNode ? refNode.ui.getEl() : null;
12697             if(insertBefore){
12698                 targetNode.insertBefore(this.wrap, insertBefore);
12699             }else{
12700                 targetNode.appendChild(this.wrap);
12701             }
12702             this.node.renderIndent(true);
12703         }
12704     },
12705
12706     addClass : function(cls){
12707         if(this.elNode){
12708             Roo.fly(this.elNode).addClass(cls);
12709         }
12710     },
12711
12712     removeClass : function(cls){
12713         if(this.elNode){
12714             Roo.fly(this.elNode).removeClass(cls);
12715         }
12716     },
12717
12718     remove : function(){
12719         if(this.rendered){
12720             this.holder = document.createElement("div");
12721             this.holder.appendChild(this.wrap);
12722         }
12723     },
12724
12725     fireEvent : function(){
12726         return this.node.fireEvent.apply(this.node, arguments);
12727     },
12728
12729     initEvents : function(){
12730         this.node.on("move", this.onMove, this);
12731         var E = Roo.EventManager;
12732         var a = this.anchor;
12733
12734         var el = Roo.fly(a, '_treeui');
12735
12736         if(Roo.isOpera){ // opera render bug ignores the CSS
12737             el.setStyle("text-decoration", "none");
12738         }
12739
12740         el.on("click", this.onClick, this);
12741         el.on("dblclick", this.onDblClick, this);
12742
12743         if(this.checkbox){
12744             Roo.EventManager.on(this.checkbox,
12745                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12746         }
12747
12748         el.on("contextmenu", this.onContextMenu, this);
12749
12750         var icon = Roo.fly(this.iconNode);
12751         icon.on("click", this.onClick, this);
12752         icon.on("dblclick", this.onDblClick, this);
12753         icon.on("contextmenu", this.onContextMenu, this);
12754         E.on(this.ecNode, "click", this.ecClick, this, true);
12755
12756         if(this.node.disabled){
12757             this.addClass("x-tree-node-disabled");
12758         }
12759         if(this.node.hidden){
12760             this.addClass("x-tree-node-disabled");
12761         }
12762         var ot = this.node.getOwnerTree();
12763         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12764         if(dd && (!this.node.isRoot || ot.rootVisible)){
12765             Roo.dd.Registry.register(this.elNode, {
12766                 node: this.node,
12767                 handles: this.getDDHandles(),
12768                 isHandle: false
12769             });
12770         }
12771     },
12772
12773     getDDHandles : function(){
12774         return [this.iconNode, this.textNode];
12775     },
12776
12777     hide : function(){
12778         if(this.rendered){
12779             this.wrap.style.display = "none";
12780         }
12781     },
12782
12783     show : function(){
12784         if(this.rendered){
12785             this.wrap.style.display = "";
12786         }
12787     },
12788
12789     onContextMenu : function(e){
12790         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12791             e.preventDefault();
12792             this.focus();
12793             this.fireEvent("contextmenu", this.node, e);
12794         }
12795     },
12796
12797     onClick : function(e){
12798         if(this.dropping){
12799             e.stopEvent();
12800             return;
12801         }
12802         if(this.fireEvent("beforeclick", this.node, e) !== false){
12803             if(!this.disabled && this.node.attributes.href){
12804                 this.fireEvent("click", this.node, e);
12805                 return;
12806             }
12807             e.preventDefault();
12808             if(this.disabled){
12809                 return;
12810             }
12811
12812             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12813                 this.node.toggle();
12814             }
12815
12816             this.fireEvent("click", this.node, e);
12817         }else{
12818             e.stopEvent();
12819         }
12820     },
12821
12822     onDblClick : function(e){
12823         e.preventDefault();
12824         if(this.disabled){
12825             return;
12826         }
12827         if(this.checkbox){
12828             this.toggleCheck();
12829         }
12830         if(!this.animating && this.node.hasChildNodes()){
12831             this.node.toggle();
12832         }
12833         this.fireEvent("dblclick", this.node, e);
12834     },
12835
12836     onCheckChange : function(){
12837         var checked = this.checkbox.checked;
12838         this.node.attributes.checked = checked;
12839         this.fireEvent('checkchange', this.node, checked);
12840     },
12841
12842     ecClick : function(e){
12843         if(!this.animating && this.node.hasChildNodes()){
12844             this.node.toggle();
12845         }
12846     },
12847
12848     startDrop : function(){
12849         this.dropping = true;
12850     },
12851
12852     // delayed drop so the click event doesn't get fired on a drop
12853     endDrop : function(){
12854        setTimeout(function(){
12855            this.dropping = false;
12856        }.createDelegate(this), 50);
12857     },
12858
12859     expand : function(){
12860         this.updateExpandIcon();
12861         this.ctNode.style.display = "";
12862     },
12863
12864     focus : function(){
12865         if(!this.node.preventHScroll){
12866             try{this.anchor.focus();
12867             }catch(e){}
12868         }else if(!Roo.isIE){
12869             try{
12870                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12871                 var l = noscroll.scrollLeft;
12872                 this.anchor.focus();
12873                 noscroll.scrollLeft = l;
12874             }catch(e){}
12875         }
12876     },
12877
12878     toggleCheck : function(value){
12879         var cb = this.checkbox;
12880         if(cb){
12881             cb.checked = (value === undefined ? !cb.checked : value);
12882         }
12883     },
12884
12885     blur : function(){
12886         try{
12887             this.anchor.blur();
12888         }catch(e){}
12889     },
12890
12891     animExpand : function(callback){
12892         var ct = Roo.get(this.ctNode);
12893         ct.stopFx();
12894         if(!this.node.hasChildNodes()){
12895             this.updateExpandIcon();
12896             this.ctNode.style.display = "";
12897             Roo.callback(callback);
12898             return;
12899         }
12900         this.animating = true;
12901         this.updateExpandIcon();
12902
12903         ct.slideIn('t', {
12904            callback : function(){
12905                this.animating = false;
12906                Roo.callback(callback);
12907             },
12908             scope: this,
12909             duration: this.node.ownerTree.duration || .25
12910         });
12911     },
12912
12913     highlight : function(){
12914         var tree = this.node.getOwnerTree();
12915         Roo.fly(this.wrap).highlight(
12916             tree.hlColor || "C3DAF9",
12917             {endColor: tree.hlBaseColor}
12918         );
12919     },
12920
12921     collapse : function(){
12922         this.updateExpandIcon();
12923         this.ctNode.style.display = "none";
12924     },
12925
12926     animCollapse : function(callback){
12927         var ct = Roo.get(this.ctNode);
12928         ct.enableDisplayMode('block');
12929         ct.stopFx();
12930
12931         this.animating = true;
12932         this.updateExpandIcon();
12933
12934         ct.slideOut('t', {
12935             callback : function(){
12936                this.animating = false;
12937                Roo.callback(callback);
12938             },
12939             scope: this,
12940             duration: this.node.ownerTree.duration || .25
12941         });
12942     },
12943
12944     getContainer : function(){
12945         return this.ctNode;
12946     },
12947
12948     getEl : function(){
12949         return this.wrap;
12950     },
12951
12952     appendDDGhost : function(ghostNode){
12953         ghostNode.appendChild(this.elNode.cloneNode(true));
12954     },
12955
12956     getDDRepairXY : function(){
12957         return Roo.lib.Dom.getXY(this.iconNode);
12958     },
12959
12960     onRender : function(){
12961         this.render();
12962     },
12963
12964     render : function(bulkRender){
12965         var n = this.node, a = n.attributes;
12966         var targetNode = n.parentNode ?
12967               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12968
12969         if(!this.rendered){
12970             this.rendered = true;
12971
12972             this.renderElements(n, a, targetNode, bulkRender);
12973
12974             if(a.qtip){
12975                if(this.textNode.setAttributeNS){
12976                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12977                    if(a.qtipTitle){
12978                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12979                    }
12980                }else{
12981                    this.textNode.setAttribute("ext:qtip", a.qtip);
12982                    if(a.qtipTitle){
12983                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12984                    }
12985                }
12986             }else if(a.qtipCfg){
12987                 a.qtipCfg.target = Roo.id(this.textNode);
12988                 Roo.QuickTips.register(a.qtipCfg);
12989             }
12990             this.initEvents();
12991             if(!this.node.expanded){
12992                 this.updateExpandIcon();
12993             }
12994         }else{
12995             if(bulkRender === true) {
12996                 targetNode.appendChild(this.wrap);
12997             }
12998         }
12999     },
13000
13001     renderElements : function(n, a, targetNode, bulkRender)
13002     {
13003         // add some indent caching, this helps performance when rendering a large tree
13004         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13005         var t = n.getOwnerTree();
13006         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13007         if (typeof(n.attributes.html) != 'undefined') {
13008             txt = n.attributes.html;
13009         }
13010         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13011         var cb = typeof a.checked == 'boolean';
13012         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13013         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13014             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13015             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13016             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13017             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13018             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13019              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13020                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13021             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13022             "</li>"];
13023
13024         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13025             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13026                                 n.nextSibling.ui.getEl(), buf.join(""));
13027         }else{
13028             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13029         }
13030
13031         this.elNode = this.wrap.childNodes[0];
13032         this.ctNode = this.wrap.childNodes[1];
13033         var cs = this.elNode.childNodes;
13034         this.indentNode = cs[0];
13035         this.ecNode = cs[1];
13036         this.iconNode = cs[2];
13037         var index = 3;
13038         if(cb){
13039             this.checkbox = cs[3];
13040             index++;
13041         }
13042         this.anchor = cs[index];
13043         this.textNode = cs[index].firstChild;
13044     },
13045
13046     getAnchor : function(){
13047         return this.anchor;
13048     },
13049
13050     getTextEl : function(){
13051         return this.textNode;
13052     },
13053
13054     getIconEl : function(){
13055         return this.iconNode;
13056     },
13057
13058     isChecked : function(){
13059         return this.checkbox ? this.checkbox.checked : false;
13060     },
13061
13062     updateExpandIcon : function(){
13063         if(this.rendered){
13064             var n = this.node, c1, c2;
13065             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13066             var hasChild = n.hasChildNodes();
13067             if(hasChild){
13068                 if(n.expanded){
13069                     cls += "-minus";
13070                     c1 = "x-tree-node-collapsed";
13071                     c2 = "x-tree-node-expanded";
13072                 }else{
13073                     cls += "-plus";
13074                     c1 = "x-tree-node-expanded";
13075                     c2 = "x-tree-node-collapsed";
13076                 }
13077                 if(this.wasLeaf){
13078                     this.removeClass("x-tree-node-leaf");
13079                     this.wasLeaf = false;
13080                 }
13081                 if(this.c1 != c1 || this.c2 != c2){
13082                     Roo.fly(this.elNode).replaceClass(c1, c2);
13083                     this.c1 = c1; this.c2 = c2;
13084                 }
13085             }else{
13086                 // this changes non-leafs into leafs if they have no children.
13087                 // it's not very rational behaviour..
13088                 
13089                 if(!this.wasLeaf && this.node.leaf){
13090                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13091                     delete this.c1;
13092                     delete this.c2;
13093                     this.wasLeaf = true;
13094                 }
13095             }
13096             var ecc = "x-tree-ec-icon "+cls;
13097             if(this.ecc != ecc){
13098                 this.ecNode.className = ecc;
13099                 this.ecc = ecc;
13100             }
13101         }
13102     },
13103
13104     getChildIndent : function(){
13105         if(!this.childIndent){
13106             var buf = [];
13107             var p = this.node;
13108             while(p){
13109                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13110                     if(!p.isLast()) {
13111                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13112                     } else {
13113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13114                     }
13115                 }
13116                 p = p.parentNode;
13117             }
13118             this.childIndent = buf.join("");
13119         }
13120         return this.childIndent;
13121     },
13122
13123     renderIndent : function(){
13124         if(this.rendered){
13125             var indent = "";
13126             var p = this.node.parentNode;
13127             if(p){
13128                 indent = p.ui.getChildIndent();
13129             }
13130             if(this.indentMarkup != indent){ // don't rerender if not required
13131                 this.indentNode.innerHTML = indent;
13132                 this.indentMarkup = indent;
13133             }
13134             this.updateExpandIcon();
13135         }
13136     }
13137 };
13138
13139 Roo.tree.RootTreeNodeUI = function(){
13140     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13141 };
13142 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13143     render : function(){
13144         if(!this.rendered){
13145             var targetNode = this.node.ownerTree.innerCt.dom;
13146             this.node.expanded = true;
13147             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13148             this.wrap = this.ctNode = targetNode.firstChild;
13149         }
13150     },
13151     collapse : function(){
13152     },
13153     expand : function(){
13154     }
13155 });/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165 /**
13166  * @class Roo.tree.TreeLoader
13167  * @extends Roo.util.Observable
13168  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13169  * nodes from a specified URL. The response must be a javascript Array definition
13170  * who's elements are node definition objects. eg:
13171  * <pre><code>
13172 {  success : true,
13173    data :      [
13174    
13175     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13176     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13177     ]
13178 }
13179
13180
13181 </code></pre>
13182  * <br><br>
13183  * The old style respose with just an array is still supported, but not recommended.
13184  * <br><br>
13185  *
13186  * A server request is sent, and child nodes are loaded only when a node is expanded.
13187  * The loading node's id is passed to the server under the parameter name "node" to
13188  * enable the server to produce the correct child nodes.
13189  * <br><br>
13190  * To pass extra parameters, an event handler may be attached to the "beforeload"
13191  * event, and the parameters specified in the TreeLoader's baseParams property:
13192  * <pre><code>
13193     myTreeLoader.on("beforeload", function(treeLoader, node) {
13194         this.baseParams.category = node.attributes.category;
13195     }, this);
13196     
13197 </code></pre>
13198  *
13199  * This would pass an HTTP parameter called "category" to the server containing
13200  * the value of the Node's "category" attribute.
13201  * @constructor
13202  * Creates a new Treeloader.
13203  * @param {Object} config A config object containing config properties.
13204  */
13205 Roo.tree.TreeLoader = function(config){
13206     this.baseParams = {};
13207     this.requestMethod = "POST";
13208     Roo.apply(this, config);
13209
13210     this.addEvents({
13211     
13212         /**
13213          * @event beforeload
13214          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13215          * @param {Object} This TreeLoader object.
13216          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13217          * @param {Object} callback The callback function specified in the {@link #load} call.
13218          */
13219         beforeload : true,
13220         /**
13221          * @event load
13222          * Fires when the node has been successfuly loaded.
13223          * @param {Object} This TreeLoader object.
13224          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13225          * @param {Object} response The response object containing the data from the server.
13226          */
13227         load : true,
13228         /**
13229          * @event loadexception
13230          * Fires if the network request failed.
13231          * @param {Object} This TreeLoader object.
13232          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13233          * @param {Object} response The response object containing the data from the server.
13234          */
13235         loadexception : true,
13236         /**
13237          * @event create
13238          * Fires before a node is created, enabling you to return custom Node types 
13239          * @param {Object} This TreeLoader object.
13240          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13241          */
13242         create : true
13243     });
13244
13245     Roo.tree.TreeLoader.superclass.constructor.call(this);
13246 };
13247
13248 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13249     /**
13250     * @cfg {String} dataUrl The URL from which to request a Json string which
13251     * specifies an array of node definition object representing the child nodes
13252     * to be loaded.
13253     */
13254     /**
13255     * @cfg {String} requestMethod either GET or POST
13256     * defaults to POST (due to BC)
13257     * to be loaded.
13258     */
13259     /**
13260     * @cfg {Object} baseParams (optional) An object containing properties which
13261     * specify HTTP parameters to be passed to each request for child nodes.
13262     */
13263     /**
13264     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13265     * created by this loader. If the attributes sent by the server have an attribute in this object,
13266     * they take priority.
13267     */
13268     /**
13269     * @cfg {Object} uiProviders (optional) An object containing properties which
13270     * 
13271     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13272     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13273     * <i>uiProvider</i> attribute of a returned child node is a string rather
13274     * than a reference to a TreeNodeUI implementation, this that string value
13275     * is used as a property name in the uiProviders object. You can define the provider named
13276     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13277     */
13278     uiProviders : {},
13279
13280     /**
13281     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13282     * child nodes before loading.
13283     */
13284     clearOnLoad : true,
13285
13286     /**
13287     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13288     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13289     * Grid query { data : [ .....] }
13290     */
13291     
13292     root : false,
13293      /**
13294     * @cfg {String} queryParam (optional) 
13295     * Name of the query as it will be passed on the querystring (defaults to 'node')
13296     * eg. the request will be ?node=[id]
13297     */
13298     
13299     
13300     queryParam: false,
13301     
13302     /**
13303      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13304      * This is called automatically when a node is expanded, but may be used to reload
13305      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13306      * @param {Roo.tree.TreeNode} node
13307      * @param {Function} callback
13308      */
13309     load : function(node, callback){
13310         if(this.clearOnLoad){
13311             while(node.firstChild){
13312                 node.removeChild(node.firstChild);
13313             }
13314         }
13315         if(node.attributes.children){ // preloaded json children
13316             var cs = node.attributes.children;
13317             for(var i = 0, len = cs.length; i < len; i++){
13318                 node.appendChild(this.createNode(cs[i]));
13319             }
13320             if(typeof callback == "function"){
13321                 callback();
13322             }
13323         }else if(this.dataUrl){
13324             this.requestData(node, callback);
13325         }
13326     },
13327
13328     getParams: function(node){
13329         var buf = [], bp = this.baseParams;
13330         for(var key in bp){
13331             if(typeof bp[key] != "function"){
13332                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13333             }
13334         }
13335         var n = this.queryParam === false ? 'node' : this.queryParam;
13336         buf.push(n + "=", encodeURIComponent(node.id));
13337         return buf.join("");
13338     },
13339
13340     requestData : function(node, callback){
13341         if(this.fireEvent("beforeload", this, node, callback) !== false){
13342             this.transId = Roo.Ajax.request({
13343                 method:this.requestMethod,
13344                 url: this.dataUrl||this.url,
13345                 success: this.handleResponse,
13346                 failure: this.handleFailure,
13347                 scope: this,
13348                 argument: {callback: callback, node: node},
13349                 params: this.getParams(node)
13350             });
13351         }else{
13352             // if the load is cancelled, make sure we notify
13353             // the node that we are done
13354             if(typeof callback == "function"){
13355                 callback();
13356             }
13357         }
13358     },
13359
13360     isLoading : function(){
13361         return this.transId ? true : false;
13362     },
13363
13364     abort : function(){
13365         if(this.isLoading()){
13366             Roo.Ajax.abort(this.transId);
13367         }
13368     },
13369
13370     // private
13371     createNode : function(attr)
13372     {
13373         // apply baseAttrs, nice idea Corey!
13374         if(this.baseAttrs){
13375             Roo.applyIf(attr, this.baseAttrs);
13376         }
13377         if(this.applyLoader !== false){
13378             attr.loader = this;
13379         }
13380         // uiProvider = depreciated..
13381         
13382         if(typeof(attr.uiProvider) == 'string'){
13383            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13384                 /**  eval:var:attr */ eval(attr.uiProvider);
13385         }
13386         if(typeof(this.uiProviders['default']) != 'undefined') {
13387             attr.uiProvider = this.uiProviders['default'];
13388         }
13389         
13390         this.fireEvent('create', this, attr);
13391         
13392         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13393         return(attr.leaf ?
13394                         new Roo.tree.TreeNode(attr) :
13395                         new Roo.tree.AsyncTreeNode(attr));
13396     },
13397
13398     processResponse : function(response, node, callback)
13399     {
13400         var json = response.responseText;
13401         try {
13402             
13403             var o = Roo.decode(json);
13404             
13405             if (this.root === false && typeof(o.success) != undefined) {
13406                 this.root = 'data'; // the default behaviour for list like data..
13407                 }
13408                 
13409             if (this.root !== false &&  !o.success) {
13410                 // it's a failure condition.
13411                 var a = response.argument;
13412                 this.fireEvent("loadexception", this, a.node, response);
13413                 Roo.log("Load failed - should have a handler really");
13414                 return;
13415             }
13416             
13417             
13418             
13419             if (this.root !== false) {
13420                  o = o[this.root];
13421             }
13422             
13423             for(var i = 0, len = o.length; i < len; i++){
13424                 var n = this.createNode(o[i]);
13425                 if(n){
13426                     node.appendChild(n);
13427                 }
13428             }
13429             if(typeof callback == "function"){
13430                 callback(this, node);
13431             }
13432         }catch(e){
13433             this.handleFailure(response);
13434         }
13435     },
13436
13437     handleResponse : function(response){
13438         this.transId = false;
13439         var a = response.argument;
13440         this.processResponse(response, a.node, a.callback);
13441         this.fireEvent("load", this, a.node, response);
13442     },
13443
13444     handleFailure : function(response)
13445     {
13446         // should handle failure better..
13447         this.transId = false;
13448         var a = response.argument;
13449         this.fireEvent("loadexception", this, a.node, response);
13450         if(typeof a.callback == "function"){
13451             a.callback(this, a.node);
13452         }
13453     }
13454 });/*
13455  * Based on:
13456  * Ext JS Library 1.1.1
13457  * Copyright(c) 2006-2007, Ext JS, LLC.
13458  *
13459  * Originally Released Under LGPL - original licence link has changed is not relivant.
13460  *
13461  * Fork - LGPL
13462  * <script type="text/javascript">
13463  */
13464
13465 /**
13466 * @class Roo.tree.TreeFilter
13467 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13468 * @param {TreePanel} tree
13469 * @param {Object} config (optional)
13470  */
13471 Roo.tree.TreeFilter = function(tree, config){
13472     this.tree = tree;
13473     this.filtered = {};
13474     Roo.apply(this, config);
13475 };
13476
13477 Roo.tree.TreeFilter.prototype = {
13478     clearBlank:false,
13479     reverse:false,
13480     autoClear:false,
13481     remove:false,
13482
13483      /**
13484      * Filter the data by a specific attribute.
13485      * @param {String/RegExp} value Either string that the attribute value
13486      * should start with or a RegExp to test against the attribute
13487      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13488      * @param {TreeNode} startNode (optional) The node to start the filter at.
13489      */
13490     filter : function(value, attr, startNode){
13491         attr = attr || "text";
13492         var f;
13493         if(typeof value == "string"){
13494             var vlen = value.length;
13495             // auto clear empty filter
13496             if(vlen == 0 && this.clearBlank){
13497                 this.clear();
13498                 return;
13499             }
13500             value = value.toLowerCase();
13501             f = function(n){
13502                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13503             };
13504         }else if(value.exec){ // regex?
13505             f = function(n){
13506                 return value.test(n.attributes[attr]);
13507             };
13508         }else{
13509             throw 'Illegal filter type, must be string or regex';
13510         }
13511         this.filterBy(f, null, startNode);
13512         },
13513
13514     /**
13515      * Filter by a function. The passed function will be called with each
13516      * node in the tree (or from the startNode). If the function returns true, the node is kept
13517      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13518      * @param {Function} fn The filter function
13519      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13520      */
13521     filterBy : function(fn, scope, startNode){
13522         startNode = startNode || this.tree.root;
13523         if(this.autoClear){
13524             this.clear();
13525         }
13526         var af = this.filtered, rv = this.reverse;
13527         var f = function(n){
13528             if(n == startNode){
13529                 return true;
13530             }
13531             if(af[n.id]){
13532                 return false;
13533             }
13534             var m = fn.call(scope || n, n);
13535             if(!m || rv){
13536                 af[n.id] = n;
13537                 n.ui.hide();
13538                 return false;
13539             }
13540             return true;
13541         };
13542         startNode.cascade(f);
13543         if(this.remove){
13544            for(var id in af){
13545                if(typeof id != "function"){
13546                    var n = af[id];
13547                    if(n && n.parentNode){
13548                        n.parentNode.removeChild(n);
13549                    }
13550                }
13551            }
13552         }
13553     },
13554
13555     /**
13556      * Clears the current filter. Note: with the "remove" option
13557      * set a filter cannot be cleared.
13558      */
13559     clear : function(){
13560         var t = this.tree;
13561         var af = this.filtered;
13562         for(var id in af){
13563             if(typeof id != "function"){
13564                 var n = af[id];
13565                 if(n){
13566                     n.ui.show();
13567                 }
13568             }
13569         }
13570         this.filtered = {};
13571     }
13572 };
13573 /*
13574  * Based on:
13575  * Ext JS Library 1.1.1
13576  * Copyright(c) 2006-2007, Ext JS, LLC.
13577  *
13578  * Originally Released Under LGPL - original licence link has changed is not relivant.
13579  *
13580  * Fork - LGPL
13581  * <script type="text/javascript">
13582  */
13583  
13584
13585 /**
13586  * @class Roo.tree.TreeSorter
13587  * Provides sorting of nodes in a TreePanel
13588  * 
13589  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13590  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13591  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13592  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13593  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13594  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13595  * @constructor
13596  * @param {TreePanel} tree
13597  * @param {Object} config
13598  */
13599 Roo.tree.TreeSorter = function(tree, config){
13600     Roo.apply(this, config);
13601     tree.on("beforechildrenrendered", this.doSort, this);
13602     tree.on("append", this.updateSort, this);
13603     tree.on("insert", this.updateSort, this);
13604     
13605     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13606     var p = this.property || "text";
13607     var sortType = this.sortType;
13608     var fs = this.folderSort;
13609     var cs = this.caseSensitive === true;
13610     var leafAttr = this.leafAttr || 'leaf';
13611
13612     this.sortFn = function(n1, n2){
13613         if(fs){
13614             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13615                 return 1;
13616             }
13617             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13618                 return -1;
13619             }
13620         }
13621         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13622         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13623         if(v1 < v2){
13624                         return dsc ? +1 : -1;
13625                 }else if(v1 > v2){
13626                         return dsc ? -1 : +1;
13627         }else{
13628                 return 0;
13629         }
13630     };
13631 };
13632
13633 Roo.tree.TreeSorter.prototype = {
13634     doSort : function(node){
13635         node.sort(this.sortFn);
13636     },
13637     
13638     compareNodes : function(n1, n2){
13639         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13640     },
13641     
13642     updateSort : function(tree, node){
13643         if(node.childrenRendered){
13644             this.doSort.defer(1, this, [node]);
13645         }
13646     }
13647 };/*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657
13658 if(Roo.dd.DropZone){
13659     
13660 Roo.tree.TreeDropZone = function(tree, config){
13661     this.allowParentInsert = false;
13662     this.allowContainerDrop = false;
13663     this.appendOnly = false;
13664     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13665     this.tree = tree;
13666     this.lastInsertClass = "x-tree-no-status";
13667     this.dragOverData = {};
13668 };
13669
13670 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13671     ddGroup : "TreeDD",
13672     scroll:  true,
13673     
13674     expandDelay : 1000,
13675     
13676     expandNode : function(node){
13677         if(node.hasChildNodes() && !node.isExpanded()){
13678             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13679         }
13680     },
13681     
13682     queueExpand : function(node){
13683         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13684     },
13685     
13686     cancelExpand : function(){
13687         if(this.expandProcId){
13688             clearTimeout(this.expandProcId);
13689             this.expandProcId = false;
13690         }
13691     },
13692     
13693     isValidDropPoint : function(n, pt, dd, e, data){
13694         if(!n || !data){ return false; }
13695         var targetNode = n.node;
13696         var dropNode = data.node;
13697         // default drop rules
13698         if(!(targetNode && targetNode.isTarget && pt)){
13699             return false;
13700         }
13701         if(pt == "append" && targetNode.allowChildren === false){
13702             return false;
13703         }
13704         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13705             return false;
13706         }
13707         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13708             return false;
13709         }
13710         // reuse the object
13711         var overEvent = this.dragOverData;
13712         overEvent.tree = this.tree;
13713         overEvent.target = targetNode;
13714         overEvent.data = data;
13715         overEvent.point = pt;
13716         overEvent.source = dd;
13717         overEvent.rawEvent = e;
13718         overEvent.dropNode = dropNode;
13719         overEvent.cancel = false;  
13720         var result = this.tree.fireEvent("nodedragover", overEvent);
13721         return overEvent.cancel === false && result !== false;
13722     },
13723     
13724     getDropPoint : function(e, n, dd)
13725     {
13726         var tn = n.node;
13727         if(tn.isRoot){
13728             return tn.allowChildren !== false ? "append" : false; // always append for root
13729         }
13730         var dragEl = n.ddel;
13731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13732         var y = Roo.lib.Event.getPageY(e);
13733         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13734         
13735         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13736         var noAppend = tn.allowChildren === false;
13737         if(this.appendOnly || tn.parentNode.allowChildren === false){
13738             return noAppend ? false : "append";
13739         }
13740         var noBelow = false;
13741         if(!this.allowParentInsert){
13742             noBelow = tn.hasChildNodes() && tn.isExpanded();
13743         }
13744         var q = (b - t) / (noAppend ? 2 : 3);
13745         if(y >= t && y < (t + q)){
13746             return "above";
13747         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13748             return "below";
13749         }else{
13750             return "append";
13751         }
13752     },
13753     
13754     onNodeEnter : function(n, dd, e, data)
13755     {
13756         this.cancelExpand();
13757     },
13758     
13759     onNodeOver : function(n, dd, e, data)
13760     {
13761        
13762         var pt = this.getDropPoint(e, n, dd);
13763         var node = n.node;
13764         
13765         // auto node expand check
13766         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13767             this.queueExpand(node);
13768         }else if(pt != "append"){
13769             this.cancelExpand();
13770         }
13771         
13772         // set the insert point style on the target node
13773         var returnCls = this.dropNotAllowed;
13774         if(this.isValidDropPoint(n, pt, dd, e, data)){
13775            if(pt){
13776                var el = n.ddel;
13777                var cls;
13778                if(pt == "above"){
13779                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13780                    cls = "x-tree-drag-insert-above";
13781                }else if(pt == "below"){
13782                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13783                    cls = "x-tree-drag-insert-below";
13784                }else{
13785                    returnCls = "x-tree-drop-ok-append";
13786                    cls = "x-tree-drag-append";
13787                }
13788                if(this.lastInsertClass != cls){
13789                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13790                    this.lastInsertClass = cls;
13791                }
13792            }
13793        }
13794        return returnCls;
13795     },
13796     
13797     onNodeOut : function(n, dd, e, data){
13798         
13799         this.cancelExpand();
13800         this.removeDropIndicators(n);
13801     },
13802     
13803     onNodeDrop : function(n, dd, e, data){
13804         var point = this.getDropPoint(e, n, dd);
13805         var targetNode = n.node;
13806         targetNode.ui.startDrop();
13807         if(!this.isValidDropPoint(n, point, dd, e, data)){
13808             targetNode.ui.endDrop();
13809             return false;
13810         }
13811         // first try to find the drop node
13812         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13813         var dropEvent = {
13814             tree : this.tree,
13815             target: targetNode,
13816             data: data,
13817             point: point,
13818             source: dd,
13819             rawEvent: e,
13820             dropNode: dropNode,
13821             cancel: !dropNode   
13822         };
13823         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13824         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13825             targetNode.ui.endDrop();
13826             return false;
13827         }
13828         // allow target changing
13829         targetNode = dropEvent.target;
13830         if(point == "append" && !targetNode.isExpanded()){
13831             targetNode.expand(false, null, function(){
13832                 this.completeDrop(dropEvent);
13833             }.createDelegate(this));
13834         }else{
13835             this.completeDrop(dropEvent);
13836         }
13837         return true;
13838     },
13839     
13840     completeDrop : function(de){
13841         var ns = de.dropNode, p = de.point, t = de.target;
13842         if(!(ns instanceof Array)){
13843             ns = [ns];
13844         }
13845         var n;
13846         for(var i = 0, len = ns.length; i < len; i++){
13847             n = ns[i];
13848             if(p == "above"){
13849                 t.parentNode.insertBefore(n, t);
13850             }else if(p == "below"){
13851                 t.parentNode.insertBefore(n, t.nextSibling);
13852             }else{
13853                 t.appendChild(n);
13854             }
13855         }
13856         n.ui.focus();
13857         if(this.tree.hlDrop){
13858             n.ui.highlight();
13859         }
13860         t.ui.endDrop();
13861         this.tree.fireEvent("nodedrop", de);
13862     },
13863     
13864     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13865         if(this.tree.hlDrop){
13866             dropNode.ui.focus();
13867             dropNode.ui.highlight();
13868         }
13869         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13870     },
13871     
13872     getTree : function(){
13873         return this.tree;
13874     },
13875     
13876     removeDropIndicators : function(n){
13877         if(n && n.ddel){
13878             var el = n.ddel;
13879             Roo.fly(el).removeClass([
13880                     "x-tree-drag-insert-above",
13881                     "x-tree-drag-insert-below",
13882                     "x-tree-drag-append"]);
13883             this.lastInsertClass = "_noclass";
13884         }
13885     },
13886     
13887     beforeDragDrop : function(target, e, id){
13888         this.cancelExpand();
13889         return true;
13890     },
13891     
13892     afterRepair : function(data){
13893         if(data && Roo.enableFx){
13894             data.node.ui.highlight();
13895         }
13896         this.hideProxy();
13897     } 
13898     
13899 });
13900
13901 }
13902 /*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912  
13913
13914 if(Roo.dd.DragZone){
13915 Roo.tree.TreeDragZone = function(tree, config){
13916     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13917     this.tree = tree;
13918 };
13919
13920 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13921     ddGroup : "TreeDD",
13922    
13923     onBeforeDrag : function(data, e){
13924         var n = data.node;
13925         return n && n.draggable && !n.disabled;
13926     },
13927      
13928     
13929     onInitDrag : function(e){
13930         var data = this.dragData;
13931         this.tree.getSelectionModel().select(data.node);
13932         this.proxy.update("");
13933         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13934         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13935     },
13936     
13937     getRepairXY : function(e, data){
13938         return data.node.ui.getDDRepairXY();
13939     },
13940     
13941     onEndDrag : function(data, e){
13942         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13943         
13944         
13945     },
13946     
13947     onValidDrop : function(dd, e, id){
13948         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13949         this.hideProxy();
13950     },
13951     
13952     beforeInvalidDrop : function(e, id){
13953         // this scrolls the original position back into view
13954         var sm = this.tree.getSelectionModel();
13955         sm.clearSelections();
13956         sm.select(this.dragData.node);
13957     }
13958 });
13959 }/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969 /**
13970  * @class Roo.tree.TreeEditor
13971  * @extends Roo.Editor
13972  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13973  * as the editor field.
13974  * @constructor
13975  * @param {Object} config (used to be the tree panel.)
13976  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13977  * 
13978  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13979  * @cfg {Roo.form.TextField|Object} field The field configuration
13980  *
13981  * 
13982  */
13983 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13984     var tree = config;
13985     var field;
13986     if (oldconfig) { // old style..
13987         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13988     } else {
13989         // new style..
13990         tree = config.tree;
13991         config.field = config.field  || {};
13992         config.field.xtype = 'TextField';
13993         field = Roo.factory(config.field, Roo.form);
13994     }
13995     config = config || {};
13996     
13997     
13998     this.addEvents({
13999         /**
14000          * @event beforenodeedit
14001          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14002          * false from the handler of this event.
14003          * @param {Editor} this
14004          * @param {Roo.tree.Node} node 
14005          */
14006         "beforenodeedit" : true
14007     });
14008     
14009     //Roo.log(config);
14010     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14011
14012     this.tree = tree;
14013
14014     tree.on('beforeclick', this.beforeNodeClick, this);
14015     tree.getTreeEl().on('mousedown', this.hide, this);
14016     this.on('complete', this.updateNode, this);
14017     this.on('beforestartedit', this.fitToTree, this);
14018     this.on('startedit', this.bindScroll, this, {delay:10});
14019     this.on('specialkey', this.onSpecialKey, this);
14020 };
14021
14022 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14023     /**
14024      * @cfg {String} alignment
14025      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14026      */
14027     alignment: "l-l",
14028     // inherit
14029     autoSize: false,
14030     /**
14031      * @cfg {Boolean} hideEl
14032      * True to hide the bound element while the editor is displayed (defaults to false)
14033      */
14034     hideEl : false,
14035     /**
14036      * @cfg {String} cls
14037      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14038      */
14039     cls: "x-small-editor x-tree-editor",
14040     /**
14041      * @cfg {Boolean} shim
14042      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14043      */
14044     shim:false,
14045     // inherit
14046     shadow:"frame",
14047     /**
14048      * @cfg {Number} maxWidth
14049      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14050      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14051      * scroll and client offsets into account prior to each edit.
14052      */
14053     maxWidth: 250,
14054
14055     editDelay : 350,
14056
14057     // private
14058     fitToTree : function(ed, el){
14059         var td = this.tree.getTreeEl().dom, nd = el.dom;
14060         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14061             td.scrollLeft = nd.offsetLeft;
14062         }
14063         var w = Math.min(
14064                 this.maxWidth,
14065                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14066         this.setSize(w, '');
14067         
14068         return this.fireEvent('beforenodeedit', this, this.editNode);
14069         
14070     },
14071
14072     // private
14073     triggerEdit : function(node){
14074         this.completeEdit();
14075         this.editNode = node;
14076         this.startEdit(node.ui.textNode, node.text);
14077     },
14078
14079     // private
14080     bindScroll : function(){
14081         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14082     },
14083
14084     // private
14085     beforeNodeClick : function(node, e){
14086         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14087         this.lastClick = new Date();
14088         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14089             e.stopEvent();
14090             this.triggerEdit(node);
14091             return false;
14092         }
14093         return true;
14094     },
14095
14096     // private
14097     updateNode : function(ed, value){
14098         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14099         this.editNode.setText(value);
14100     },
14101
14102     // private
14103     onHide : function(){
14104         Roo.tree.TreeEditor.superclass.onHide.call(this);
14105         if(this.editNode){
14106             this.editNode.ui.focus();
14107         }
14108     },
14109
14110     // private
14111     onSpecialKey : function(field, e){
14112         var k = e.getKey();
14113         if(k == e.ESC){
14114             e.stopEvent();
14115             this.cancelEdit();
14116         }else if(k == e.ENTER && !e.hasModifier()){
14117             e.stopEvent();
14118             this.completeEdit();
14119         }
14120     }
14121 });//<Script type="text/javascript">
14122 /*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132  
14133 /**
14134  * Not documented??? - probably should be...
14135  */
14136
14137 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14138     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14139     
14140     renderElements : function(n, a, targetNode, bulkRender){
14141         //consel.log("renderElements?");
14142         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14143
14144         var t = n.getOwnerTree();
14145         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14146         
14147         var cols = t.columns;
14148         var bw = t.borderWidth;
14149         var c = cols[0];
14150         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14151          var cb = typeof a.checked == "boolean";
14152         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14153         var colcls = 'x-t-' + tid + '-c0';
14154         var buf = [
14155             '<li class="x-tree-node">',
14156             
14157                 
14158                 '<div class="x-tree-node-el ', a.cls,'">',
14159                     // extran...
14160                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14161                 
14162                 
14163                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14164                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14165                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14166                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14167                            (a.iconCls ? ' '+a.iconCls : ''),
14168                            '" unselectable="on" />',
14169                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14170                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14171                              
14172                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14174                             '<span unselectable="on" qtip="' + tx + '">',
14175                              tx,
14176                              '</span></a>' ,
14177                     '</div>',
14178                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14179                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14180                  ];
14181         for(var i = 1, len = cols.length; i < len; i++){
14182             c = cols[i];
14183             colcls = 'x-t-' + tid + '-c' +i;
14184             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14185             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14186                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14187                       "</div>");
14188          }
14189          
14190          buf.push(
14191             '</a>',
14192             '<div class="x-clear"></div></div>',
14193             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14194             "</li>");
14195         
14196         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14197             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14198                                 n.nextSibling.ui.getEl(), buf.join(""));
14199         }else{
14200             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14201         }
14202         var el = this.wrap.firstChild;
14203         this.elRow = el;
14204         this.elNode = el.firstChild;
14205         this.ranchor = el.childNodes[1];
14206         this.ctNode = this.wrap.childNodes[1];
14207         var cs = el.firstChild.childNodes;
14208         this.indentNode = cs[0];
14209         this.ecNode = cs[1];
14210         this.iconNode = cs[2];
14211         var index = 3;
14212         if(cb){
14213             this.checkbox = cs[3];
14214             index++;
14215         }
14216         this.anchor = cs[index];
14217         
14218         this.textNode = cs[index].firstChild;
14219         
14220         //el.on("click", this.onClick, this);
14221         //el.on("dblclick", this.onDblClick, this);
14222         
14223         
14224        // console.log(this);
14225     },
14226     initEvents : function(){
14227         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14228         
14229             
14230         var a = this.ranchor;
14231
14232         var el = Roo.get(a);
14233
14234         if(Roo.isOpera){ // opera render bug ignores the CSS
14235             el.setStyle("text-decoration", "none");
14236         }
14237
14238         el.on("click", this.onClick, this);
14239         el.on("dblclick", this.onDblClick, this);
14240         el.on("contextmenu", this.onContextMenu, this);
14241         
14242     },
14243     
14244     /*onSelectedChange : function(state){
14245         if(state){
14246             this.focus();
14247             this.addClass("x-tree-selected");
14248         }else{
14249             //this.blur();
14250             this.removeClass("x-tree-selected");
14251         }
14252     },*/
14253     addClass : function(cls){
14254         if(this.elRow){
14255             Roo.fly(this.elRow).addClass(cls);
14256         }
14257         
14258     },
14259     
14260     
14261     removeClass : function(cls){
14262         if(this.elRow){
14263             Roo.fly(this.elRow).removeClass(cls);
14264         }
14265     }
14266
14267     
14268     
14269 });//<Script type="text/javascript">
14270
14271 /*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281  
14282
14283 /**
14284  * @class Roo.tree.ColumnTree
14285  * @extends Roo.data.TreePanel
14286  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14287  * @cfg {int} borderWidth  compined right/left border allowance
14288  * @constructor
14289  * @param {String/HTMLElement/Element} el The container element
14290  * @param {Object} config
14291  */
14292 Roo.tree.ColumnTree =  function(el, config)
14293 {
14294    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14295    this.addEvents({
14296         /**
14297         * @event resize
14298         * Fire this event on a container when it resizes
14299         * @param {int} w Width
14300         * @param {int} h Height
14301         */
14302        "resize" : true
14303     });
14304     this.on('resize', this.onResize, this);
14305 };
14306
14307 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14308     //lines:false,
14309     
14310     
14311     borderWidth: Roo.isBorderBox ? 0 : 2, 
14312     headEls : false,
14313     
14314     render : function(){
14315         // add the header.....
14316        
14317         Roo.tree.ColumnTree.superclass.render.apply(this);
14318         
14319         this.el.addClass('x-column-tree');
14320         
14321         this.headers = this.el.createChild(
14322             {cls:'x-tree-headers'},this.innerCt.dom);
14323    
14324         var cols = this.columns, c;
14325         var totalWidth = 0;
14326         this.headEls = [];
14327         var  len = cols.length;
14328         for(var i = 0; i < len; i++){
14329              c = cols[i];
14330              totalWidth += c.width;
14331             this.headEls.push(this.headers.createChild({
14332                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14333                  cn: {
14334                      cls:'x-tree-hd-text',
14335                      html: c.header
14336                  },
14337                  style:'width:'+(c.width-this.borderWidth)+'px;'
14338              }));
14339         }
14340         this.headers.createChild({cls:'x-clear'});
14341         // prevent floats from wrapping when clipped
14342         this.headers.setWidth(totalWidth);
14343         //this.innerCt.setWidth(totalWidth);
14344         this.innerCt.setStyle({ overflow: 'auto' });
14345         this.onResize(this.width, this.height);
14346              
14347         
14348     },
14349     onResize : function(w,h)
14350     {
14351         this.height = h;
14352         this.width = w;
14353         // resize cols..
14354         this.innerCt.setWidth(this.width);
14355         this.innerCt.setHeight(this.height-20);
14356         
14357         // headers...
14358         var cols = this.columns, c;
14359         var totalWidth = 0;
14360         var expEl = false;
14361         var len = cols.length;
14362         for(var i = 0; i < len; i++){
14363             c = cols[i];
14364             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14365                 // it's the expander..
14366                 expEl  = this.headEls[i];
14367                 continue;
14368             }
14369             totalWidth += c.width;
14370             
14371         }
14372         if (expEl) {
14373             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14374         }
14375         this.headers.setWidth(w-20);
14376
14377         
14378         
14379         
14380     }
14381 });
14382 /*
14383  * Based on:
14384  * Ext JS Library 1.1.1
14385  * Copyright(c) 2006-2007, Ext JS, LLC.
14386  *
14387  * Originally Released Under LGPL - original licence link has changed is not relivant.
14388  *
14389  * Fork - LGPL
14390  * <script type="text/javascript">
14391  */
14392  
14393 /**
14394  * @class Roo.menu.Menu
14395  * @extends Roo.util.Observable
14396  * @children Roo.menu.BaseItem
14397  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14398  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14399  * @constructor
14400  * Creates a new Menu
14401  * @param {Object} config Configuration options
14402  */
14403 Roo.menu.Menu = function(config){
14404     
14405     Roo.menu.Menu.superclass.constructor.call(this, config);
14406     
14407     this.id = this.id || Roo.id();
14408     this.addEvents({
14409         /**
14410          * @event beforeshow
14411          * Fires before this menu is displayed
14412          * @param {Roo.menu.Menu} this
14413          */
14414         beforeshow : true,
14415         /**
14416          * @event beforehide
14417          * Fires before this menu is hidden
14418          * @param {Roo.menu.Menu} this
14419          */
14420         beforehide : true,
14421         /**
14422          * @event show
14423          * Fires after this menu is displayed
14424          * @param {Roo.menu.Menu} this
14425          */
14426         show : true,
14427         /**
14428          * @event hide
14429          * Fires after this menu is hidden
14430          * @param {Roo.menu.Menu} this
14431          */
14432         hide : true,
14433         /**
14434          * @event click
14435          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14436          * @param {Roo.menu.Menu} this
14437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14438          * @param {Roo.EventObject} e
14439          */
14440         click : true,
14441         /**
14442          * @event mouseover
14443          * Fires when the mouse is hovering over this menu
14444          * @param {Roo.menu.Menu} this
14445          * @param {Roo.EventObject} e
14446          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14447          */
14448         mouseover : true,
14449         /**
14450          * @event mouseout
14451          * Fires when the mouse exits this menu
14452          * @param {Roo.menu.Menu} this
14453          * @param {Roo.EventObject} e
14454          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14455          */
14456         mouseout : true,
14457         /**
14458          * @event itemclick
14459          * Fires when a menu item contained in this menu is clicked
14460          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14461          * @param {Roo.EventObject} e
14462          */
14463         itemclick: true
14464     });
14465     if (this.registerMenu) {
14466         Roo.menu.MenuMgr.register(this);
14467     }
14468     
14469     var mis = this.items;
14470     this.items = new Roo.util.MixedCollection();
14471     if(mis){
14472         this.add.apply(this, mis);
14473     }
14474 };
14475
14476 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14477     /**
14478      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14479      */
14480     minWidth : 120,
14481     /**
14482      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14483      * for bottom-right shadow (defaults to "sides")
14484      */
14485     shadow : "sides",
14486     /**
14487      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14488      * this menu (defaults to "tl-tr?")
14489      */
14490     subMenuAlign : "tl-tr?",
14491     /**
14492      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14493      * relative to its element of origin (defaults to "tl-bl?")
14494      */
14495     defaultAlign : "tl-bl?",
14496     /**
14497      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14498      */
14499     allowOtherMenus : false,
14500     /**
14501      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14502      */
14503     registerMenu : true,
14504
14505     hidden:true,
14506
14507     // private
14508     render : function(){
14509         if(this.el){
14510             return;
14511         }
14512         var el = this.el = new Roo.Layer({
14513             cls: "x-menu",
14514             shadow:this.shadow,
14515             constrain: false,
14516             parentEl: this.parentEl || document.body,
14517             zindex:15000
14518         });
14519
14520         this.keyNav = new Roo.menu.MenuNav(this);
14521
14522         if(this.plain){
14523             el.addClass("x-menu-plain");
14524         }
14525         if(this.cls){
14526             el.addClass(this.cls);
14527         }
14528         // generic focus element
14529         this.focusEl = el.createChild({
14530             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14531         });
14532         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14533         //disabling touch- as it's causing issues ..
14534         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14535         ul.on('click'   , this.onClick, this);
14536         
14537         
14538         ul.on("mouseover", this.onMouseOver, this);
14539         ul.on("mouseout", this.onMouseOut, this);
14540         this.items.each(function(item){
14541             if (item.hidden) {
14542                 return;
14543             }
14544             
14545             var li = document.createElement("li");
14546             li.className = "x-menu-list-item";
14547             ul.dom.appendChild(li);
14548             item.render(li, this);
14549         }, this);
14550         this.ul = ul;
14551         this.autoWidth();
14552     },
14553
14554     // private
14555     autoWidth : function(){
14556         var el = this.el, ul = this.ul;
14557         if(!el){
14558             return;
14559         }
14560         var w = this.width;
14561         if(w){
14562             el.setWidth(w);
14563         }else if(Roo.isIE){
14564             el.setWidth(this.minWidth);
14565             var t = el.dom.offsetWidth; // force recalc
14566             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14567         }
14568     },
14569
14570     // private
14571     delayAutoWidth : function(){
14572         if(this.rendered){
14573             if(!this.awTask){
14574                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14575             }
14576             this.awTask.delay(20);
14577         }
14578     },
14579
14580     // private
14581     findTargetItem : function(e){
14582         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14583         if(t && t.menuItemId){
14584             return this.items.get(t.menuItemId);
14585         }
14586     },
14587
14588     // private
14589     onClick : function(e){
14590         Roo.log("menu.onClick");
14591         var t = this.findTargetItem(e);
14592         if(!t){
14593             return;
14594         }
14595         Roo.log(e);
14596         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14597             if(t == this.activeItem && t.shouldDeactivate(e)){
14598                 this.activeItem.deactivate();
14599                 delete this.activeItem;
14600                 return;
14601             }
14602             if(t.canActivate){
14603                 this.setActiveItem(t, true);
14604             }
14605             return;
14606             
14607             
14608         }
14609         
14610         t.onClick(e);
14611         this.fireEvent("click", this, t, e);
14612     },
14613
14614     // private
14615     setActiveItem : function(item, autoExpand){
14616         if(item != this.activeItem){
14617             if(this.activeItem){
14618                 this.activeItem.deactivate();
14619             }
14620             this.activeItem = item;
14621             item.activate(autoExpand);
14622         }else if(autoExpand){
14623             item.expandMenu();
14624         }
14625     },
14626
14627     // private
14628     tryActivate : function(start, step){
14629         var items = this.items;
14630         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14631             var item = items.get(i);
14632             if(!item.disabled && item.canActivate){
14633                 this.setActiveItem(item, false);
14634                 return item;
14635             }
14636         }
14637         return false;
14638     },
14639
14640     // private
14641     onMouseOver : function(e){
14642         var t;
14643         if(t = this.findTargetItem(e)){
14644             if(t.canActivate && !t.disabled){
14645                 this.setActiveItem(t, true);
14646             }
14647         }
14648         this.fireEvent("mouseover", this, e, t);
14649     },
14650
14651     // private
14652     onMouseOut : function(e){
14653         var t;
14654         if(t = this.findTargetItem(e)){
14655             if(t == this.activeItem && t.shouldDeactivate(e)){
14656                 this.activeItem.deactivate();
14657                 delete this.activeItem;
14658             }
14659         }
14660         this.fireEvent("mouseout", this, e, t);
14661     },
14662
14663     /**
14664      * Read-only.  Returns true if the menu is currently displayed, else false.
14665      * @type Boolean
14666      */
14667     isVisible : function(){
14668         return this.el && !this.hidden;
14669     },
14670
14671     /**
14672      * Displays this menu relative to another element
14673      * @param {String/HTMLElement/Roo.Element} element The element to align to
14674      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14675      * the element (defaults to this.defaultAlign)
14676      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14677      */
14678     show : function(el, pos, parentMenu){
14679         this.parentMenu = parentMenu;
14680         if(!this.el){
14681             this.render();
14682         }
14683         this.fireEvent("beforeshow", this);
14684         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14685     },
14686
14687     /**
14688      * Displays this menu at a specific xy position
14689      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14690      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14691      */
14692     showAt : function(xy, parentMenu, /* private: */_e){
14693         this.parentMenu = parentMenu;
14694         if(!this.el){
14695             this.render();
14696         }
14697         if(_e !== false){
14698             this.fireEvent("beforeshow", this);
14699             xy = this.el.adjustForConstraints(xy);
14700         }
14701         this.el.setXY(xy);
14702         this.el.show();
14703         this.hidden = false;
14704         this.focus();
14705         this.fireEvent("show", this);
14706     },
14707
14708     focus : function(){
14709         if(!this.hidden){
14710             this.doFocus.defer(50, this);
14711         }
14712     },
14713
14714     doFocus : function(){
14715         if(!this.hidden){
14716             this.focusEl.focus();
14717         }
14718     },
14719
14720     /**
14721      * Hides this menu and optionally all parent menus
14722      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14723      */
14724     hide : function(deep){
14725         if(this.el && this.isVisible()){
14726             this.fireEvent("beforehide", this);
14727             if(this.activeItem){
14728                 this.activeItem.deactivate();
14729                 this.activeItem = null;
14730             }
14731             this.el.hide();
14732             this.hidden = true;
14733             this.fireEvent("hide", this);
14734         }
14735         if(deep === true && this.parentMenu){
14736             this.parentMenu.hide(true);
14737         }
14738     },
14739
14740     /**
14741      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14742      * Any of the following are valid:
14743      * <ul>
14744      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14745      * <li>An HTMLElement object which will be converted to a menu item</li>
14746      * <li>A menu item config object that will be created as a new menu item</li>
14747      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14748      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14749      * </ul>
14750      * Usage:
14751      * <pre><code>
14752 // Create the menu
14753 var menu = new Roo.menu.Menu();
14754
14755 // Create a menu item to add by reference
14756 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14757
14758 // Add a bunch of items at once using different methods.
14759 // Only the last item added will be returned.
14760 var item = menu.add(
14761     menuItem,                // add existing item by ref
14762     'Dynamic Item',          // new TextItem
14763     '-',                     // new separator
14764     { text: 'Config Item' }  // new item by config
14765 );
14766 </code></pre>
14767      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14768      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14769      */
14770     add : function(){
14771         var a = arguments, l = a.length, item;
14772         for(var i = 0; i < l; i++){
14773             var el = a[i];
14774             if ((typeof(el) == "object") && el.xtype && el.xns) {
14775                 el = Roo.factory(el, Roo.menu);
14776             }
14777             
14778             if(el.render){ // some kind of Item
14779                 item = this.addItem(el);
14780             }else if(typeof el == "string"){ // string
14781                 if(el == "separator" || el == "-"){
14782                     item = this.addSeparator();
14783                 }else{
14784                     item = this.addText(el);
14785                 }
14786             }else if(el.tagName || el.el){ // element
14787                 item = this.addElement(el);
14788             }else if(typeof el == "object"){ // must be menu item config?
14789                 item = this.addMenuItem(el);
14790             }
14791         }
14792         return item;
14793     },
14794
14795     /**
14796      * Returns this menu's underlying {@link Roo.Element} object
14797      * @return {Roo.Element} The element
14798      */
14799     getEl : function(){
14800         if(!this.el){
14801             this.render();
14802         }
14803         return this.el;
14804     },
14805
14806     /**
14807      * Adds a separator bar to the menu
14808      * @return {Roo.menu.Item} The menu item that was added
14809      */
14810     addSeparator : function(){
14811         return this.addItem(new Roo.menu.Separator());
14812     },
14813
14814     /**
14815      * Adds an {@link Roo.Element} object to the menu
14816      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14817      * @return {Roo.menu.Item} The menu item that was added
14818      */
14819     addElement : function(el){
14820         return this.addItem(new Roo.menu.BaseItem(el));
14821     },
14822
14823     /**
14824      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14825      * @param {Roo.menu.Item} item The menu item to add
14826      * @return {Roo.menu.Item} The menu item that was added
14827      */
14828     addItem : function(item){
14829         this.items.add(item);
14830         if(this.ul){
14831             var li = document.createElement("li");
14832             li.className = "x-menu-list-item";
14833             this.ul.dom.appendChild(li);
14834             item.render(li, this);
14835             this.delayAutoWidth();
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14842      * @param {Object} config A MenuItem config object
14843      * @return {Roo.menu.Item} The menu item that was added
14844      */
14845     addMenuItem : function(config){
14846         if(!(config instanceof Roo.menu.Item)){
14847             if(typeof config.checked == "boolean"){ // must be check menu item config?
14848                 config = new Roo.menu.CheckItem(config);
14849             }else{
14850                 config = new Roo.menu.Item(config);
14851             }
14852         }
14853         return this.addItem(config);
14854     },
14855
14856     /**
14857      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14858      * @param {String} text The text to display in the menu item
14859      * @return {Roo.menu.Item} The menu item that was added
14860      */
14861     addText : function(text){
14862         return this.addItem(new Roo.menu.TextItem({ text : text }));
14863     },
14864
14865     /**
14866      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14867      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14868      * @param {Roo.menu.Item} item The menu item to add
14869      * @return {Roo.menu.Item} The menu item that was added
14870      */
14871     insert : function(index, item){
14872         this.items.insert(index, item);
14873         if(this.ul){
14874             var li = document.createElement("li");
14875             li.className = "x-menu-list-item";
14876             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14877             item.render(li, this);
14878             this.delayAutoWidth();
14879         }
14880         return item;
14881     },
14882
14883     /**
14884      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14885      * @param {Roo.menu.Item} item The menu item to remove
14886      */
14887     remove : function(item){
14888         this.items.removeKey(item.id);
14889         item.destroy();
14890     },
14891
14892     /**
14893      * Removes and destroys all items in the menu
14894      */
14895     removeAll : function(){
14896         var f;
14897         while(f = this.items.first()){
14898             this.remove(f);
14899         }
14900     }
14901 });
14902
14903 // MenuNav is a private utility class used internally by the Menu
14904 Roo.menu.MenuNav = function(menu){
14905     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14906     this.scope = this.menu = menu;
14907 };
14908
14909 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14910     doRelay : function(e, h){
14911         var k = e.getKey();
14912         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14913             this.menu.tryActivate(0, 1);
14914             return false;
14915         }
14916         return h.call(this.scope || this, e, this.menu);
14917     },
14918
14919     up : function(e, m){
14920         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14921             m.tryActivate(m.items.length-1, -1);
14922         }
14923     },
14924
14925     down : function(e, m){
14926         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14927             m.tryActivate(0, 1);
14928         }
14929     },
14930
14931     right : function(e, m){
14932         if(m.activeItem){
14933             m.activeItem.expandMenu(true);
14934         }
14935     },
14936
14937     left : function(e, m){
14938         m.hide();
14939         if(m.parentMenu && m.parentMenu.activeItem){
14940             m.parentMenu.activeItem.activate();
14941         }
14942     },
14943
14944     enter : function(e, m){
14945         if(m.activeItem){
14946             e.stopPropagation();
14947             m.activeItem.onClick(e);
14948             m.fireEvent("click", this, m.activeItem);
14949             return true;
14950         }
14951     }
14952 });/*
14953  * Based on:
14954  * Ext JS Library 1.1.1
14955  * Copyright(c) 2006-2007, Ext JS, LLC.
14956  *
14957  * Originally Released Under LGPL - original licence link has changed is not relivant.
14958  *
14959  * Fork - LGPL
14960  * <script type="text/javascript">
14961  */
14962  
14963 /**
14964  * @class Roo.menu.MenuMgr
14965  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14966  * @singleton
14967  */
14968 Roo.menu.MenuMgr = function(){
14969    var menus, active, groups = {}, attached = false, lastShow = new Date();
14970
14971    // private - called when first menu is created
14972    function init(){
14973        menus = {};
14974        active = new Roo.util.MixedCollection();
14975        Roo.get(document).addKeyListener(27, function(){
14976            if(active.length > 0){
14977                hideAll();
14978            }
14979        });
14980    }
14981
14982    // private
14983    function hideAll(){
14984        if(active && active.length > 0){
14985            var c = active.clone();
14986            c.each(function(m){
14987                m.hide();
14988            });
14989        }
14990    }
14991
14992    // private
14993    function onHide(m){
14994        active.remove(m);
14995        if(active.length < 1){
14996            Roo.get(document).un("mousedown", onMouseDown);
14997            attached = false;
14998        }
14999    }
15000
15001    // private
15002    function onShow(m){
15003        var last = active.last();
15004        lastShow = new Date();
15005        active.add(m);
15006        if(!attached){
15007            Roo.get(document).on("mousedown", onMouseDown);
15008            attached = true;
15009        }
15010        if(m.parentMenu){
15011           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15012           m.parentMenu.activeChild = m;
15013        }else if(last && last.isVisible()){
15014           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15015        }
15016    }
15017
15018    // private
15019    function onBeforeHide(m){
15020        if(m.activeChild){
15021            m.activeChild.hide();
15022        }
15023        if(m.autoHideTimer){
15024            clearTimeout(m.autoHideTimer);
15025            delete m.autoHideTimer;
15026        }
15027    }
15028
15029    // private
15030    function onBeforeShow(m){
15031        var pm = m.parentMenu;
15032        if(!pm && !m.allowOtherMenus){
15033            hideAll();
15034        }else if(pm && pm.activeChild && active != m){
15035            pm.activeChild.hide();
15036        }
15037    }
15038
15039    // private
15040    function onMouseDown(e){
15041        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15042            hideAll();
15043        }
15044    }
15045
15046    // private
15047    function onBeforeCheck(mi, state){
15048        if(state){
15049            var g = groups[mi.group];
15050            for(var i = 0, l = g.length; i < l; i++){
15051                if(g[i] != mi){
15052                    g[i].setChecked(false);
15053                }
15054            }
15055        }
15056    }
15057
15058    return {
15059
15060        /**
15061         * Hides all menus that are currently visible
15062         */
15063        hideAll : function(){
15064             hideAll();  
15065        },
15066
15067        // private
15068        register : function(menu){
15069            if(!menus){
15070                init();
15071            }
15072            menus[menu.id] = menu;
15073            menu.on("beforehide", onBeforeHide);
15074            menu.on("hide", onHide);
15075            menu.on("beforeshow", onBeforeShow);
15076            menu.on("show", onShow);
15077            var g = menu.group;
15078            if(g && menu.events["checkchange"]){
15079                if(!groups[g]){
15080                    groups[g] = [];
15081                }
15082                groups[g].push(menu);
15083                menu.on("checkchange", onCheck);
15084            }
15085        },
15086
15087         /**
15088          * Returns a {@link Roo.menu.Menu} object
15089          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15090          * be used to generate and return a new Menu instance.
15091          */
15092        get : function(menu){
15093            if(typeof menu == "string"){ // menu id
15094                return menus[menu];
15095            }else if(menu.events){  // menu instance
15096                return menu;
15097            }else if(typeof menu.length == 'number'){ // array of menu items?
15098                return new Roo.menu.Menu({items:menu});
15099            }else{ // otherwise, must be a config
15100                return new Roo.menu.Menu(menu);
15101            }
15102        },
15103
15104        // private
15105        unregister : function(menu){
15106            delete menus[menu.id];
15107            menu.un("beforehide", onBeforeHide);
15108            menu.un("hide", onHide);
15109            menu.un("beforeshow", onBeforeShow);
15110            menu.un("show", onShow);
15111            var g = menu.group;
15112            if(g && menu.events["checkchange"]){
15113                groups[g].remove(menu);
15114                menu.un("checkchange", onCheck);
15115            }
15116        },
15117
15118        // private
15119        registerCheckable : function(menuItem){
15120            var g = menuItem.group;
15121            if(g){
15122                if(!groups[g]){
15123                    groups[g] = [];
15124                }
15125                groups[g].push(menuItem);
15126                menuItem.on("beforecheckchange", onBeforeCheck);
15127            }
15128        },
15129
15130        // private
15131        unregisterCheckable : function(menuItem){
15132            var g = menuItem.group;
15133            if(g){
15134                groups[g].remove(menuItem);
15135                menuItem.un("beforecheckchange", onBeforeCheck);
15136            }
15137        }
15138    };
15139 }();/*
15140  * Based on:
15141  * Ext JS Library 1.1.1
15142  * Copyright(c) 2006-2007, Ext JS, LLC.
15143  *
15144  * Originally Released Under LGPL - original licence link has changed is not relivant.
15145  *
15146  * Fork - LGPL
15147  * <script type="text/javascript">
15148  */
15149  
15150
15151 /**
15152  * @class Roo.menu.BaseItem
15153  * @extends Roo.Component
15154  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15155  * management and base configuration options shared by all menu components.
15156  * @constructor
15157  * Creates a new BaseItem
15158  * @param {Object} config Configuration options
15159  */
15160 Roo.menu.BaseItem = function(config){
15161     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15162
15163     this.addEvents({
15164         /**
15165          * @event click
15166          * Fires when this item is clicked
15167          * @param {Roo.menu.BaseItem} this
15168          * @param {Roo.EventObject} e
15169          */
15170         click: true,
15171         /**
15172          * @event activate
15173          * Fires when this item is activated
15174          * @param {Roo.menu.BaseItem} this
15175          */
15176         activate : true,
15177         /**
15178          * @event deactivate
15179          * Fires when this item is deactivated
15180          * @param {Roo.menu.BaseItem} this
15181          */
15182         deactivate : true
15183     });
15184
15185     if(this.handler){
15186         this.on("click", this.handler, this.scope, true);
15187     }
15188 };
15189
15190 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15191     /**
15192      * @cfg {Function} handler
15193      * A function that will handle the click event of this menu item (defaults to undefined)
15194      */
15195     /**
15196      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15197      */
15198     canActivate : false,
15199     
15200      /**
15201      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15202      */
15203     hidden: false,
15204     
15205     /**
15206      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15207      */
15208     activeClass : "x-menu-item-active",
15209     /**
15210      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15211      */
15212     hideOnClick : true,
15213     /**
15214      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15215      */
15216     hideDelay : 100,
15217
15218     // private
15219     ctype: "Roo.menu.BaseItem",
15220
15221     // private
15222     actionMode : "container",
15223
15224     // private
15225     render : function(container, parentMenu){
15226         this.parentMenu = parentMenu;
15227         Roo.menu.BaseItem.superclass.render.call(this, container);
15228         this.container.menuItemId = this.id;
15229     },
15230
15231     // private
15232     onRender : function(container, position){
15233         this.el = Roo.get(this.el);
15234         container.dom.appendChild(this.el.dom);
15235     },
15236
15237     // private
15238     onClick : function(e){
15239         if(!this.disabled && this.fireEvent("click", this, e) !== false
15240                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15241             this.handleClick(e);
15242         }else{
15243             e.stopEvent();
15244         }
15245     },
15246
15247     // private
15248     activate : function(){
15249         if(this.disabled){
15250             return false;
15251         }
15252         var li = this.container;
15253         li.addClass(this.activeClass);
15254         this.region = li.getRegion().adjust(2, 2, -2, -2);
15255         this.fireEvent("activate", this);
15256         return true;
15257     },
15258
15259     // private
15260     deactivate : function(){
15261         this.container.removeClass(this.activeClass);
15262         this.fireEvent("deactivate", this);
15263     },
15264
15265     // private
15266     shouldDeactivate : function(e){
15267         return !this.region || !this.region.contains(e.getPoint());
15268     },
15269
15270     // private
15271     handleClick : function(e){
15272         if(this.hideOnClick){
15273             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15274         }
15275     },
15276
15277     // private
15278     expandMenu : function(autoActivate){
15279         // do nothing
15280     },
15281
15282     // private
15283     hideMenu : function(){
15284         // do nothing
15285     }
15286 });/*
15287  * Based on:
15288  * Ext JS Library 1.1.1
15289  * Copyright(c) 2006-2007, Ext JS, LLC.
15290  *
15291  * Originally Released Under LGPL - original licence link has changed is not relivant.
15292  *
15293  * Fork - LGPL
15294  * <script type="text/javascript">
15295  */
15296  
15297 /**
15298  * @class Roo.menu.Adapter
15299  * @extends Roo.menu.BaseItem
15300  * 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.
15301  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15302  * @constructor
15303  * Creates a new Adapter
15304  * @param {Object} config Configuration options
15305  */
15306 Roo.menu.Adapter = function(component, config){
15307     Roo.menu.Adapter.superclass.constructor.call(this, config);
15308     this.component = component;
15309 };
15310 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15311     // private
15312     canActivate : true,
15313
15314     // private
15315     onRender : function(container, position){
15316         this.component.render(container);
15317         this.el = this.component.getEl();
15318     },
15319
15320     // private
15321     activate : function(){
15322         if(this.disabled){
15323             return false;
15324         }
15325         this.component.focus();
15326         this.fireEvent("activate", this);
15327         return true;
15328     },
15329
15330     // private
15331     deactivate : function(){
15332         this.fireEvent("deactivate", this);
15333     },
15334
15335     // private
15336     disable : function(){
15337         this.component.disable();
15338         Roo.menu.Adapter.superclass.disable.call(this);
15339     },
15340
15341     // private
15342     enable : function(){
15343         this.component.enable();
15344         Roo.menu.Adapter.superclass.enable.call(this);
15345     }
15346 });/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356
15357 /**
15358  * @class Roo.menu.TextItem
15359  * @extends Roo.menu.BaseItem
15360  * Adds a static text string to a menu, usually used as either a heading or group separator.
15361  * Note: old style constructor with text is still supported.
15362  * 
15363  * @constructor
15364  * Creates a new TextItem
15365  * @param {Object} cfg Configuration
15366  */
15367 Roo.menu.TextItem = function(cfg){
15368     if (typeof(cfg) == 'string') {
15369         this.text = cfg;
15370     } else {
15371         Roo.apply(this,cfg);
15372     }
15373     
15374     Roo.menu.TextItem.superclass.constructor.call(this);
15375 };
15376
15377 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15378     /**
15379      * @cfg {String} text Text to show on item.
15380      */
15381     text : '',
15382     
15383     /**
15384      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15385      */
15386     hideOnClick : false,
15387     /**
15388      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15389      */
15390     itemCls : "x-menu-text",
15391
15392     // private
15393     onRender : function(){
15394         var s = document.createElement("span");
15395         s.className = this.itemCls;
15396         s.innerHTML = this.text;
15397         this.el = s;
15398         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15399     }
15400 });/*
15401  * Based on:
15402  * Ext JS Library 1.1.1
15403  * Copyright(c) 2006-2007, Ext JS, LLC.
15404  *
15405  * Originally Released Under LGPL - original licence link has changed is not relivant.
15406  *
15407  * Fork - LGPL
15408  * <script type="text/javascript">
15409  */
15410
15411 /**
15412  * @class Roo.menu.Separator
15413  * @extends Roo.menu.BaseItem
15414  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15415  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15416  * @constructor
15417  * @param {Object} config Configuration options
15418  */
15419 Roo.menu.Separator = function(config){
15420     Roo.menu.Separator.superclass.constructor.call(this, config);
15421 };
15422
15423 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15424     /**
15425      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15426      */
15427     itemCls : "x-menu-sep",
15428     /**
15429      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15430      */
15431     hideOnClick : false,
15432
15433     // private
15434     onRender : function(li){
15435         var s = document.createElement("span");
15436         s.className = this.itemCls;
15437         s.innerHTML = "&#160;";
15438         this.el = s;
15439         li.addClass("x-menu-sep-li");
15440         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452 /**
15453  * @class Roo.menu.Item
15454  * @extends Roo.menu.BaseItem
15455  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15456  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15457  * activation and click handling.
15458  * @constructor
15459  * Creates a new Item
15460  * @param {Object} config Configuration options
15461  */
15462 Roo.menu.Item = function(config){
15463     Roo.menu.Item.superclass.constructor.call(this, config);
15464     if(this.menu){
15465         this.menu = Roo.menu.MenuMgr.get(this.menu);
15466     }
15467 };
15468 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15469     /**
15470      * @cfg {Roo.menu.Menu} menu
15471      * A Sub menu
15472      */
15473     /**
15474      * @cfg {String} text
15475      * The text to show on the menu item.
15476      */
15477     text: '',
15478      /**
15479      * @cfg {String} HTML to render in menu
15480      * The text to show on the menu item (HTML version).
15481      */
15482     html: '',
15483     /**
15484      * @cfg {String} icon
15485      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15486      */
15487     icon: undefined,
15488     /**
15489      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15490      */
15491     itemCls : "x-menu-item",
15492     /**
15493      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15494      */
15495     canActivate : true,
15496     /**
15497      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15498      */
15499     showDelay: 200,
15500     // doc'd in BaseItem
15501     hideDelay: 200,
15502
15503     // private
15504     ctype: "Roo.menu.Item",
15505     
15506     // private
15507     onRender : function(container, position){
15508         var el = document.createElement("a");
15509         el.hideFocus = true;
15510         el.unselectable = "on";
15511         el.href = this.href || "#";
15512         if(this.hrefTarget){
15513             el.target = this.hrefTarget;
15514         }
15515         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15516         
15517         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15518         
15519         el.innerHTML = String.format(
15520                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15521                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15522         this.el = el;
15523         Roo.menu.Item.superclass.onRender.call(this, container, position);
15524     },
15525
15526     /**
15527      * Sets the text to display in this menu item
15528      * @param {String} text The text to display
15529      * @param {Boolean} isHTML true to indicate text is pure html.
15530      */
15531     setText : function(text, isHTML){
15532         if (isHTML) {
15533             this.html = text;
15534         } else {
15535             this.text = text;
15536             this.html = '';
15537         }
15538         if(this.rendered){
15539             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15540      
15541             this.el.update(String.format(
15542                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15543                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15544             this.parentMenu.autoWidth();
15545         }
15546     },
15547
15548     // private
15549     handleClick : function(e){
15550         if(!this.href){ // if no link defined, stop the event automatically
15551             e.stopEvent();
15552         }
15553         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15554     },
15555
15556     // private
15557     activate : function(autoExpand){
15558         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15559             this.focus();
15560             if(autoExpand){
15561                 this.expandMenu();
15562             }
15563         }
15564         return true;
15565     },
15566
15567     // private
15568     shouldDeactivate : function(e){
15569         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15570             if(this.menu && this.menu.isVisible()){
15571                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15572             }
15573             return true;
15574         }
15575         return false;
15576     },
15577
15578     // private
15579     deactivate : function(){
15580         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15581         this.hideMenu();
15582     },
15583
15584     // private
15585     expandMenu : function(autoActivate){
15586         if(!this.disabled && this.menu){
15587             clearTimeout(this.hideTimer);
15588             delete this.hideTimer;
15589             if(!this.menu.isVisible() && !this.showTimer){
15590                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15591             }else if (this.menu.isVisible() && autoActivate){
15592                 this.menu.tryActivate(0, 1);
15593             }
15594         }
15595     },
15596
15597     // private
15598     deferExpand : function(autoActivate){
15599         delete this.showTimer;
15600         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15601         if(autoActivate){
15602             this.menu.tryActivate(0, 1);
15603         }
15604     },
15605
15606     // private
15607     hideMenu : function(){
15608         clearTimeout(this.showTimer);
15609         delete this.showTimer;
15610         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15611             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15612         }
15613     },
15614
15615     // private
15616     deferHide : function(){
15617         delete this.hideTimer;
15618         this.menu.hide();
15619     }
15620 });/*
15621  * Based on:
15622  * Ext JS Library 1.1.1
15623  * Copyright(c) 2006-2007, Ext JS, LLC.
15624  *
15625  * Originally Released Under LGPL - original licence link has changed is not relivant.
15626  *
15627  * Fork - LGPL
15628  * <script type="text/javascript">
15629  */
15630  
15631 /**
15632  * @class Roo.menu.CheckItem
15633  * @extends Roo.menu.Item
15634  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15635  * @constructor
15636  * Creates a new CheckItem
15637  * @param {Object} config Configuration options
15638  */
15639 Roo.menu.CheckItem = function(config){
15640     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15641     this.addEvents({
15642         /**
15643          * @event beforecheckchange
15644          * Fires before the checked value is set, providing an opportunity to cancel if needed
15645          * @param {Roo.menu.CheckItem} this
15646          * @param {Boolean} checked The new checked value that will be set
15647          */
15648         "beforecheckchange" : true,
15649         /**
15650          * @event checkchange
15651          * Fires after the checked value has been set
15652          * @param {Roo.menu.CheckItem} this
15653          * @param {Boolean} checked The checked value that was set
15654          */
15655         "checkchange" : true
15656     });
15657     if(this.checkHandler){
15658         this.on('checkchange', this.checkHandler, this.scope);
15659     }
15660 };
15661 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15662     /**
15663      * @cfg {String} group
15664      * All check items with the same group name will automatically be grouped into a single-select
15665      * radio button group (defaults to '')
15666      */
15667     /**
15668      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15669      */
15670     itemCls : "x-menu-item x-menu-check-item",
15671     /**
15672      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15673      */
15674     groupClass : "x-menu-group-item",
15675
15676     /**
15677      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15678      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15679      * initialized with checked = true will be rendered as checked.
15680      */
15681     checked: false,
15682
15683     // private
15684     ctype: "Roo.menu.CheckItem",
15685
15686     // private
15687     onRender : function(c){
15688         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15689         if(this.group){
15690             this.el.addClass(this.groupClass);
15691         }
15692         Roo.menu.MenuMgr.registerCheckable(this);
15693         if(this.checked){
15694             this.checked = false;
15695             this.setChecked(true, true);
15696         }
15697     },
15698
15699     // private
15700     destroy : function(){
15701         if(this.rendered){
15702             Roo.menu.MenuMgr.unregisterCheckable(this);
15703         }
15704         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15705     },
15706
15707     /**
15708      * Set the checked state of this item
15709      * @param {Boolean} checked The new checked value
15710      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15711      */
15712     setChecked : function(state, suppressEvent){
15713         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15714             if(this.container){
15715                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15716             }
15717             this.checked = state;
15718             if(suppressEvent !== true){
15719                 this.fireEvent("checkchange", this, state);
15720             }
15721         }
15722     },
15723
15724     // private
15725     handleClick : function(e){
15726        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15727            this.setChecked(!this.checked);
15728        }
15729        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15730     }
15731 });/*
15732  * Based on:
15733  * Ext JS Library 1.1.1
15734  * Copyright(c) 2006-2007, Ext JS, LLC.
15735  *
15736  * Originally Released Under LGPL - original licence link has changed is not relivant.
15737  *
15738  * Fork - LGPL
15739  * <script type="text/javascript">
15740  */
15741  
15742 /**
15743  * @class Roo.menu.DateItem
15744  * @extends Roo.menu.Adapter
15745  * A menu item that wraps the {@link Roo.DatPicker} component.
15746  * @constructor
15747  * Creates a new DateItem
15748  * @param {Object} config Configuration options
15749  */
15750 Roo.menu.DateItem = function(config){
15751     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15752     /** The Roo.DatePicker object @type Roo.DatePicker */
15753     this.picker = this.component;
15754     this.addEvents({select: true});
15755     
15756     this.picker.on("render", function(picker){
15757         picker.getEl().swallowEvent("click");
15758         picker.container.addClass("x-menu-date-item");
15759     });
15760
15761     this.picker.on("select", this.onSelect, this);
15762 };
15763
15764 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15765     // private
15766     onSelect : function(picker, date){
15767         this.fireEvent("select", this, date, picker);
15768         Roo.menu.DateItem.superclass.handleClick.call(this);
15769     }
15770 });/*
15771  * Based on:
15772  * Ext JS Library 1.1.1
15773  * Copyright(c) 2006-2007, Ext JS, LLC.
15774  *
15775  * Originally Released Under LGPL - original licence link has changed is not relivant.
15776  *
15777  * Fork - LGPL
15778  * <script type="text/javascript">
15779  */
15780  
15781 /**
15782  * @class Roo.menu.ColorItem
15783  * @extends Roo.menu.Adapter
15784  * A menu item that wraps the {@link Roo.ColorPalette} component.
15785  * @constructor
15786  * Creates a new ColorItem
15787  * @param {Object} config Configuration options
15788  */
15789 Roo.menu.ColorItem = function(config){
15790     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15791     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15792     this.palette = this.component;
15793     this.relayEvents(this.palette, ["select"]);
15794     if(this.selectHandler){
15795         this.on('select', this.selectHandler, this.scope);
15796     }
15797 };
15798 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15799  * Based on:
15800  * Ext JS Library 1.1.1
15801  * Copyright(c) 2006-2007, Ext JS, LLC.
15802  *
15803  * Originally Released Under LGPL - original licence link has changed is not relivant.
15804  *
15805  * Fork - LGPL
15806  * <script type="text/javascript">
15807  */
15808  
15809
15810 /**
15811  * @class Roo.menu.DateMenu
15812  * @extends Roo.menu.Menu
15813  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15814  * @constructor
15815  * Creates a new DateMenu
15816  * @param {Object} config Configuration options
15817  */
15818 Roo.menu.DateMenu = function(config){
15819     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15820     this.plain = true;
15821     var di = new Roo.menu.DateItem(config);
15822     this.add(di);
15823     /**
15824      * The {@link Roo.DatePicker} instance for this DateMenu
15825      * @type DatePicker
15826      */
15827     this.picker = di.picker;
15828     /**
15829      * @event select
15830      * @param {DatePicker} picker
15831      * @param {Date} date
15832      */
15833     this.relayEvents(di, ["select"]);
15834     this.on('beforeshow', function(){
15835         if(this.picker){
15836             this.picker.hideMonthPicker(false);
15837         }
15838     }, this);
15839 };
15840 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15841     cls:'x-date-menu'
15842 });/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852  
15853
15854 /**
15855  * @class Roo.menu.ColorMenu
15856  * @extends Roo.menu.Menu
15857  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15858  * @constructor
15859  * Creates a new ColorMenu
15860  * @param {Object} config Configuration options
15861  */
15862 Roo.menu.ColorMenu = function(config){
15863     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15864     this.plain = true;
15865     var ci = new Roo.menu.ColorItem(config);
15866     this.add(ci);
15867     /**
15868      * The {@link Roo.ColorPalette} instance for this ColorMenu
15869      * @type ColorPalette
15870      */
15871     this.palette = ci.palette;
15872     /**
15873      * @event select
15874      * @param {ColorPalette} palette
15875      * @param {String} color
15876      */
15877     this.relayEvents(ci, ["select"]);
15878 };
15879 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15880  * Based on:
15881  * Ext JS Library 1.1.1
15882  * Copyright(c) 2006-2007, Ext JS, LLC.
15883  *
15884  * Originally Released Under LGPL - original licence link has changed is not relivant.
15885  *
15886  * Fork - LGPL
15887  * <script type="text/javascript">
15888  */
15889  
15890 /**
15891  * @class Roo.form.TextItem
15892  * @extends Roo.BoxComponent
15893  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15894  * @constructor
15895  * Creates a new TextItem
15896  * @param {Object} config Configuration options
15897  */
15898 Roo.form.TextItem = function(config){
15899     Roo.form.TextItem.superclass.constructor.call(this, config);
15900 };
15901
15902 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15903     
15904     /**
15905      * @cfg {String} tag the tag for this item (default div)
15906      */
15907     tag : 'div',
15908     /**
15909      * @cfg {String} html the content for this item
15910      */
15911     html : '',
15912     
15913     getAutoCreate : function()
15914     {
15915         var cfg = {
15916             id: this.id,
15917             tag: this.tag,
15918             html: this.html,
15919             cls: 'x-form-item'
15920         };
15921         
15922         return cfg;
15923         
15924     },
15925     
15926     onRender : function(ct, position)
15927     {
15928         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15929         
15930         if(!this.el){
15931             var cfg = this.getAutoCreate();
15932             if(!cfg.name){
15933                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15934             }
15935             if (!cfg.name.length) {
15936                 delete cfg.name;
15937             }
15938             this.el = ct.createChild(cfg, position);
15939         }
15940     },
15941     /*
15942      * setHTML
15943      * @param {String} html update the Contents of the element.
15944      */
15945     setHTML : function(html)
15946     {
15947         this.fieldEl.dom.innerHTML = html;
15948     }
15949     
15950 });/*
15951  * Based on:
15952  * Ext JS Library 1.1.1
15953  * Copyright(c) 2006-2007, Ext JS, LLC.
15954  *
15955  * Originally Released Under LGPL - original licence link has changed is not relivant.
15956  *
15957  * Fork - LGPL
15958  * <script type="text/javascript">
15959  */
15960  
15961 /**
15962  * @class Roo.form.Field
15963  * @extends Roo.BoxComponent
15964  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15965  * @constructor
15966  * Creates a new Field
15967  * @param {Object} config Configuration options
15968  */
15969 Roo.form.Field = function(config){
15970     Roo.form.Field.superclass.constructor.call(this, config);
15971 };
15972
15973 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15974     /**
15975      * @cfg {String} fieldLabel Label to use when rendering a form.
15976      */
15977        /**
15978      * @cfg {String} qtip Mouse over tip
15979      */
15980      
15981     /**
15982      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15983      */
15984     invalidClass : "x-form-invalid",
15985     /**
15986      * @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")
15987      */
15988     invalidText : "The value in this field is invalid",
15989     /**
15990      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15991      */
15992     focusClass : "x-form-focus",
15993     /**
15994      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15995       automatic validation (defaults to "keyup").
15996      */
15997     validationEvent : "keyup",
15998     /**
15999      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16000      */
16001     validateOnBlur : true,
16002     /**
16003      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16004      */
16005     validationDelay : 250,
16006     /**
16007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16008      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16009      */
16010     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16011     /**
16012      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16013      */
16014     fieldClass : "x-form-field",
16015     /**
16016      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16017      *<pre>
16018 Value         Description
16019 -----------   ----------------------------------------------------------------------
16020 qtip          Display a quick tip when the user hovers over the field
16021 title         Display a default browser title attribute popup
16022 under         Add a block div beneath the field containing the error text
16023 side          Add an error icon to the right of the field with a popup on hover
16024 [element id]  Add the error text directly to the innerHTML of the specified element
16025 </pre>
16026      */
16027     msgTarget : 'qtip',
16028     /**
16029      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16030      */
16031     msgFx : 'normal',
16032
16033     /**
16034      * @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.
16035      */
16036     readOnly : false,
16037
16038     /**
16039      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16040      */
16041     disabled : false,
16042
16043     /**
16044      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16045      */
16046     inputType : undefined,
16047     
16048     /**
16049      * @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).
16050          */
16051         tabIndex : undefined,
16052         
16053     // private
16054     isFormField : true,
16055
16056     // private
16057     hasFocus : false,
16058     /**
16059      * @property {Roo.Element} fieldEl
16060      * Element Containing the rendered Field (with label etc.)
16061      */
16062     /**
16063      * @cfg {Mixed} value A value to initialize this field with.
16064      */
16065     value : undefined,
16066
16067     /**
16068      * @cfg {String} name The field's HTML name attribute.
16069      */
16070     /**
16071      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16072      */
16073     // private
16074     loadedValue : false,
16075      
16076      
16077         // private ??
16078         initComponent : function(){
16079         Roo.form.Field.superclass.initComponent.call(this);
16080         this.addEvents({
16081             /**
16082              * @event focus
16083              * Fires when this field receives input focus.
16084              * @param {Roo.form.Field} this
16085              */
16086             focus : true,
16087             /**
16088              * @event blur
16089              * Fires when this field loses input focus.
16090              * @param {Roo.form.Field} this
16091              */
16092             blur : true,
16093             /**
16094              * @event specialkey
16095              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16096              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16097              * @param {Roo.form.Field} this
16098              * @param {Roo.EventObject} e The event object
16099              */
16100             specialkey : true,
16101             /**
16102              * @event change
16103              * Fires just before the field blurs if the field value has changed.
16104              * @param {Roo.form.Field} this
16105              * @param {Mixed} newValue The new value
16106              * @param {Mixed} oldValue The original value
16107              */
16108             change : true,
16109             /**
16110              * @event invalid
16111              * Fires after the field has been marked as invalid.
16112              * @param {Roo.form.Field} this
16113              * @param {String} msg The validation message
16114              */
16115             invalid : true,
16116             /**
16117              * @event valid
16118              * Fires after the field has been validated with no errors.
16119              * @param {Roo.form.Field} this
16120              */
16121             valid : true,
16122              /**
16123              * @event keyup
16124              * Fires after the key up
16125              * @param {Roo.form.Field} this
16126              * @param {Roo.EventObject}  e The event Object
16127              */
16128             keyup : true
16129         });
16130     },
16131
16132     /**
16133      * Returns the name attribute of the field if available
16134      * @return {String} name The field name
16135      */
16136     getName: function(){
16137          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16138     },
16139
16140     // private
16141     onRender : function(ct, position){
16142         Roo.form.Field.superclass.onRender.call(this, ct, position);
16143         if(!this.el){
16144             var cfg = this.getAutoCreate();
16145             if(!cfg.name){
16146                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16147             }
16148             if (!cfg.name.length) {
16149                 delete cfg.name;
16150             }
16151             if(this.inputType){
16152                 cfg.type = this.inputType;
16153             }
16154             this.el = ct.createChild(cfg, position);
16155         }
16156         var type = this.el.dom.type;
16157         if(type){
16158             if(type == 'password'){
16159                 type = 'text';
16160             }
16161             this.el.addClass('x-form-'+type);
16162         }
16163         if(this.readOnly){
16164             this.el.dom.readOnly = true;
16165         }
16166         if(this.tabIndex !== undefined){
16167             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16168         }
16169
16170         this.el.addClass([this.fieldClass, this.cls]);
16171         this.initValue();
16172     },
16173
16174     /**
16175      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16176      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16177      * @return {Roo.form.Field} this
16178      */
16179     applyTo : function(target){
16180         this.allowDomMove = false;
16181         this.el = Roo.get(target);
16182         this.render(this.el.dom.parentNode);
16183         return this;
16184     },
16185
16186     // private
16187     initValue : function(){
16188         if(this.value !== undefined){
16189             this.setValue(this.value);
16190         }else if(this.el.dom.value.length > 0){
16191             this.setValue(this.el.dom.value);
16192         }
16193     },
16194
16195     /**
16196      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16197      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16198      */
16199     isDirty : function() {
16200         if(this.disabled) {
16201             return false;
16202         }
16203         return String(this.getValue()) !== String(this.originalValue);
16204     },
16205
16206     /**
16207      * stores the current value in loadedValue
16208      */
16209     resetHasChanged : function()
16210     {
16211         this.loadedValue = String(this.getValue());
16212     },
16213     /**
16214      * checks the current value against the 'loaded' value.
16215      * Note - will return false if 'resetHasChanged' has not been called first.
16216      */
16217     hasChanged : function()
16218     {
16219         if(this.disabled || this.readOnly) {
16220             return false;
16221         }
16222         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16223     },
16224     
16225     
16226     
16227     // private
16228     afterRender : function(){
16229         Roo.form.Field.superclass.afterRender.call(this);
16230         this.initEvents();
16231     },
16232
16233     // private
16234     fireKey : function(e){
16235         //Roo.log('field ' + e.getKey());
16236         if(e.isNavKeyPress()){
16237             this.fireEvent("specialkey", this, e);
16238         }
16239     },
16240
16241     /**
16242      * Resets the current field value to the originally loaded value and clears any validation messages
16243      */
16244     reset : function(){
16245         this.setValue(this.resetValue);
16246         this.originalValue = this.getValue();
16247         this.clearInvalid();
16248     },
16249
16250     // private
16251     initEvents : function(){
16252         // safari killled keypress - so keydown is now used..
16253         this.el.on("keydown" , this.fireKey,  this);
16254         this.el.on("focus", this.onFocus,  this);
16255         this.el.on("blur", this.onBlur,  this);
16256         this.el.relayEvent('keyup', this);
16257
16258         // reference to original value for reset
16259         this.originalValue = this.getValue();
16260         this.resetValue =  this.getValue();
16261     },
16262
16263     // private
16264     onFocus : function(){
16265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16266             this.el.addClass(this.focusClass);
16267         }
16268         if(!this.hasFocus){
16269             this.hasFocus = true;
16270             this.startValue = this.getValue();
16271             this.fireEvent("focus", this);
16272         }
16273     },
16274
16275     beforeBlur : Roo.emptyFn,
16276
16277     // private
16278     onBlur : function(){
16279         this.beforeBlur();
16280         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16281             this.el.removeClass(this.focusClass);
16282         }
16283         this.hasFocus = false;
16284         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16285             this.validate();
16286         }
16287         var v = this.getValue();
16288         if(String(v) !== String(this.startValue)){
16289             this.fireEvent('change', this, v, this.startValue);
16290         }
16291         this.fireEvent("blur", this);
16292     },
16293
16294     /**
16295      * Returns whether or not the field value is currently valid
16296      * @param {Boolean} preventMark True to disable marking the field invalid
16297      * @return {Boolean} True if the value is valid, else false
16298      */
16299     isValid : function(preventMark){
16300         if(this.disabled){
16301             return true;
16302         }
16303         var restore = this.preventMark;
16304         this.preventMark = preventMark === true;
16305         var v = this.validateValue(this.processValue(this.getRawValue()));
16306         this.preventMark = restore;
16307         return v;
16308     },
16309
16310     /**
16311      * Validates the field value
16312      * @return {Boolean} True if the value is valid, else false
16313      */
16314     validate : function(){
16315         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16316             this.clearInvalid();
16317             return true;
16318         }
16319         return false;
16320     },
16321
16322     processValue : function(value){
16323         return value;
16324     },
16325
16326     // private
16327     // Subclasses should provide the validation implementation by overriding this
16328     validateValue : function(value){
16329         return true;
16330     },
16331
16332     /**
16333      * Mark this field as invalid
16334      * @param {String} msg The validation message
16335      */
16336     markInvalid : function(msg){
16337         if(!this.rendered || this.preventMark){ // not rendered
16338             return;
16339         }
16340         
16341         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16342         
16343         obj.el.addClass(this.invalidClass);
16344         msg = msg || this.invalidText;
16345         switch(this.msgTarget){
16346             case 'qtip':
16347                 obj.el.dom.qtip = msg;
16348                 obj.el.dom.qclass = 'x-form-invalid-tip';
16349                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16350                     Roo.QuickTips.enable();
16351                 }
16352                 break;
16353             case 'title':
16354                 this.el.dom.title = msg;
16355                 break;
16356             case 'under':
16357                 if(!this.errorEl){
16358                     var elp = this.el.findParent('.x-form-element', 5, true);
16359                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16360                     this.errorEl.setWidth(elp.getWidth(true)-20);
16361                 }
16362                 this.errorEl.update(msg);
16363                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16364                 break;
16365             case 'side':
16366                 if(!this.errorIcon){
16367                     var elp = this.el.findParent('.x-form-element', 5, true);
16368                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16369                 }
16370                 this.alignErrorIcon();
16371                 this.errorIcon.dom.qtip = msg;
16372                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16373                 this.errorIcon.show();
16374                 this.on('resize', this.alignErrorIcon, this);
16375                 break;
16376             default:
16377                 var t = Roo.getDom(this.msgTarget);
16378                 t.innerHTML = msg;
16379                 t.style.display = this.msgDisplay;
16380                 break;
16381         }
16382         this.fireEvent('invalid', this, msg);
16383     },
16384
16385     // private
16386     alignErrorIcon : function(){
16387         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16388     },
16389
16390     /**
16391      * Clear any invalid styles/messages for this field
16392      */
16393     clearInvalid : function(){
16394         if(!this.rendered || this.preventMark){ // not rendered
16395             return;
16396         }
16397         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16398         
16399         obj.el.removeClass(this.invalidClass);
16400         switch(this.msgTarget){
16401             case 'qtip':
16402                 obj.el.dom.qtip = '';
16403                 break;
16404             case 'title':
16405                 this.el.dom.title = '';
16406                 break;
16407             case 'under':
16408                 if(this.errorEl){
16409                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16410                 }
16411                 break;
16412             case 'side':
16413                 if(this.errorIcon){
16414                     this.errorIcon.dom.qtip = '';
16415                     this.errorIcon.hide();
16416                     this.un('resize', this.alignErrorIcon, this);
16417                 }
16418                 break;
16419             default:
16420                 var t = Roo.getDom(this.msgTarget);
16421                 t.innerHTML = '';
16422                 t.style.display = 'none';
16423                 break;
16424         }
16425         this.fireEvent('valid', this);
16426     },
16427
16428     /**
16429      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16430      * @return {Mixed} value The field value
16431      */
16432     getRawValue : function(){
16433         var v = this.el.getValue();
16434         
16435         return v;
16436     },
16437
16438     /**
16439      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16440      * @return {Mixed} value The field value
16441      */
16442     getValue : function(){
16443         var v = this.el.getValue();
16444          
16445         return v;
16446     },
16447
16448     /**
16449      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16450      * @param {Mixed} value The value to set
16451      */
16452     setRawValue : function(v){
16453         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16454     },
16455
16456     /**
16457      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16458      * @param {Mixed} value The value to set
16459      */
16460     setValue : function(v){
16461         this.value = v;
16462         if(this.rendered){
16463             this.el.dom.value = (v === null || v === undefined ? '' : v);
16464              this.validate();
16465         }
16466     },
16467
16468     adjustSize : function(w, h){
16469         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16470         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16471         return s;
16472     },
16473
16474     adjustWidth : function(tag, w){
16475         tag = tag.toLowerCase();
16476         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16477             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16478                 if(tag == 'input'){
16479                     return w + 2;
16480                 }
16481                 if(tag == 'textarea'){
16482                     return w-2;
16483                 }
16484             }else if(Roo.isOpera){
16485                 if(tag == 'input'){
16486                     return w + 2;
16487                 }
16488                 if(tag == 'textarea'){
16489                     return w-2;
16490                 }
16491             }
16492         }
16493         return w;
16494     }
16495 });
16496
16497
16498 // anything other than normal should be considered experimental
16499 Roo.form.Field.msgFx = {
16500     normal : {
16501         show: function(msgEl, f){
16502             msgEl.setDisplayed('block');
16503         },
16504
16505         hide : function(msgEl, f){
16506             msgEl.setDisplayed(false).update('');
16507         }
16508     },
16509
16510     slide : {
16511         show: function(msgEl, f){
16512             msgEl.slideIn('t', {stopFx:true});
16513         },
16514
16515         hide : function(msgEl, f){
16516             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16517         }
16518     },
16519
16520     slideRight : {
16521         show: function(msgEl, f){
16522             msgEl.fixDisplay();
16523             msgEl.alignTo(f.el, 'tl-tr');
16524             msgEl.slideIn('l', {stopFx:true});
16525         },
16526
16527         hide : function(msgEl, f){
16528             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16529         }
16530     }
16531 };/*
16532  * Based on:
16533  * Ext JS Library 1.1.1
16534  * Copyright(c) 2006-2007, Ext JS, LLC.
16535  *
16536  * Originally Released Under LGPL - original licence link has changed is not relivant.
16537  *
16538  * Fork - LGPL
16539  * <script type="text/javascript">
16540  */
16541  
16542
16543 /**
16544  * @class Roo.form.TextField
16545  * @extends Roo.form.Field
16546  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16547  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16548  * @constructor
16549  * Creates a new TextField
16550  * @param {Object} config Configuration options
16551  */
16552 Roo.form.TextField = function(config){
16553     Roo.form.TextField.superclass.constructor.call(this, config);
16554     this.addEvents({
16555         /**
16556          * @event autosize
16557          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16558          * according to the default logic, but this event provides a hook for the developer to apply additional
16559          * logic at runtime to resize the field if needed.
16560              * @param {Roo.form.Field} this This text field
16561              * @param {Number} width The new field width
16562              */
16563         autosize : true
16564     });
16565 };
16566
16567 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16568     /**
16569      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16570      */
16571     grow : false,
16572     /**
16573      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16574      */
16575     growMin : 30,
16576     /**
16577      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16578      */
16579     growMax : 800,
16580     /**
16581      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16582      */
16583     vtype : null,
16584     /**
16585      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16586      */
16587     maskRe : null,
16588     /**
16589      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16590      */
16591     disableKeyFilter : false,
16592     /**
16593      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16594      */
16595     allowBlank : true,
16596     /**
16597      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16598      */
16599     minLength : 0,
16600     /**
16601      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16602      */
16603     maxLength : Number.MAX_VALUE,
16604     /**
16605      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16606      */
16607     minLengthText : "The minimum length for this field is {0}",
16608     /**
16609      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16610      */
16611     maxLengthText : "The maximum length for this field is {0}",
16612     /**
16613      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16614      */
16615     selectOnFocus : false,
16616     /**
16617      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16618      */    
16619     allowLeadingSpace : false,
16620     /**
16621      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16622      */
16623     blankText : "This field is required",
16624     /**
16625      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16626      * If available, this function will be called only after the basic validators all return true, and will be passed the
16627      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16628      */
16629     validator : null,
16630     /**
16631      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16632      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16633      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16634      */
16635     regex : null,
16636     /**
16637      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16638      */
16639     regexText : "",
16640     /**
16641      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16642      */
16643     emptyText : null,
16644    
16645
16646     // private
16647     initEvents : function()
16648     {
16649         if (this.emptyText) {
16650             this.el.attr('placeholder', this.emptyText);
16651         }
16652         
16653         Roo.form.TextField.superclass.initEvents.call(this);
16654         if(this.validationEvent == 'keyup'){
16655             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16656             this.el.on('keyup', this.filterValidation, this);
16657         }
16658         else if(this.validationEvent !== false){
16659             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16660         }
16661         
16662         if(this.selectOnFocus){
16663             this.on("focus", this.preFocus, this);
16664         }
16665         if (!this.allowLeadingSpace) {
16666             this.on('blur', this.cleanLeadingSpace, this);
16667         }
16668         
16669         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16670             this.el.on("keypress", this.filterKeys, this);
16671         }
16672         if(this.grow){
16673             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16674             this.el.on("click", this.autoSize,  this);
16675         }
16676         if(this.el.is('input[type=password]') && Roo.isSafari){
16677             this.el.on('keydown', this.SafariOnKeyDown, this);
16678         }
16679     },
16680
16681     processValue : function(value){
16682         if(this.stripCharsRe){
16683             var newValue = value.replace(this.stripCharsRe, '');
16684             if(newValue !== value){
16685                 this.setRawValue(newValue);
16686                 return newValue;
16687             }
16688         }
16689         return value;
16690     },
16691
16692     filterValidation : function(e){
16693         if(!e.isNavKeyPress()){
16694             this.validationTask.delay(this.validationDelay);
16695         }
16696     },
16697
16698     // private
16699     onKeyUp : function(e){
16700         if(!e.isNavKeyPress()){
16701             this.autoSize();
16702         }
16703     },
16704     // private - clean the leading white space
16705     cleanLeadingSpace : function(e)
16706     {
16707         if ( this.inputType == 'file') {
16708             return;
16709         }
16710         
16711         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16712     },
16713     /**
16714      * Resets the current field value to the originally-loaded value and clears any validation messages.
16715      *  
16716      */
16717     reset : function(){
16718         Roo.form.TextField.superclass.reset.call(this);
16719        
16720     }, 
16721     // private
16722     preFocus : function(){
16723         
16724         if(this.selectOnFocus){
16725             this.el.dom.select();
16726         }
16727     },
16728
16729     
16730     // private
16731     filterKeys : function(e){
16732         var k = e.getKey();
16733         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16734             return;
16735         }
16736         var c = e.getCharCode(), cc = String.fromCharCode(c);
16737         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16738             return;
16739         }
16740         if(!this.maskRe.test(cc)){
16741             e.stopEvent();
16742         }
16743     },
16744
16745     setValue : function(v){
16746         
16747         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16748         
16749         this.autoSize();
16750     },
16751
16752     /**
16753      * Validates a value according to the field's validation rules and marks the field as invalid
16754      * if the validation fails
16755      * @param {Mixed} value The value to validate
16756      * @return {Boolean} True if the value is valid, else false
16757      */
16758     validateValue : function(value){
16759         if(value.length < 1)  { // if it's blank
16760              if(this.allowBlank){
16761                 this.clearInvalid();
16762                 return true;
16763              }else{
16764                 this.markInvalid(this.blankText);
16765                 return false;
16766              }
16767         }
16768         if(value.length < this.minLength){
16769             this.markInvalid(String.format(this.minLengthText, this.minLength));
16770             return false;
16771         }
16772         if(value.length > this.maxLength){
16773             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16774             return false;
16775         }
16776         if(this.vtype){
16777             var vt = Roo.form.VTypes;
16778             if(!vt[this.vtype](value, this)){
16779                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16780                 return false;
16781             }
16782         }
16783         if(typeof this.validator == "function"){
16784             var msg = this.validator(value);
16785             if(msg !== true){
16786                 this.markInvalid(msg);
16787                 return false;
16788             }
16789         }
16790         if(this.regex && !this.regex.test(value)){
16791             this.markInvalid(this.regexText);
16792             return false;
16793         }
16794         return true;
16795     },
16796
16797     /**
16798      * Selects text in this field
16799      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16800      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16801      */
16802     selectText : function(start, end){
16803         var v = this.getRawValue();
16804         if(v.length > 0){
16805             start = start === undefined ? 0 : start;
16806             end = end === undefined ? v.length : end;
16807             var d = this.el.dom;
16808             if(d.setSelectionRange){
16809                 d.setSelectionRange(start, end);
16810             }else if(d.createTextRange){
16811                 var range = d.createTextRange();
16812                 range.moveStart("character", start);
16813                 range.moveEnd("character", v.length-end);
16814                 range.select();
16815             }
16816         }
16817     },
16818
16819     /**
16820      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16821      * This only takes effect if grow = true, and fires the autosize event.
16822      */
16823     autoSize : function(){
16824         if(!this.grow || !this.rendered){
16825             return;
16826         }
16827         if(!this.metrics){
16828             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16829         }
16830         var el = this.el;
16831         var v = el.dom.value;
16832         var d = document.createElement('div');
16833         d.appendChild(document.createTextNode(v));
16834         v = d.innerHTML;
16835         d = null;
16836         v += "&#160;";
16837         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16838         this.el.setWidth(w);
16839         this.fireEvent("autosize", this, w);
16840     },
16841     
16842     // private
16843     SafariOnKeyDown : function(event)
16844     {
16845         // this is a workaround for a password hang bug on chrome/ webkit.
16846         
16847         var isSelectAll = false;
16848         
16849         if(this.el.dom.selectionEnd > 0){
16850             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16851         }
16852         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16853             event.preventDefault();
16854             this.setValue('');
16855             return;
16856         }
16857         
16858         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16859             
16860             event.preventDefault();
16861             // this is very hacky as keydown always get's upper case.
16862             
16863             var cc = String.fromCharCode(event.getCharCode());
16864             
16865             
16866             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16867             
16868         }
16869         
16870         
16871     }
16872 });/*
16873  * Based on:
16874  * Ext JS Library 1.1.1
16875  * Copyright(c) 2006-2007, Ext JS, LLC.
16876  *
16877  * Originally Released Under LGPL - original licence link has changed is not relivant.
16878  *
16879  * Fork - LGPL
16880  * <script type="text/javascript">
16881  */
16882  
16883 /**
16884  * @class Roo.form.Hidden
16885  * @extends Roo.form.TextField
16886  * Simple Hidden element used on forms 
16887  * 
16888  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16889  * 
16890  * @constructor
16891  * Creates a new Hidden form element.
16892  * @param {Object} config Configuration options
16893  */
16894
16895
16896
16897 // easy hidden field...
16898 Roo.form.Hidden = function(config){
16899     Roo.form.Hidden.superclass.constructor.call(this, config);
16900 };
16901   
16902 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16903     fieldLabel:      '',
16904     inputType:      'hidden',
16905     width:          50,
16906     allowBlank:     true,
16907     labelSeparator: '',
16908     hidden:         true,
16909     itemCls :       'x-form-item-display-none'
16910
16911
16912 });
16913
16914
16915 /*
16916  * Based on:
16917  * Ext JS Library 1.1.1
16918  * Copyright(c) 2006-2007, Ext JS, LLC.
16919  *
16920  * Originally Released Under LGPL - original licence link has changed is not relivant.
16921  *
16922  * Fork - LGPL
16923  * <script type="text/javascript">
16924  */
16925  
16926 /**
16927  * @class Roo.form.TriggerField
16928  * @extends Roo.form.TextField
16929  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16930  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16931  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16932  * for which you can provide a custom implementation.  For example:
16933  * <pre><code>
16934 var trigger = new Roo.form.TriggerField();
16935 trigger.onTriggerClick = myTriggerFn;
16936 trigger.applyTo('my-field');
16937 </code></pre>
16938  *
16939  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16940  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16941  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16942  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16943  * @constructor
16944  * Create a new TriggerField.
16945  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16946  * to the base TextField)
16947  */
16948 Roo.form.TriggerField = function(config){
16949     this.mimicing = false;
16950     Roo.form.TriggerField.superclass.constructor.call(this, config);
16951 };
16952
16953 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16954     /**
16955      * @cfg {String} triggerClass A CSS class to apply to the trigger
16956      */
16957     /**
16958      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16959      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16960      */
16961     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16962     /**
16963      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16964      */
16965     hideTrigger:false,
16966
16967     /** @cfg {Boolean} grow @hide */
16968     /** @cfg {Number} growMin @hide */
16969     /** @cfg {Number} growMax @hide */
16970
16971     /**
16972      * @hide 
16973      * @method
16974      */
16975     autoSize: Roo.emptyFn,
16976     // private
16977     monitorTab : true,
16978     // private
16979     deferHeight : true,
16980
16981     
16982     actionMode : 'wrap',
16983     // private
16984     onResize : function(w, h){
16985         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16986         if(typeof w == 'number'){
16987             var x = w - this.trigger.getWidth();
16988             this.el.setWidth(this.adjustWidth('input', x));
16989             this.trigger.setStyle('left', x+'px');
16990         }
16991     },
16992
16993     // private
16994     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16995
16996     // private
16997     getResizeEl : function(){
16998         return this.wrap;
16999     },
17000
17001     // private
17002     getPositionEl : function(){
17003         return this.wrap;
17004     },
17005
17006     // private
17007     alignErrorIcon : function(){
17008         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17009     },
17010
17011     // private
17012     onRender : function(ct, position){
17013         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17014         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17015         this.trigger = this.wrap.createChild(this.triggerConfig ||
17016                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17017         if(this.hideTrigger){
17018             this.trigger.setDisplayed(false);
17019         }
17020         this.initTrigger();
17021         if(!this.width){
17022             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17023         }
17024     },
17025
17026     // private
17027     initTrigger : function(){
17028         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17029         this.trigger.addClassOnOver('x-form-trigger-over');
17030         this.trigger.addClassOnClick('x-form-trigger-click');
17031     },
17032
17033     // private
17034     onDestroy : function(){
17035         if(this.trigger){
17036             this.trigger.removeAllListeners();
17037             this.trigger.remove();
17038         }
17039         if(this.wrap){
17040             this.wrap.remove();
17041         }
17042         Roo.form.TriggerField.superclass.onDestroy.call(this);
17043     },
17044
17045     // private
17046     onFocus : function(){
17047         Roo.form.TriggerField.superclass.onFocus.call(this);
17048         if(!this.mimicing){
17049             this.wrap.addClass('x-trigger-wrap-focus');
17050             this.mimicing = true;
17051             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17052             if(this.monitorTab){
17053                 this.el.on("keydown", this.checkTab, this);
17054             }
17055         }
17056     },
17057
17058     // private
17059     checkTab : function(e){
17060         if(e.getKey() == e.TAB){
17061             this.triggerBlur();
17062         }
17063     },
17064
17065     // private
17066     onBlur : function(){
17067         // do nothing
17068     },
17069
17070     // private
17071     mimicBlur : function(e, t){
17072         if(!this.wrap.contains(t) && this.validateBlur()){
17073             this.triggerBlur();
17074         }
17075     },
17076
17077     // private
17078     triggerBlur : function(){
17079         this.mimicing = false;
17080         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17081         if(this.monitorTab){
17082             this.el.un("keydown", this.checkTab, this);
17083         }
17084         this.wrap.removeClass('x-trigger-wrap-focus');
17085         Roo.form.TriggerField.superclass.onBlur.call(this);
17086     },
17087
17088     // private
17089     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17090     validateBlur : function(e, t){
17091         return true;
17092     },
17093
17094     // private
17095     onDisable : function(){
17096         Roo.form.TriggerField.superclass.onDisable.call(this);
17097         if(this.wrap){
17098             this.wrap.addClass('x-item-disabled');
17099         }
17100     },
17101
17102     // private
17103     onEnable : function(){
17104         Roo.form.TriggerField.superclass.onEnable.call(this);
17105         if(this.wrap){
17106             this.wrap.removeClass('x-item-disabled');
17107         }
17108     },
17109
17110     // private
17111     onShow : function(){
17112         var ae = this.getActionEl();
17113         
17114         if(ae){
17115             ae.dom.style.display = '';
17116             ae.dom.style.visibility = 'visible';
17117         }
17118     },
17119
17120     // private
17121     
17122     onHide : function(){
17123         var ae = this.getActionEl();
17124         ae.dom.style.display = 'none';
17125     },
17126
17127     /**
17128      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17129      * by an implementing function.
17130      * @method
17131      * @param {EventObject} e
17132      */
17133     onTriggerClick : Roo.emptyFn
17134 });
17135
17136 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17137 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17138 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17139 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17140     initComponent : function(){
17141         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17142
17143         this.triggerConfig = {
17144             tag:'span', cls:'x-form-twin-triggers', cn:[
17145             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17146             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17147         ]};
17148     },
17149
17150     getTrigger : function(index){
17151         return this.triggers[index];
17152     },
17153
17154     initTrigger : function(){
17155         var ts = this.trigger.select('.x-form-trigger', true);
17156         this.wrap.setStyle('overflow', 'hidden');
17157         var triggerField = this;
17158         ts.each(function(t, all, index){
17159             t.hide = function(){
17160                 var w = triggerField.wrap.getWidth();
17161                 this.dom.style.display = 'none';
17162                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17163             };
17164             t.show = function(){
17165                 var w = triggerField.wrap.getWidth();
17166                 this.dom.style.display = '';
17167                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17168             };
17169             var triggerIndex = 'Trigger'+(index+1);
17170
17171             if(this['hide'+triggerIndex]){
17172                 t.dom.style.display = 'none';
17173             }
17174             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17175             t.addClassOnOver('x-form-trigger-over');
17176             t.addClassOnClick('x-form-trigger-click');
17177         }, this);
17178         this.triggers = ts.elements;
17179     },
17180
17181     onTrigger1Click : Roo.emptyFn,
17182     onTrigger2Click : Roo.emptyFn
17183 });/*
17184  * Based on:
17185  * Ext JS Library 1.1.1
17186  * Copyright(c) 2006-2007, Ext JS, LLC.
17187  *
17188  * Originally Released Under LGPL - original licence link has changed is not relivant.
17189  *
17190  * Fork - LGPL
17191  * <script type="text/javascript">
17192  */
17193  
17194 /**
17195  * @class Roo.form.TextArea
17196  * @extends Roo.form.TextField
17197  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17198  * support for auto-sizing.
17199  * @constructor
17200  * Creates a new TextArea
17201  * @param {Object} config Configuration options
17202  */
17203 Roo.form.TextArea = function(config){
17204     Roo.form.TextArea.superclass.constructor.call(this, config);
17205     // these are provided exchanges for backwards compat
17206     // minHeight/maxHeight were replaced by growMin/growMax to be
17207     // compatible with TextField growing config values
17208     if(this.minHeight !== undefined){
17209         this.growMin = this.minHeight;
17210     }
17211     if(this.maxHeight !== undefined){
17212         this.growMax = this.maxHeight;
17213     }
17214 };
17215
17216 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17217     /**
17218      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17219      */
17220     growMin : 60,
17221     /**
17222      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17223      */
17224     growMax: 1000,
17225     /**
17226      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17227      * in the field (equivalent to setting overflow: hidden, defaults to false)
17228      */
17229     preventScrollbars: false,
17230     /**
17231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17232      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17233      */
17234
17235     // private
17236     onRender : function(ct, position){
17237         if(!this.el){
17238             this.defaultAutoCreate = {
17239                 tag: "textarea",
17240                 style:"width:300px;height:60px;",
17241                 autocomplete: "new-password"
17242             };
17243         }
17244         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17245         if(this.grow){
17246             this.textSizeEl = Roo.DomHelper.append(document.body, {
17247                 tag: "pre", cls: "x-form-grow-sizer"
17248             });
17249             if(this.preventScrollbars){
17250                 this.el.setStyle("overflow", "hidden");
17251             }
17252             this.el.setHeight(this.growMin);
17253         }
17254     },
17255
17256     onDestroy : function(){
17257         if(this.textSizeEl){
17258             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17259         }
17260         Roo.form.TextArea.superclass.onDestroy.call(this);
17261     },
17262
17263     // private
17264     onKeyUp : function(e){
17265         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17266             this.autoSize();
17267         }
17268     },
17269
17270     /**
17271      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17272      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17273      */
17274     autoSize : function(){
17275         if(!this.grow || !this.textSizeEl){
17276             return;
17277         }
17278         var el = this.el;
17279         var v = el.dom.value;
17280         var ts = this.textSizeEl;
17281
17282         ts.innerHTML = '';
17283         ts.appendChild(document.createTextNode(v));
17284         v = ts.innerHTML;
17285
17286         Roo.fly(ts).setWidth(this.el.getWidth());
17287         if(v.length < 1){
17288             v = "&#160;&#160;";
17289         }else{
17290             if(Roo.isIE){
17291                 v = v.replace(/\n/g, '<p>&#160;</p>');
17292             }
17293             v += "&#160;\n&#160;";
17294         }
17295         ts.innerHTML = v;
17296         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17297         if(h != this.lastHeight){
17298             this.lastHeight = h;
17299             this.el.setHeight(h);
17300             this.fireEvent("autosize", this, h);
17301         }
17302     }
17303 });/*
17304  * Based on:
17305  * Ext JS Library 1.1.1
17306  * Copyright(c) 2006-2007, Ext JS, LLC.
17307  *
17308  * Originally Released Under LGPL - original licence link has changed is not relivant.
17309  *
17310  * Fork - LGPL
17311  * <script type="text/javascript">
17312  */
17313  
17314
17315 /**
17316  * @class Roo.form.NumberField
17317  * @extends Roo.form.TextField
17318  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17319  * @constructor
17320  * Creates a new NumberField
17321  * @param {Object} config Configuration options
17322  */
17323 Roo.form.NumberField = function(config){
17324     Roo.form.NumberField.superclass.constructor.call(this, config);
17325 };
17326
17327 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17328     /**
17329      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17330      */
17331     fieldClass: "x-form-field x-form-num-field",
17332     /**
17333      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17334      */
17335     allowDecimals : true,
17336     /**
17337      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17338      */
17339     decimalSeparator : ".",
17340     /**
17341      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17342      */
17343     decimalPrecision : 2,
17344     /**
17345      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17346      */
17347     allowNegative : true,
17348     /**
17349      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17350      */
17351     minValue : Number.NEGATIVE_INFINITY,
17352     /**
17353      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17354      */
17355     maxValue : Number.MAX_VALUE,
17356     /**
17357      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17358      */
17359     minText : "The minimum value for this field is {0}",
17360     /**
17361      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17362      */
17363     maxText : "The maximum value for this field is {0}",
17364     /**
17365      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17366      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17367      */
17368     nanText : "{0} is not a valid number",
17369
17370     // private
17371     initEvents : function(){
17372         Roo.form.NumberField.superclass.initEvents.call(this);
17373         var allowed = "0123456789";
17374         if(this.allowDecimals){
17375             allowed += this.decimalSeparator;
17376         }
17377         if(this.allowNegative){
17378             allowed += "-";
17379         }
17380         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17381         var keyPress = function(e){
17382             var k = e.getKey();
17383             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17384                 return;
17385             }
17386             var c = e.getCharCode();
17387             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17388                 e.stopEvent();
17389             }
17390         };
17391         this.el.on("keypress", keyPress, this);
17392     },
17393
17394     // private
17395     validateValue : function(value){
17396         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17397             return false;
17398         }
17399         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17400              return true;
17401         }
17402         var num = this.parseValue(value);
17403         if(isNaN(num)){
17404             this.markInvalid(String.format(this.nanText, value));
17405             return false;
17406         }
17407         if(num < this.minValue){
17408             this.markInvalid(String.format(this.minText, this.minValue));
17409             return false;
17410         }
17411         if(num > this.maxValue){
17412             this.markInvalid(String.format(this.maxText, this.maxValue));
17413             return false;
17414         }
17415         return true;
17416     },
17417
17418     getValue : function(){
17419         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17420     },
17421
17422     // private
17423     parseValue : function(value){
17424         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17425         return isNaN(value) ? '' : value;
17426     },
17427
17428     // private
17429     fixPrecision : function(value){
17430         var nan = isNaN(value);
17431         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17432             return nan ? '' : value;
17433         }
17434         return parseFloat(value).toFixed(this.decimalPrecision);
17435     },
17436
17437     setValue : function(v){
17438         v = this.fixPrecision(v);
17439         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17440     },
17441
17442     // private
17443     decimalPrecisionFcn : function(v){
17444         return Math.floor(v);
17445     },
17446
17447     beforeBlur : function(){
17448         var v = this.parseValue(this.getRawValue());
17449         if(v){
17450             this.setValue(v);
17451         }
17452     }
17453 });/*
17454  * Based on:
17455  * Ext JS Library 1.1.1
17456  * Copyright(c) 2006-2007, Ext JS, LLC.
17457  *
17458  * Originally Released Under LGPL - original licence link has changed is not relivant.
17459  *
17460  * Fork - LGPL
17461  * <script type="text/javascript">
17462  */
17463  
17464 /**
17465  * @class Roo.form.DateField
17466  * @extends Roo.form.TriggerField
17467  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17468 * @constructor
17469 * Create a new DateField
17470 * @param {Object} config
17471  */
17472 Roo.form.DateField = function(config)
17473 {
17474     Roo.form.DateField.superclass.constructor.call(this, config);
17475     
17476       this.addEvents({
17477          
17478         /**
17479          * @event select
17480          * Fires when a date is selected
17481              * @param {Roo.form.DateField} combo This combo box
17482              * @param {Date} date The date selected
17483              */
17484         'select' : true
17485          
17486     });
17487     
17488     
17489     if(typeof this.minValue == "string") {
17490         this.minValue = this.parseDate(this.minValue);
17491     }
17492     if(typeof this.maxValue == "string") {
17493         this.maxValue = this.parseDate(this.maxValue);
17494     }
17495     this.ddMatch = null;
17496     if(this.disabledDates){
17497         var dd = this.disabledDates;
17498         var re = "(?:";
17499         for(var i = 0; i < dd.length; i++){
17500             re += dd[i];
17501             if(i != dd.length-1) {
17502                 re += "|";
17503             }
17504         }
17505         this.ddMatch = new RegExp(re + ")");
17506     }
17507 };
17508
17509 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17510     /**
17511      * @cfg {String} format
17512      * The default date format string which can be overriden for localization support.  The format must be
17513      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17514      */
17515     format : "m/d/y",
17516     /**
17517      * @cfg {String} altFormats
17518      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17519      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17520      */
17521     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17522     /**
17523      * @cfg {Array} disabledDays
17524      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17525      */
17526     disabledDays : null,
17527     /**
17528      * @cfg {String} disabledDaysText
17529      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17530      */
17531     disabledDaysText : "Disabled",
17532     /**
17533      * @cfg {Array} disabledDates
17534      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17535      * expression so they are very powerful. Some examples:
17536      * <ul>
17537      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17538      * <li>["03/08", "09/16"] would disable those days for every year</li>
17539      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17540      * <li>["03/../2006"] would disable every day in March 2006</li>
17541      * <li>["^03"] would disable every day in every March</li>
17542      * </ul>
17543      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17544      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17545      */
17546     disabledDates : null,
17547     /**
17548      * @cfg {String} disabledDatesText
17549      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17550      */
17551     disabledDatesText : "Disabled",
17552     /**
17553      * @cfg {Date/String} minValue
17554      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17555      * valid format (defaults to null).
17556      */
17557     minValue : null,
17558     /**
17559      * @cfg {Date/String} maxValue
17560      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17561      * valid format (defaults to null).
17562      */
17563     maxValue : null,
17564     /**
17565      * @cfg {String} minText
17566      * The error text to display when the date in the cell is before minValue (defaults to
17567      * 'The date in this field must be after {minValue}').
17568      */
17569     minText : "The date in this field must be equal to or after {0}",
17570     /**
17571      * @cfg {String} maxText
17572      * The error text to display when the date in the cell is after maxValue (defaults to
17573      * 'The date in this field must be before {maxValue}').
17574      */
17575     maxText : "The date in this field must be equal to or before {0}",
17576     /**
17577      * @cfg {String} invalidText
17578      * The error text to display when the date in the field is invalid (defaults to
17579      * '{value} is not a valid date - it must be in the format {format}').
17580      */
17581     invalidText : "{0} is not a valid date - it must be in the format {1}",
17582     /**
17583      * @cfg {String} triggerClass
17584      * An additional CSS class used to style the trigger button.  The trigger will always get the
17585      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17586      * which displays a calendar icon).
17587      */
17588     triggerClass : 'x-form-date-trigger',
17589     
17590
17591     /**
17592      * @cfg {Boolean} useIso
17593      * if enabled, then the date field will use a hidden field to store the 
17594      * real value as iso formated date. default (false)
17595      */ 
17596     useIso : false,
17597     /**
17598      * @cfg {String/Object} autoCreate
17599      * A DomHelper element spec, or true for a default element spec (defaults to
17600      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17601      */ 
17602     // private
17603     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17604     
17605     // private
17606     hiddenField: false,
17607     
17608     onRender : function(ct, position)
17609     {
17610         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17611         if (this.useIso) {
17612             //this.el.dom.removeAttribute('name'); 
17613             Roo.log("Changing name?");
17614             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17615             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17616                     'before', true);
17617             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17618             // prevent input submission
17619             this.hiddenName = this.name;
17620         }
17621             
17622             
17623     },
17624     
17625     // private
17626     validateValue : function(value)
17627     {
17628         value = this.formatDate(value);
17629         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17630             Roo.log('super failed');
17631             return false;
17632         }
17633         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17634              return true;
17635         }
17636         var svalue = value;
17637         value = this.parseDate(value);
17638         if(!value){
17639             Roo.log('parse date failed' + svalue);
17640             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17641             return false;
17642         }
17643         var time = value.getTime();
17644         if(this.minValue && time < this.minValue.getTime()){
17645             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17646             return false;
17647         }
17648         if(this.maxValue && time > this.maxValue.getTime()){
17649             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17650             return false;
17651         }
17652         if(this.disabledDays){
17653             var day = value.getDay();
17654             for(var i = 0; i < this.disabledDays.length; i++) {
17655                 if(day === this.disabledDays[i]){
17656                     this.markInvalid(this.disabledDaysText);
17657                     return false;
17658                 }
17659             }
17660         }
17661         var fvalue = this.formatDate(value);
17662         if(this.ddMatch && this.ddMatch.test(fvalue)){
17663             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17664             return false;
17665         }
17666         return true;
17667     },
17668
17669     // private
17670     // Provides logic to override the default TriggerField.validateBlur which just returns true
17671     validateBlur : function(){
17672         return !this.menu || !this.menu.isVisible();
17673     },
17674     
17675     getName: function()
17676     {
17677         // returns hidden if it's set..
17678         if (!this.rendered) {return ''};
17679         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17680         
17681     },
17682
17683     /**
17684      * Returns the current date value of the date field.
17685      * @return {Date} The date value
17686      */
17687     getValue : function(){
17688         
17689         return  this.hiddenField ?
17690                 this.hiddenField.value :
17691                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17692     },
17693
17694     /**
17695      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17696      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17697      * (the default format used is "m/d/y").
17698      * <br />Usage:
17699      * <pre><code>
17700 //All of these calls set the same date value (May 4, 2006)
17701
17702 //Pass a date object:
17703 var dt = new Date('5/4/06');
17704 dateField.setValue(dt);
17705
17706 //Pass a date string (default format):
17707 dateField.setValue('5/4/06');
17708
17709 //Pass a date string (custom format):
17710 dateField.format = 'Y-m-d';
17711 dateField.setValue('2006-5-4');
17712 </code></pre>
17713      * @param {String/Date} date The date or valid date string
17714      */
17715     setValue : function(date){
17716         if (this.hiddenField) {
17717             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17718         }
17719         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17720         // make sure the value field is always stored as a date..
17721         this.value = this.parseDate(date);
17722         
17723         
17724     },
17725
17726     // private
17727     parseDate : function(value){
17728         if(!value || value instanceof Date){
17729             return value;
17730         }
17731         var v = Date.parseDate(value, this.format);
17732          if (!v && this.useIso) {
17733             v = Date.parseDate(value, 'Y-m-d');
17734         }
17735         if(!v && this.altFormats){
17736             if(!this.altFormatsArray){
17737                 this.altFormatsArray = this.altFormats.split("|");
17738             }
17739             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17740                 v = Date.parseDate(value, this.altFormatsArray[i]);
17741             }
17742         }
17743         return v;
17744     },
17745
17746     // private
17747     formatDate : function(date, fmt){
17748         return (!date || !(date instanceof Date)) ?
17749                date : date.dateFormat(fmt || this.format);
17750     },
17751
17752     // private
17753     menuListeners : {
17754         select: function(m, d){
17755             
17756             this.setValue(d);
17757             this.fireEvent('select', this, d);
17758         },
17759         show : function(){ // retain focus styling
17760             this.onFocus();
17761         },
17762         hide : function(){
17763             this.focus.defer(10, this);
17764             var ml = this.menuListeners;
17765             this.menu.un("select", ml.select,  this);
17766             this.menu.un("show", ml.show,  this);
17767             this.menu.un("hide", ml.hide,  this);
17768         }
17769     },
17770
17771     // private
17772     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17773     onTriggerClick : function(){
17774         if(this.disabled){
17775             return;
17776         }
17777         if(this.menu == null){
17778             this.menu = new Roo.menu.DateMenu();
17779         }
17780         Roo.apply(this.menu.picker,  {
17781             showClear: this.allowBlank,
17782             minDate : this.minValue,
17783             maxDate : this.maxValue,
17784             disabledDatesRE : this.ddMatch,
17785             disabledDatesText : this.disabledDatesText,
17786             disabledDays : this.disabledDays,
17787             disabledDaysText : this.disabledDaysText,
17788             format : this.useIso ? 'Y-m-d' : this.format,
17789             minText : String.format(this.minText, this.formatDate(this.minValue)),
17790             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17791         });
17792         this.menu.on(Roo.apply({}, this.menuListeners, {
17793             scope:this
17794         }));
17795         this.menu.picker.setValue(this.getValue() || new Date());
17796         this.menu.show(this.el, "tl-bl?");
17797     },
17798
17799     beforeBlur : function(){
17800         var v = this.parseDate(this.getRawValue());
17801         if(v){
17802             this.setValue(v);
17803         }
17804     },
17805
17806     /*@
17807      * overide
17808      * 
17809      */
17810     isDirty : function() {
17811         if(this.disabled) {
17812             return false;
17813         }
17814         
17815         if(typeof(this.startValue) === 'undefined'){
17816             return false;
17817         }
17818         
17819         return String(this.getValue()) !== String(this.startValue);
17820         
17821     },
17822     // @overide
17823     cleanLeadingSpace : function(e)
17824     {
17825        return;
17826     }
17827     
17828 });/*
17829  * Based on:
17830  * Ext JS Library 1.1.1
17831  * Copyright(c) 2006-2007, Ext JS, LLC.
17832  *
17833  * Originally Released Under LGPL - original licence link has changed is not relivant.
17834  *
17835  * Fork - LGPL
17836  * <script type="text/javascript">
17837  */
17838  
17839 /**
17840  * @class Roo.form.MonthField
17841  * @extends Roo.form.TriggerField
17842  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17843 * @constructor
17844 * Create a new MonthField
17845 * @param {Object} config
17846  */
17847 Roo.form.MonthField = function(config){
17848     
17849     Roo.form.MonthField.superclass.constructor.call(this, config);
17850     
17851       this.addEvents({
17852          
17853         /**
17854          * @event select
17855          * Fires when a date is selected
17856              * @param {Roo.form.MonthFieeld} combo This combo box
17857              * @param {Date} date The date selected
17858              */
17859         'select' : true
17860          
17861     });
17862     
17863     
17864     if(typeof this.minValue == "string") {
17865         this.minValue = this.parseDate(this.minValue);
17866     }
17867     if(typeof this.maxValue == "string") {
17868         this.maxValue = this.parseDate(this.maxValue);
17869     }
17870     this.ddMatch = null;
17871     if(this.disabledDates){
17872         var dd = this.disabledDates;
17873         var re = "(?:";
17874         for(var i = 0; i < dd.length; i++){
17875             re += dd[i];
17876             if(i != dd.length-1) {
17877                 re += "|";
17878             }
17879         }
17880         this.ddMatch = new RegExp(re + ")");
17881     }
17882 };
17883
17884 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17885     /**
17886      * @cfg {String} format
17887      * The default date format string which can be overriden for localization support.  The format must be
17888      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17889      */
17890     format : "M Y",
17891     /**
17892      * @cfg {String} altFormats
17893      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17894      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17895      */
17896     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17897     /**
17898      * @cfg {Array} disabledDays
17899      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17900      */
17901     disabledDays : [0,1,2,3,4,5,6],
17902     /**
17903      * @cfg {String} disabledDaysText
17904      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17905      */
17906     disabledDaysText : "Disabled",
17907     /**
17908      * @cfg {Array} disabledDates
17909      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17910      * expression so they are very powerful. Some examples:
17911      * <ul>
17912      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17913      * <li>["03/08", "09/16"] would disable those days for every year</li>
17914      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17915      * <li>["03/../2006"] would disable every day in March 2006</li>
17916      * <li>["^03"] would disable every day in every March</li>
17917      * </ul>
17918      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17919      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17920      */
17921     disabledDates : null,
17922     /**
17923      * @cfg {String} disabledDatesText
17924      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17925      */
17926     disabledDatesText : "Disabled",
17927     /**
17928      * @cfg {Date/String} minValue
17929      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17930      * valid format (defaults to null).
17931      */
17932     minValue : null,
17933     /**
17934      * @cfg {Date/String} maxValue
17935      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17936      * valid format (defaults to null).
17937      */
17938     maxValue : null,
17939     /**
17940      * @cfg {String} minText
17941      * The error text to display when the date in the cell is before minValue (defaults to
17942      * 'The date in this field must be after {minValue}').
17943      */
17944     minText : "The date in this field must be equal to or after {0}",
17945     /**
17946      * @cfg {String} maxTextf
17947      * The error text to display when the date in the cell is after maxValue (defaults to
17948      * 'The date in this field must be before {maxValue}').
17949      */
17950     maxText : "The date in this field must be equal to or before {0}",
17951     /**
17952      * @cfg {String} invalidText
17953      * The error text to display when the date in the field is invalid (defaults to
17954      * '{value} is not a valid date - it must be in the format {format}').
17955      */
17956     invalidText : "{0} is not a valid date - it must be in the format {1}",
17957     /**
17958      * @cfg {String} triggerClass
17959      * An additional CSS class used to style the trigger button.  The trigger will always get the
17960      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17961      * which displays a calendar icon).
17962      */
17963     triggerClass : 'x-form-date-trigger',
17964     
17965
17966     /**
17967      * @cfg {Boolean} useIso
17968      * if enabled, then the date field will use a hidden field to store the 
17969      * real value as iso formated date. default (true)
17970      */ 
17971     useIso : true,
17972     /**
17973      * @cfg {String/Object} autoCreate
17974      * A DomHelper element spec, or true for a default element spec (defaults to
17975      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17976      */ 
17977     // private
17978     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17979     
17980     // private
17981     hiddenField: false,
17982     
17983     hideMonthPicker : false,
17984     
17985     onRender : function(ct, position)
17986     {
17987         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17988         if (this.useIso) {
17989             this.el.dom.removeAttribute('name'); 
17990             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17991                     'before', true);
17992             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17993             // prevent input submission
17994             this.hiddenName = this.name;
17995         }
17996             
17997             
17998     },
17999     
18000     // private
18001     validateValue : function(value)
18002     {
18003         value = this.formatDate(value);
18004         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18005             return false;
18006         }
18007         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18008              return true;
18009         }
18010         var svalue = value;
18011         value = this.parseDate(value);
18012         if(!value){
18013             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18014             return false;
18015         }
18016         var time = value.getTime();
18017         if(this.minValue && time < this.minValue.getTime()){
18018             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18019             return false;
18020         }
18021         if(this.maxValue && time > this.maxValue.getTime()){
18022             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18023             return false;
18024         }
18025         /*if(this.disabledDays){
18026             var day = value.getDay();
18027             for(var i = 0; i < this.disabledDays.length; i++) {
18028                 if(day === this.disabledDays[i]){
18029                     this.markInvalid(this.disabledDaysText);
18030                     return false;
18031                 }
18032             }
18033         }
18034         */
18035         var fvalue = this.formatDate(value);
18036         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18037             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18038             return false;
18039         }
18040         */
18041         return true;
18042     },
18043
18044     // private
18045     // Provides logic to override the default TriggerField.validateBlur which just returns true
18046     validateBlur : function(){
18047         return !this.menu || !this.menu.isVisible();
18048     },
18049
18050     /**
18051      * Returns the current date value of the date field.
18052      * @return {Date} The date value
18053      */
18054     getValue : function(){
18055         
18056         
18057         
18058         return  this.hiddenField ?
18059                 this.hiddenField.value :
18060                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18061     },
18062
18063     /**
18064      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18065      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18066      * (the default format used is "m/d/y").
18067      * <br />Usage:
18068      * <pre><code>
18069 //All of these calls set the same date value (May 4, 2006)
18070
18071 //Pass a date object:
18072 var dt = new Date('5/4/06');
18073 monthField.setValue(dt);
18074
18075 //Pass a date string (default format):
18076 monthField.setValue('5/4/06');
18077
18078 //Pass a date string (custom format):
18079 monthField.format = 'Y-m-d';
18080 monthField.setValue('2006-5-4');
18081 </code></pre>
18082      * @param {String/Date} date The date or valid date string
18083      */
18084     setValue : function(date){
18085         Roo.log('month setValue' + date);
18086         // can only be first of month..
18087         
18088         var val = this.parseDate(date);
18089         
18090         if (this.hiddenField) {
18091             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18092         }
18093         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18094         this.value = this.parseDate(date);
18095     },
18096
18097     // private
18098     parseDate : function(value){
18099         if(!value || value instanceof Date){
18100             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18101             return value;
18102         }
18103         var v = Date.parseDate(value, this.format);
18104         if (!v && this.useIso) {
18105             v = Date.parseDate(value, 'Y-m-d');
18106         }
18107         if (v) {
18108             // 
18109             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18110         }
18111         
18112         
18113         if(!v && this.altFormats){
18114             if(!this.altFormatsArray){
18115                 this.altFormatsArray = this.altFormats.split("|");
18116             }
18117             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18118                 v = Date.parseDate(value, this.altFormatsArray[i]);
18119             }
18120         }
18121         return v;
18122     },
18123
18124     // private
18125     formatDate : function(date, fmt){
18126         return (!date || !(date instanceof Date)) ?
18127                date : date.dateFormat(fmt || this.format);
18128     },
18129
18130     // private
18131     menuListeners : {
18132         select: function(m, d){
18133             this.setValue(d);
18134             this.fireEvent('select', this, d);
18135         },
18136         show : function(){ // retain focus styling
18137             this.onFocus();
18138         },
18139         hide : function(){
18140             this.focus.defer(10, this);
18141             var ml = this.menuListeners;
18142             this.menu.un("select", ml.select,  this);
18143             this.menu.un("show", ml.show,  this);
18144             this.menu.un("hide", ml.hide,  this);
18145         }
18146     },
18147     // private
18148     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18149     onTriggerClick : function(){
18150         if(this.disabled){
18151             return;
18152         }
18153         if(this.menu == null){
18154             this.menu = new Roo.menu.DateMenu();
18155            
18156         }
18157         
18158         Roo.apply(this.menu.picker,  {
18159             
18160             showClear: this.allowBlank,
18161             minDate : this.minValue,
18162             maxDate : this.maxValue,
18163             disabledDatesRE : this.ddMatch,
18164             disabledDatesText : this.disabledDatesText,
18165             
18166             format : this.useIso ? 'Y-m-d' : this.format,
18167             minText : String.format(this.minText, this.formatDate(this.minValue)),
18168             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18169             
18170         });
18171          this.menu.on(Roo.apply({}, this.menuListeners, {
18172             scope:this
18173         }));
18174        
18175         
18176         var m = this.menu;
18177         var p = m.picker;
18178         
18179         // hide month picker get's called when we called by 'before hide';
18180         
18181         var ignorehide = true;
18182         p.hideMonthPicker  = function(disableAnim){
18183             if (ignorehide) {
18184                 return;
18185             }
18186              if(this.monthPicker){
18187                 Roo.log("hideMonthPicker called");
18188                 if(disableAnim === true){
18189                     this.monthPicker.hide();
18190                 }else{
18191                     this.monthPicker.slideOut('t', {duration:.2});
18192                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18193                     p.fireEvent("select", this, this.value);
18194                     m.hide();
18195                 }
18196             }
18197         }
18198         
18199         Roo.log('picker set value');
18200         Roo.log(this.getValue());
18201         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18202         m.show(this.el, 'tl-bl?');
18203         ignorehide  = false;
18204         // this will trigger hideMonthPicker..
18205         
18206         
18207         // hidden the day picker
18208         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18209         
18210         
18211         
18212       
18213         
18214         p.showMonthPicker.defer(100, p);
18215     
18216         
18217        
18218     },
18219
18220     beforeBlur : function(){
18221         var v = this.parseDate(this.getRawValue());
18222         if(v){
18223             this.setValue(v);
18224         }
18225     }
18226
18227     /** @cfg {Boolean} grow @hide */
18228     /** @cfg {Number} growMin @hide */
18229     /** @cfg {Number} growMax @hide */
18230     /**
18231      * @hide
18232      * @method autoSize
18233      */
18234 });/*
18235  * Based on:
18236  * Ext JS Library 1.1.1
18237  * Copyright(c) 2006-2007, Ext JS, LLC.
18238  *
18239  * Originally Released Under LGPL - original licence link has changed is not relivant.
18240  *
18241  * Fork - LGPL
18242  * <script type="text/javascript">
18243  */
18244  
18245
18246 /**
18247  * @class Roo.form.ComboBox
18248  * @extends Roo.form.TriggerField
18249  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18250  * @constructor
18251  * Create a new ComboBox.
18252  * @param {Object} config Configuration options
18253  */
18254 Roo.form.ComboBox = function(config){
18255     Roo.form.ComboBox.superclass.constructor.call(this, config);
18256     this.addEvents({
18257         /**
18258          * @event expand
18259          * Fires when the dropdown list is expanded
18260              * @param {Roo.form.ComboBox} combo This combo box
18261              */
18262         'expand' : true,
18263         /**
18264          * @event collapse
18265          * Fires when the dropdown list is collapsed
18266              * @param {Roo.form.ComboBox} combo This combo box
18267              */
18268         'collapse' : true,
18269         /**
18270          * @event beforeselect
18271          * Fires before a list item is selected. Return false to cancel the selection.
18272              * @param {Roo.form.ComboBox} combo This combo box
18273              * @param {Roo.data.Record} record The data record returned from the underlying store
18274              * @param {Number} index The index of the selected item in the dropdown list
18275              */
18276         'beforeselect' : true,
18277         /**
18278          * @event select
18279          * Fires when a list item is selected
18280              * @param {Roo.form.ComboBox} combo This combo box
18281              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18282              * @param {Number} index The index of the selected item in the dropdown list
18283              */
18284         'select' : true,
18285         /**
18286          * @event beforequery
18287          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18288          * The event object passed has these properties:
18289              * @param {Roo.form.ComboBox} combo This combo box
18290              * @param {String} query The query
18291              * @param {Boolean} forceAll true to force "all" query
18292              * @param {Boolean} cancel true to cancel the query
18293              * @param {Object} e The query event object
18294              */
18295         'beforequery': true,
18296          /**
18297          * @event add
18298          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18299              * @param {Roo.form.ComboBox} combo This combo box
18300              */
18301         'add' : true,
18302         /**
18303          * @event edit
18304          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18305              * @param {Roo.form.ComboBox} combo This combo box
18306              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18307              */
18308         'edit' : true
18309         
18310         
18311     });
18312     if(this.transform){
18313         this.allowDomMove = false;
18314         var s = Roo.getDom(this.transform);
18315         if(!this.hiddenName){
18316             this.hiddenName = s.name;
18317         }
18318         if(!this.store){
18319             this.mode = 'local';
18320             var d = [], opts = s.options;
18321             for(var i = 0, len = opts.length;i < len; i++){
18322                 var o = opts[i];
18323                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18324                 if(o.selected) {
18325                     this.value = value;
18326                 }
18327                 d.push([value, o.text]);
18328             }
18329             this.store = new Roo.data.SimpleStore({
18330                 'id': 0,
18331                 fields: ['value', 'text'],
18332                 data : d
18333             });
18334             this.valueField = 'value';
18335             this.displayField = 'text';
18336         }
18337         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18338         if(!this.lazyRender){
18339             this.target = true;
18340             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18341             s.parentNode.removeChild(s); // remove it
18342             this.render(this.el.parentNode);
18343         }else{
18344             s.parentNode.removeChild(s); // remove it
18345         }
18346
18347     }
18348     if (this.store) {
18349         this.store = Roo.factory(this.store, Roo.data);
18350     }
18351     
18352     this.selectedIndex = -1;
18353     if(this.mode == 'local'){
18354         if(config.queryDelay === undefined){
18355             this.queryDelay = 10;
18356         }
18357         if(config.minChars === undefined){
18358             this.minChars = 0;
18359         }
18360     }
18361 };
18362
18363 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18364     /**
18365      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18366      */
18367     /**
18368      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18369      * rendering into an Roo.Editor, defaults to false)
18370      */
18371     /**
18372      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18373      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18374      */
18375     /**
18376      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18377      */
18378     /**
18379      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18380      * the dropdown list (defaults to undefined, with no header element)
18381      */
18382
18383      /**
18384      * @cfg {String/Roo.Template} tpl The template to use to render the output
18385      */
18386      
18387     // private
18388     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18389     /**
18390      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18391      */
18392     listWidth: undefined,
18393     /**
18394      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18395      * mode = 'remote' or 'text' if mode = 'local')
18396      */
18397     displayField: undefined,
18398     /**
18399      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18400      * mode = 'remote' or 'value' if mode = 'local'). 
18401      * Note: use of a valueField requires the user make a selection
18402      * in order for a value to be mapped.
18403      */
18404     valueField: undefined,
18405     
18406     
18407     /**
18408      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18409      * field's data value (defaults to the underlying DOM element's name)
18410      */
18411     hiddenName: undefined,
18412     /**
18413      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18414      */
18415     listClass: '',
18416     /**
18417      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18418      */
18419     selectedClass: 'x-combo-selected',
18420     /**
18421      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18422      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18423      * which displays a downward arrow icon).
18424      */
18425     triggerClass : 'x-form-arrow-trigger',
18426     /**
18427      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18428      */
18429     shadow:'sides',
18430     /**
18431      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18432      * anchor positions (defaults to 'tl-bl')
18433      */
18434     listAlign: 'tl-bl?',
18435     /**
18436      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18437      */
18438     maxHeight: 300,
18439     /**
18440      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18441      * query specified by the allQuery config option (defaults to 'query')
18442      */
18443     triggerAction: 'query',
18444     /**
18445      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18446      * (defaults to 4, does not apply if editable = false)
18447      */
18448     minChars : 4,
18449     /**
18450      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18451      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18452      */
18453     typeAhead: false,
18454     /**
18455      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18456      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18457      */
18458     queryDelay: 500,
18459     /**
18460      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18461      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18462      */
18463     pageSize: 0,
18464     /**
18465      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18466      * when editable = true (defaults to false)
18467      */
18468     selectOnFocus:false,
18469     /**
18470      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18471      */
18472     queryParam: 'query',
18473     /**
18474      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18475      * when mode = 'remote' (defaults to 'Loading...')
18476      */
18477     loadingText: 'Loading...',
18478     /**
18479      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18480      */
18481     resizable: false,
18482     /**
18483      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18484      */
18485     handleHeight : 8,
18486     /**
18487      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18488      * traditional select (defaults to true)
18489      */
18490     editable: true,
18491     /**
18492      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18493      */
18494     allQuery: '',
18495     /**
18496      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18497      */
18498     mode: 'remote',
18499     /**
18500      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18501      * listWidth has a higher value)
18502      */
18503     minListWidth : 70,
18504     /**
18505      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18506      * allow the user to set arbitrary text into the field (defaults to false)
18507      */
18508     forceSelection:false,
18509     /**
18510      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18511      * if typeAhead = true (defaults to 250)
18512      */
18513     typeAheadDelay : 250,
18514     /**
18515      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18516      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18517      */
18518     valueNotFoundText : undefined,
18519     /**
18520      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18521      */
18522     blockFocus : false,
18523     
18524     /**
18525      * @cfg {Boolean} disableClear Disable showing of clear button.
18526      */
18527     disableClear : false,
18528     /**
18529      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18530      */
18531     alwaysQuery : false,
18532     
18533     //private
18534     addicon : false,
18535     editicon: false,
18536     
18537     // element that contains real text value.. (when hidden is used..)
18538      
18539     // private
18540     onRender : function(ct, position)
18541     {
18542         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18543         
18544         if(this.hiddenName){
18545             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18546                     'before', true);
18547             this.hiddenField.value =
18548                 this.hiddenValue !== undefined ? this.hiddenValue :
18549                 this.value !== undefined ? this.value : '';
18550
18551             // prevent input submission
18552             this.el.dom.removeAttribute('name');
18553              
18554              
18555         }
18556         
18557         if(Roo.isGecko){
18558             this.el.dom.setAttribute('autocomplete', 'off');
18559         }
18560
18561         var cls = 'x-combo-list';
18562
18563         this.list = new Roo.Layer({
18564             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18565         });
18566
18567         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18568         this.list.setWidth(lw);
18569         this.list.swallowEvent('mousewheel');
18570         this.assetHeight = 0;
18571
18572         if(this.title){
18573             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18574             this.assetHeight += this.header.getHeight();
18575         }
18576
18577         this.innerList = this.list.createChild({cls:cls+'-inner'});
18578         this.innerList.on('mouseover', this.onViewOver, this);
18579         this.innerList.on('mousemove', this.onViewMove, this);
18580         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18581         
18582         if(this.allowBlank && !this.pageSize && !this.disableClear){
18583             this.footer = this.list.createChild({cls:cls+'-ft'});
18584             this.pageTb = new Roo.Toolbar(this.footer);
18585            
18586         }
18587         if(this.pageSize){
18588             this.footer = this.list.createChild({cls:cls+'-ft'});
18589             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18590                     {pageSize: this.pageSize});
18591             
18592         }
18593         
18594         if (this.pageTb && this.allowBlank && !this.disableClear) {
18595             var _this = this;
18596             this.pageTb.add(new Roo.Toolbar.Fill(), {
18597                 cls: 'x-btn-icon x-btn-clear',
18598                 text: '&#160;',
18599                 handler: function()
18600                 {
18601                     _this.collapse();
18602                     _this.clearValue();
18603                     _this.onSelect(false, -1);
18604                 }
18605             });
18606         }
18607         if (this.footer) {
18608             this.assetHeight += this.footer.getHeight();
18609         }
18610         
18611
18612         if(!this.tpl){
18613             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18614         }
18615
18616         this.view = new Roo.View(this.innerList, this.tpl, {
18617             singleSelect:true,
18618             store: this.store,
18619             selectedClass: this.selectedClass
18620         });
18621
18622         this.view.on('click', this.onViewClick, this);
18623
18624         this.store.on('beforeload', this.onBeforeLoad, this);
18625         this.store.on('load', this.onLoad, this);
18626         this.store.on('loadexception', this.onLoadException, this);
18627
18628         if(this.resizable){
18629             this.resizer = new Roo.Resizable(this.list,  {
18630                pinned:true, handles:'se'
18631             });
18632             this.resizer.on('resize', function(r, w, h){
18633                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18634                 this.listWidth = w;
18635                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18636                 this.restrictHeight();
18637             }, this);
18638             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18639         }
18640         if(!this.editable){
18641             this.editable = true;
18642             this.setEditable(false);
18643         }  
18644         
18645         
18646         if (typeof(this.events.add.listeners) != 'undefined') {
18647             
18648             this.addicon = this.wrap.createChild(
18649                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18650        
18651             this.addicon.on('click', function(e) {
18652                 this.fireEvent('add', this);
18653             }, this);
18654         }
18655         if (typeof(this.events.edit.listeners) != 'undefined') {
18656             
18657             this.editicon = this.wrap.createChild(
18658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18659             if (this.addicon) {
18660                 this.editicon.setStyle('margin-left', '40px');
18661             }
18662             this.editicon.on('click', function(e) {
18663                 
18664                 // we fire even  if inothing is selected..
18665                 this.fireEvent('edit', this, this.lastData );
18666                 
18667             }, this);
18668         }
18669         
18670         
18671         
18672     },
18673
18674     // private
18675     initEvents : function(){
18676         Roo.form.ComboBox.superclass.initEvents.call(this);
18677
18678         this.keyNav = new Roo.KeyNav(this.el, {
18679             "up" : function(e){
18680                 this.inKeyMode = true;
18681                 this.selectPrev();
18682             },
18683
18684             "down" : function(e){
18685                 if(!this.isExpanded()){
18686                     this.onTriggerClick();
18687                 }else{
18688                     this.inKeyMode = true;
18689                     this.selectNext();
18690                 }
18691             },
18692
18693             "enter" : function(e){
18694                 this.onViewClick();
18695                 //return true;
18696             },
18697
18698             "esc" : function(e){
18699                 this.collapse();
18700             },
18701
18702             "tab" : function(e){
18703                 this.onViewClick(false);
18704                 this.fireEvent("specialkey", this, e);
18705                 return true;
18706             },
18707
18708             scope : this,
18709
18710             doRelay : function(foo, bar, hname){
18711                 if(hname == 'down' || this.scope.isExpanded()){
18712                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18713                 }
18714                 return true;
18715             },
18716
18717             forceKeyDown: true
18718         });
18719         this.queryDelay = Math.max(this.queryDelay || 10,
18720                 this.mode == 'local' ? 10 : 250);
18721         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18722         if(this.typeAhead){
18723             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18724         }
18725         if(this.editable !== false){
18726             this.el.on("keyup", this.onKeyUp, this);
18727         }
18728         if(this.forceSelection){
18729             this.on('blur', this.doForce, this);
18730         }
18731     },
18732
18733     onDestroy : function(){
18734         if(this.view){
18735             this.view.setStore(null);
18736             this.view.el.removeAllListeners();
18737             this.view.el.remove();
18738             this.view.purgeListeners();
18739         }
18740         if(this.list){
18741             this.list.destroy();
18742         }
18743         if(this.store){
18744             this.store.un('beforeload', this.onBeforeLoad, this);
18745             this.store.un('load', this.onLoad, this);
18746             this.store.un('loadexception', this.onLoadException, this);
18747         }
18748         Roo.form.ComboBox.superclass.onDestroy.call(this);
18749     },
18750
18751     // private
18752     fireKey : function(e){
18753         if(e.isNavKeyPress() && !this.list.isVisible()){
18754             this.fireEvent("specialkey", this, e);
18755         }
18756     },
18757
18758     // private
18759     onResize: function(w, h){
18760         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18761         
18762         if(typeof w != 'number'){
18763             // we do not handle it!?!?
18764             return;
18765         }
18766         var tw = this.trigger.getWidth();
18767         tw += this.addicon ? this.addicon.getWidth() : 0;
18768         tw += this.editicon ? this.editicon.getWidth() : 0;
18769         var x = w - tw;
18770         this.el.setWidth( this.adjustWidth('input', x));
18771             
18772         this.trigger.setStyle('left', x+'px');
18773         
18774         if(this.list && this.listWidth === undefined){
18775             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18776             this.list.setWidth(lw);
18777             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18778         }
18779         
18780     
18781         
18782     },
18783
18784     /**
18785      * Allow or prevent the user from directly editing the field text.  If false is passed,
18786      * the user will only be able to select from the items defined in the dropdown list.  This method
18787      * is the runtime equivalent of setting the 'editable' config option at config time.
18788      * @param {Boolean} value True to allow the user to directly edit the field text
18789      */
18790     setEditable : function(value){
18791         if(value == this.editable){
18792             return;
18793         }
18794         this.editable = value;
18795         if(!value){
18796             this.el.dom.setAttribute('readOnly', true);
18797             this.el.on('mousedown', this.onTriggerClick,  this);
18798             this.el.addClass('x-combo-noedit');
18799         }else{
18800             this.el.dom.setAttribute('readOnly', false);
18801             this.el.un('mousedown', this.onTriggerClick,  this);
18802             this.el.removeClass('x-combo-noedit');
18803         }
18804     },
18805
18806     // private
18807     onBeforeLoad : function(){
18808         if(!this.hasFocus){
18809             return;
18810         }
18811         this.innerList.update(this.loadingText ?
18812                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18813         this.restrictHeight();
18814         this.selectedIndex = -1;
18815     },
18816
18817     // private
18818     onLoad : function(){
18819         if(!this.hasFocus){
18820             return;
18821         }
18822         if(this.store.getCount() > 0){
18823             this.expand();
18824             this.restrictHeight();
18825             if(this.lastQuery == this.allQuery){
18826                 if(this.editable){
18827                     this.el.dom.select();
18828                 }
18829                 if(!this.selectByValue(this.value, true)){
18830                     this.select(0, true);
18831                 }
18832             }else{
18833                 this.selectNext();
18834                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18835                     this.taTask.delay(this.typeAheadDelay);
18836                 }
18837             }
18838         }else{
18839             this.onEmptyResults();
18840         }
18841         //this.el.focus();
18842     },
18843     // private
18844     onLoadException : function()
18845     {
18846         this.collapse();
18847         Roo.log(this.store.reader.jsonData);
18848         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18849             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18850         }
18851         
18852         
18853     },
18854     // private
18855     onTypeAhead : function(){
18856         if(this.store.getCount() > 0){
18857             var r = this.store.getAt(0);
18858             var newValue = r.data[this.displayField];
18859             var len = newValue.length;
18860             var selStart = this.getRawValue().length;
18861             if(selStart != len){
18862                 this.setRawValue(newValue);
18863                 this.selectText(selStart, newValue.length);
18864             }
18865         }
18866     },
18867
18868     // private
18869     onSelect : function(record, index){
18870         if(this.fireEvent('beforeselect', this, record, index) !== false){
18871             this.setFromData(index > -1 ? record.data : false);
18872             this.collapse();
18873             this.fireEvent('select', this, record, index);
18874         }
18875     },
18876
18877     /**
18878      * Returns the currently selected field value or empty string if no value is set.
18879      * @return {String} value The selected value
18880      */
18881     getValue : function(){
18882         if(this.valueField){
18883             return typeof this.value != 'undefined' ? this.value : '';
18884         }
18885         return Roo.form.ComboBox.superclass.getValue.call(this);
18886     },
18887
18888     /**
18889      * Clears any text/value currently set in the field
18890      */
18891     clearValue : function(){
18892         if(this.hiddenField){
18893             this.hiddenField.value = '';
18894         }
18895         this.value = '';
18896         this.setRawValue('');
18897         this.lastSelectionText = '';
18898         
18899     },
18900
18901     /**
18902      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18903      * will be displayed in the field.  If the value does not match the data value of an existing item,
18904      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18905      * Otherwise the field will be blank (although the value will still be set).
18906      * @param {String} value The value to match
18907      */
18908     setValue : function(v){
18909         var text = v;
18910         if(this.valueField){
18911             var r = this.findRecord(this.valueField, v);
18912             if(r){
18913                 text = r.data[this.displayField];
18914             }else if(this.valueNotFoundText !== undefined){
18915                 text = this.valueNotFoundText;
18916             }
18917         }
18918         this.lastSelectionText = text;
18919         if(this.hiddenField){
18920             this.hiddenField.value = v;
18921         }
18922         Roo.form.ComboBox.superclass.setValue.call(this, text);
18923         this.value = v;
18924     },
18925     /**
18926      * @property {Object} the last set data for the element
18927      */
18928     
18929     lastData : false,
18930     /**
18931      * Sets the value of the field based on a object which is related to the record format for the store.
18932      * @param {Object} value the value to set as. or false on reset?
18933      */
18934     setFromData : function(o){
18935         var dv = ''; // display value
18936         var vv = ''; // value value..
18937         this.lastData = o;
18938         if (this.displayField) {
18939             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18940         } else {
18941             // this is an error condition!!!
18942             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18943         }
18944         
18945         if(this.valueField){
18946             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18947         }
18948         if(this.hiddenField){
18949             this.hiddenField.value = vv;
18950             
18951             this.lastSelectionText = dv;
18952             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18953             this.value = vv;
18954             return;
18955         }
18956         // no hidden field.. - we store the value in 'value', but still display
18957         // display field!!!!
18958         this.lastSelectionText = dv;
18959         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18960         this.value = vv;
18961         
18962         
18963     },
18964     // private
18965     reset : function(){
18966         // overridden so that last data is reset..
18967         this.setValue(this.resetValue);
18968         this.originalValue = this.getValue();
18969         this.clearInvalid();
18970         this.lastData = false;
18971         if (this.view) {
18972             this.view.clearSelections();
18973         }
18974     },
18975     // private
18976     findRecord : function(prop, value){
18977         var record;
18978         if(this.store.getCount() > 0){
18979             this.store.each(function(r){
18980                 if(r.data[prop] == value){
18981                     record = r;
18982                     return false;
18983                 }
18984                 return true;
18985             });
18986         }
18987         return record;
18988     },
18989     
18990     getName: function()
18991     {
18992         // returns hidden if it's set..
18993         if (!this.rendered) {return ''};
18994         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18995         
18996     },
18997     // private
18998     onViewMove : function(e, t){
18999         this.inKeyMode = false;
19000     },
19001
19002     // private
19003     onViewOver : function(e, t){
19004         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19005             return;
19006         }
19007         var item = this.view.findItemFromChild(t);
19008         if(item){
19009             var index = this.view.indexOf(item);
19010             this.select(index, false);
19011         }
19012     },
19013
19014     // private
19015     onViewClick : function(doFocus)
19016     {
19017         var index = this.view.getSelectedIndexes()[0];
19018         var r = this.store.getAt(index);
19019         if(r){
19020             this.onSelect(r, index);
19021         }
19022         if(doFocus !== false && !this.blockFocus){
19023             this.el.focus();
19024         }
19025     },
19026
19027     // private
19028     restrictHeight : function(){
19029         this.innerList.dom.style.height = '';
19030         var inner = this.innerList.dom;
19031         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19032         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19033         this.list.beginUpdate();
19034         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19035         this.list.alignTo(this.el, this.listAlign);
19036         this.list.endUpdate();
19037     },
19038
19039     // private
19040     onEmptyResults : function(){
19041         this.collapse();
19042     },
19043
19044     /**
19045      * Returns true if the dropdown list is expanded, else false.
19046      */
19047     isExpanded : function(){
19048         return this.list.isVisible();
19049     },
19050
19051     /**
19052      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19053      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19054      * @param {String} value The data value of the item to select
19055      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19056      * selected item if it is not currently in view (defaults to true)
19057      * @return {Boolean} True if the value matched an item in the list, else false
19058      */
19059     selectByValue : function(v, scrollIntoView){
19060         if(v !== undefined && v !== null){
19061             var r = this.findRecord(this.valueField || this.displayField, v);
19062             if(r){
19063                 this.select(this.store.indexOf(r), scrollIntoView);
19064                 return true;
19065             }
19066         }
19067         return false;
19068     },
19069
19070     /**
19071      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19073      * @param {Number} index The zero-based index of the list item to select
19074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19075      * selected item if it is not currently in view (defaults to true)
19076      */
19077     select : function(index, scrollIntoView){
19078         this.selectedIndex = index;
19079         this.view.select(index);
19080         if(scrollIntoView !== false){
19081             var el = this.view.getNode(index);
19082             if(el){
19083                 this.innerList.scrollChildIntoView(el, false);
19084             }
19085         }
19086     },
19087
19088     // private
19089     selectNext : function(){
19090         var ct = this.store.getCount();
19091         if(ct > 0){
19092             if(this.selectedIndex == -1){
19093                 this.select(0);
19094             }else if(this.selectedIndex < ct-1){
19095                 this.select(this.selectedIndex+1);
19096             }
19097         }
19098     },
19099
19100     // private
19101     selectPrev : function(){
19102         var ct = this.store.getCount();
19103         if(ct > 0){
19104             if(this.selectedIndex == -1){
19105                 this.select(0);
19106             }else if(this.selectedIndex != 0){
19107                 this.select(this.selectedIndex-1);
19108             }
19109         }
19110     },
19111
19112     // private
19113     onKeyUp : function(e){
19114         if(this.editable !== false && !e.isSpecialKey()){
19115             this.lastKey = e.getKey();
19116             this.dqTask.delay(this.queryDelay);
19117         }
19118     },
19119
19120     // private
19121     validateBlur : function(){
19122         return !this.list || !this.list.isVisible();   
19123     },
19124
19125     // private
19126     initQuery : function(){
19127         this.doQuery(this.getRawValue());
19128     },
19129
19130     // private
19131     doForce : function(){
19132         if(this.el.dom.value.length > 0){
19133             this.el.dom.value =
19134                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19135              
19136         }
19137     },
19138
19139     /**
19140      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19141      * query allowing the query action to be canceled if needed.
19142      * @param {String} query The SQL query to execute
19143      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19144      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19145      * saved in the current store (defaults to false)
19146      */
19147     doQuery : function(q, forceAll){
19148         if(q === undefined || q === null){
19149             q = '';
19150         }
19151         var qe = {
19152             query: q,
19153             forceAll: forceAll,
19154             combo: this,
19155             cancel:false
19156         };
19157         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19158             return false;
19159         }
19160         q = qe.query;
19161         forceAll = qe.forceAll;
19162         if(forceAll === true || (q.length >= this.minChars)){
19163             if(this.lastQuery != q || this.alwaysQuery){
19164                 this.lastQuery = q;
19165                 if(this.mode == 'local'){
19166                     this.selectedIndex = -1;
19167                     if(forceAll){
19168                         this.store.clearFilter();
19169                     }else{
19170                         this.store.filter(this.displayField, q);
19171                     }
19172                     this.onLoad();
19173                 }else{
19174                     this.store.baseParams[this.queryParam] = q;
19175                     this.store.load({
19176                         params: this.getParams(q)
19177                     });
19178                     this.expand();
19179                 }
19180             }else{
19181                 this.selectedIndex = -1;
19182                 this.onLoad();   
19183             }
19184         }
19185     },
19186
19187     // private
19188     getParams : function(q){
19189         var p = {};
19190         //p[this.queryParam] = q;
19191         if(this.pageSize){
19192             p.start = 0;
19193             p.limit = this.pageSize;
19194         }
19195         return p;
19196     },
19197
19198     /**
19199      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19200      */
19201     collapse : function(){
19202         if(!this.isExpanded()){
19203             return;
19204         }
19205         this.list.hide();
19206         Roo.get(document).un('mousedown', this.collapseIf, this);
19207         Roo.get(document).un('mousewheel', this.collapseIf, this);
19208         if (!this.editable) {
19209             Roo.get(document).un('keydown', this.listKeyPress, this);
19210         }
19211         this.fireEvent('collapse', this);
19212     },
19213
19214     // private
19215     collapseIf : function(e){
19216         if(!e.within(this.wrap) && !e.within(this.list)){
19217             this.collapse();
19218         }
19219     },
19220
19221     /**
19222      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19223      */
19224     expand : function(){
19225         if(this.isExpanded() || !this.hasFocus){
19226             return;
19227         }
19228         this.list.alignTo(this.el, this.listAlign);
19229         this.list.show();
19230         Roo.get(document).on('mousedown', this.collapseIf, this);
19231         Roo.get(document).on('mousewheel', this.collapseIf, this);
19232         if (!this.editable) {
19233             Roo.get(document).on('keydown', this.listKeyPress, this);
19234         }
19235         
19236         this.fireEvent('expand', this);
19237     },
19238
19239     // private
19240     // Implements the default empty TriggerField.onTriggerClick function
19241     onTriggerClick : function(){
19242         if(this.disabled){
19243             return;
19244         }
19245         if(this.isExpanded()){
19246             this.collapse();
19247             if (!this.blockFocus) {
19248                 this.el.focus();
19249             }
19250             
19251         }else {
19252             this.hasFocus = true;
19253             if(this.triggerAction == 'all') {
19254                 this.doQuery(this.allQuery, true);
19255             } else {
19256                 this.doQuery(this.getRawValue());
19257             }
19258             if (!this.blockFocus) {
19259                 this.el.focus();
19260             }
19261         }
19262     },
19263     listKeyPress : function(e)
19264     {
19265         //Roo.log('listkeypress');
19266         // scroll to first matching element based on key pres..
19267         if (e.isSpecialKey()) {
19268             return false;
19269         }
19270         var k = String.fromCharCode(e.getKey()).toUpperCase();
19271         //Roo.log(k);
19272         var match  = false;
19273         var csel = this.view.getSelectedNodes();
19274         var cselitem = false;
19275         if (csel.length) {
19276             var ix = this.view.indexOf(csel[0]);
19277             cselitem  = this.store.getAt(ix);
19278             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19279                 cselitem = false;
19280             }
19281             
19282         }
19283         
19284         this.store.each(function(v) { 
19285             if (cselitem) {
19286                 // start at existing selection.
19287                 if (cselitem.id == v.id) {
19288                     cselitem = false;
19289                 }
19290                 return;
19291             }
19292                 
19293             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19294                 match = this.store.indexOf(v);
19295                 return false;
19296             }
19297         }, this);
19298         
19299         if (match === false) {
19300             return true; // no more action?
19301         }
19302         // scroll to?
19303         this.view.select(match);
19304         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19305         sn.scrollIntoView(sn.dom.parentNode, false);
19306     } 
19307
19308     /** 
19309     * @cfg {Boolean} grow 
19310     * @hide 
19311     */
19312     /** 
19313     * @cfg {Number} growMin 
19314     * @hide 
19315     */
19316     /** 
19317     * @cfg {Number} growMax 
19318     * @hide 
19319     */
19320     /**
19321      * @hide
19322      * @method autoSize
19323      */
19324 });/*
19325  * Copyright(c) 2010-2012, Roo J Solutions Limited
19326  *
19327  * Licence LGPL
19328  *
19329  */
19330
19331 /**
19332  * @class Roo.form.ComboBoxArray
19333  * @extends Roo.form.TextField
19334  * A facebook style adder... for lists of email / people / countries  etc...
19335  * pick multiple items from a combo box, and shows each one.
19336  *
19337  *  Fred [x]  Brian [x]  [Pick another |v]
19338  *
19339  *
19340  *  For this to work: it needs various extra information
19341  *    - normal combo problay has
19342  *      name, hiddenName
19343  *    + displayField, valueField
19344  *
19345  *    For our purpose...
19346  *
19347  *
19348  *   If we change from 'extends' to wrapping...
19349  *   
19350  *  
19351  *
19352  
19353  
19354  * @constructor
19355  * Create a new ComboBoxArray.
19356  * @param {Object} config Configuration options
19357  */
19358  
19359
19360 Roo.form.ComboBoxArray = function(config)
19361 {
19362     this.addEvents({
19363         /**
19364          * @event beforeremove
19365          * Fires before remove the value from the list
19366              * @param {Roo.form.ComboBoxArray} _self This combo box array
19367              * @param {Roo.form.ComboBoxArray.Item} item removed item
19368              */
19369         'beforeremove' : true,
19370         /**
19371          * @event remove
19372          * Fires when remove the value from the list
19373              * @param {Roo.form.ComboBoxArray} _self This combo box array
19374              * @param {Roo.form.ComboBoxArray.Item} item removed item
19375              */
19376         'remove' : true
19377         
19378         
19379     });
19380     
19381     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19382     
19383     this.items = new Roo.util.MixedCollection(false);
19384     
19385     // construct the child combo...
19386     
19387     
19388     
19389     
19390    
19391     
19392 }
19393
19394  
19395 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19396
19397     /**
19398      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19399      */
19400     
19401     lastData : false,
19402     
19403     // behavies liek a hiddne field
19404     inputType:      'hidden',
19405     /**
19406      * @cfg {Number} width The width of the box that displays the selected element
19407      */ 
19408     width:          300,
19409
19410     
19411     
19412     /**
19413      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19414      */
19415     name : false,
19416     /**
19417      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19418      */
19419     hiddenName : false,
19420       /**
19421      * @cfg {String} seperator    The value seperator normally ',' 
19422      */
19423     seperator : ',',
19424     
19425     // private the array of items that are displayed..
19426     items  : false,
19427     // private - the hidden field el.
19428     hiddenEl : false,
19429     // private - the filed el..
19430     el : false,
19431     
19432     //validateValue : function() { return true; }, // all values are ok!
19433     //onAddClick: function() { },
19434     
19435     onRender : function(ct, position) 
19436     {
19437         
19438         // create the standard hidden element
19439         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19440         
19441         
19442         // give fake names to child combo;
19443         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19444         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19445         
19446         this.combo = Roo.factory(this.combo, Roo.form);
19447         this.combo.onRender(ct, position);
19448         if (typeof(this.combo.width) != 'undefined') {
19449             this.combo.onResize(this.combo.width,0);
19450         }
19451         
19452         this.combo.initEvents();
19453         
19454         // assigned so form know we need to do this..
19455         this.store          = this.combo.store;
19456         this.valueField     = this.combo.valueField;
19457         this.displayField   = this.combo.displayField ;
19458         
19459         
19460         this.combo.wrap.addClass('x-cbarray-grp');
19461         
19462         var cbwrap = this.combo.wrap.createChild(
19463             {tag: 'div', cls: 'x-cbarray-cb'},
19464             this.combo.el.dom
19465         );
19466         
19467              
19468         this.hiddenEl = this.combo.wrap.createChild({
19469             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19470         });
19471         this.el = this.combo.wrap.createChild({
19472             tag: 'input',  type:'hidden' , name: this.name, value : ''
19473         });
19474          //   this.el.dom.removeAttribute("name");
19475         
19476         
19477         this.outerWrap = this.combo.wrap;
19478         this.wrap = cbwrap;
19479         
19480         this.outerWrap.setWidth(this.width);
19481         this.outerWrap.dom.removeChild(this.el.dom);
19482         
19483         this.wrap.dom.appendChild(this.el.dom);
19484         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19485         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19486         
19487         this.combo.trigger.setStyle('position','relative');
19488         this.combo.trigger.setStyle('left', '0px');
19489         this.combo.trigger.setStyle('top', '2px');
19490         
19491         this.combo.el.setStyle('vertical-align', 'text-bottom');
19492         
19493         //this.trigger.setStyle('vertical-align', 'top');
19494         
19495         // this should use the code from combo really... on('add' ....)
19496         if (this.adder) {
19497             
19498         
19499             this.adder = this.outerWrap.createChild(
19500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19501             var _t = this;
19502             this.adder.on('click', function(e) {
19503                 _t.fireEvent('adderclick', this, e);
19504             }, _t);
19505         }
19506         //var _t = this;
19507         //this.adder.on('click', this.onAddClick, _t);
19508         
19509         
19510         this.combo.on('select', function(cb, rec, ix) {
19511             this.addItem(rec.data);
19512             
19513             cb.setValue('');
19514             cb.el.dom.value = '';
19515             //cb.lastData = rec.data;
19516             // add to list
19517             
19518         }, this);
19519         
19520         
19521     },
19522     
19523     
19524     getName: function()
19525     {
19526         // returns hidden if it's set..
19527         if (!this.rendered) {return ''};
19528         return  this.hiddenName ? this.hiddenName : this.name;
19529         
19530     },
19531     
19532     
19533     onResize: function(w, h){
19534         
19535         return;
19536         // not sure if this is needed..
19537         //this.combo.onResize(w,h);
19538         
19539         if(typeof w != 'number'){
19540             // we do not handle it!?!?
19541             return;
19542         }
19543         var tw = this.combo.trigger.getWidth();
19544         tw += this.addicon ? this.addicon.getWidth() : 0;
19545         tw += this.editicon ? this.editicon.getWidth() : 0;
19546         var x = w - tw;
19547         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19548             
19549         this.combo.trigger.setStyle('left', '0px');
19550         
19551         if(this.list && this.listWidth === undefined){
19552             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19553             this.list.setWidth(lw);
19554             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19555         }
19556         
19557     
19558         
19559     },
19560     
19561     addItem: function(rec)
19562     {
19563         var valueField = this.combo.valueField;
19564         var displayField = this.combo.displayField;
19565         
19566         if (this.items.indexOfKey(rec[valueField]) > -1) {
19567             //console.log("GOT " + rec.data.id);
19568             return;
19569         }
19570         
19571         var x = new Roo.form.ComboBoxArray.Item({
19572             //id : rec[this.idField],
19573             data : rec,
19574             displayField : displayField ,
19575             tipField : displayField ,
19576             cb : this
19577         });
19578         // use the 
19579         this.items.add(rec[valueField],x);
19580         // add it before the element..
19581         this.updateHiddenEl();
19582         x.render(this.outerWrap, this.wrap.dom);
19583         // add the image handler..
19584     },
19585     
19586     updateHiddenEl : function()
19587     {
19588         this.validate();
19589         if (!this.hiddenEl) {
19590             return;
19591         }
19592         var ar = [];
19593         var idField = this.combo.valueField;
19594         
19595         this.items.each(function(f) {
19596             ar.push(f.data[idField]);
19597         });
19598         this.hiddenEl.dom.value = ar.join(this.seperator);
19599         this.validate();
19600     },
19601     
19602     reset : function()
19603     {
19604         this.items.clear();
19605         
19606         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19607            el.remove();
19608         });
19609         
19610         this.el.dom.value = '';
19611         if (this.hiddenEl) {
19612             this.hiddenEl.dom.value = '';
19613         }
19614         
19615     },
19616     getValue: function()
19617     {
19618         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19619     },
19620     setValue: function(v) // not a valid action - must use addItems..
19621     {
19622         
19623         this.reset();
19624          
19625         if (this.store.isLocal && (typeof(v) == 'string')) {
19626             // then we can use the store to find the values..
19627             // comma seperated at present.. this needs to allow JSON based encoding..
19628             this.hiddenEl.value  = v;
19629             var v_ar = [];
19630             Roo.each(v.split(this.seperator), function(k) {
19631                 Roo.log("CHECK " + this.valueField + ',' + k);
19632                 var li = this.store.query(this.valueField, k);
19633                 if (!li.length) {
19634                     return;
19635                 }
19636                 var add = {};
19637                 add[this.valueField] = k;
19638                 add[this.displayField] = li.item(0).data[this.displayField];
19639                 
19640                 this.addItem(add);
19641             }, this) 
19642              
19643         }
19644         if (typeof(v) == 'object' ) {
19645             // then let's assume it's an array of objects..
19646             Roo.each(v, function(l) {
19647                 var add = l;
19648                 if (typeof(l) == 'string') {
19649                     add = {};
19650                     add[this.valueField] = l;
19651                     add[this.displayField] = l
19652                 }
19653                 this.addItem(add);
19654             }, this);
19655              
19656         }
19657         
19658         
19659     },
19660     setFromData: function(v)
19661     {
19662         // this recieves an object, if setValues is called.
19663         this.reset();
19664         this.el.dom.value = v[this.displayField];
19665         this.hiddenEl.dom.value = v[this.valueField];
19666         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19667             return;
19668         }
19669         var kv = v[this.valueField];
19670         var dv = v[this.displayField];
19671         kv = typeof(kv) != 'string' ? '' : kv;
19672         dv = typeof(dv) != 'string' ? '' : dv;
19673         
19674         
19675         var keys = kv.split(this.seperator);
19676         var display = dv.split(this.seperator);
19677         for (var i = 0 ; i < keys.length; i++) {
19678             add = {};
19679             add[this.valueField] = keys[i];
19680             add[this.displayField] = display[i];
19681             this.addItem(add);
19682         }
19683       
19684         
19685     },
19686     
19687     /**
19688      * Validates the combox array value
19689      * @return {Boolean} True if the value is valid, else false
19690      */
19691     validate : function(){
19692         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19693             this.clearInvalid();
19694             return true;
19695         }
19696         return false;
19697     },
19698     
19699     validateValue : function(value){
19700         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19701         
19702     },
19703     
19704     /*@
19705      * overide
19706      * 
19707      */
19708     isDirty : function() {
19709         if(this.disabled) {
19710             return false;
19711         }
19712         
19713         try {
19714             var d = Roo.decode(String(this.originalValue));
19715         } catch (e) {
19716             return String(this.getValue()) !== String(this.originalValue);
19717         }
19718         
19719         var originalValue = [];
19720         
19721         for (var i = 0; i < d.length; i++){
19722             originalValue.push(d[i][this.valueField]);
19723         }
19724         
19725         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19726         
19727     }
19728     
19729 });
19730
19731
19732
19733 /**
19734  * @class Roo.form.ComboBoxArray.Item
19735  * @extends Roo.BoxComponent
19736  * A selected item in the list
19737  *  Fred [x]  Brian [x]  [Pick another |v]
19738  * 
19739  * @constructor
19740  * Create a new item.
19741  * @param {Object} config Configuration options
19742  */
19743  
19744 Roo.form.ComboBoxArray.Item = function(config) {
19745     config.id = Roo.id();
19746     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19747 }
19748
19749 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19750     data : {},
19751     cb: false,
19752     displayField : false,
19753     tipField : false,
19754     
19755     
19756     defaultAutoCreate : {
19757         tag: 'div',
19758         cls: 'x-cbarray-item',
19759         cn : [ 
19760             { tag: 'div' },
19761             {
19762                 tag: 'img',
19763                 width:16,
19764                 height : 16,
19765                 src : Roo.BLANK_IMAGE_URL ,
19766                 align: 'center'
19767             }
19768         ]
19769         
19770     },
19771     
19772  
19773     onRender : function(ct, position)
19774     {
19775         Roo.form.Field.superclass.onRender.call(this, ct, position);
19776         
19777         if(!this.el){
19778             var cfg = this.getAutoCreate();
19779             this.el = ct.createChild(cfg, position);
19780         }
19781         
19782         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19783         
19784         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19785             this.cb.renderer(this.data) :
19786             String.format('{0}',this.data[this.displayField]);
19787         
19788             
19789         this.el.child('div').dom.setAttribute('qtip',
19790                         String.format('{0}',this.data[this.tipField])
19791         );
19792         
19793         this.el.child('img').on('click', this.remove, this);
19794         
19795     },
19796    
19797     remove : function()
19798     {
19799         if(this.cb.disabled){
19800             return;
19801         }
19802         
19803         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19804             this.cb.items.remove(this);
19805             this.el.child('img').un('click', this.remove, this);
19806             this.el.remove();
19807             this.cb.updateHiddenEl();
19808
19809             this.cb.fireEvent('remove', this.cb, this);
19810         }
19811         
19812     }
19813 });/*
19814  * RooJS Library 1.1.1
19815  * Copyright(c) 2008-2011  Alan Knowles
19816  *
19817  * License - LGPL
19818  */
19819  
19820
19821 /**
19822  * @class Roo.form.ComboNested
19823  * @extends Roo.form.ComboBox
19824  * A combobox for that allows selection of nested items in a list,
19825  * eg.
19826  *
19827  *  Book
19828  *    -> red
19829  *    -> green
19830  *  Table
19831  *    -> square
19832  *      ->red
19833  *      ->green
19834  *    -> rectangle
19835  *      ->green
19836  *      
19837  * 
19838  * @constructor
19839  * Create a new ComboNested
19840  * @param {Object} config Configuration options
19841  */
19842 Roo.form.ComboNested = function(config){
19843     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19844     // should verify some data...
19845     // like
19846     // hiddenName = required..
19847     // displayField = required
19848     // valudField == required
19849     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19850     var _t = this;
19851     Roo.each(req, function(e) {
19852         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19853             throw "Roo.form.ComboNested : missing value for: " + e;
19854         }
19855     });
19856      
19857     
19858 };
19859
19860 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19861    
19862     /*
19863      * @config {Number} max Number of columns to show
19864      */
19865     
19866     maxColumns : 3,
19867    
19868     list : null, // the outermost div..
19869     innerLists : null, // the
19870     views : null,
19871     stores : null,
19872     // private
19873     loadingChildren : false,
19874     
19875     onRender : function(ct, position)
19876     {
19877         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19878         
19879         if(this.hiddenName){
19880             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19881                     'before', true);
19882             this.hiddenField.value =
19883                 this.hiddenValue !== undefined ? this.hiddenValue :
19884                 this.value !== undefined ? this.value : '';
19885
19886             // prevent input submission
19887             this.el.dom.removeAttribute('name');
19888              
19889              
19890         }
19891         
19892         if(Roo.isGecko){
19893             this.el.dom.setAttribute('autocomplete', 'off');
19894         }
19895
19896         var cls = 'x-combo-list';
19897
19898         this.list = new Roo.Layer({
19899             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19900         });
19901
19902         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19903         this.list.setWidth(lw);
19904         this.list.swallowEvent('mousewheel');
19905         this.assetHeight = 0;
19906
19907         if(this.title){
19908             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19909             this.assetHeight += this.header.getHeight();
19910         }
19911         this.innerLists = [];
19912         this.views = [];
19913         this.stores = [];
19914         for (var i =0 ; i < this.maxColumns; i++) {
19915             this.onRenderList( cls, i);
19916         }
19917         
19918         // always needs footer, as we are going to have an 'OK' button.
19919         this.footer = this.list.createChild({cls:cls+'-ft'});
19920         this.pageTb = new Roo.Toolbar(this.footer);  
19921         var _this = this;
19922         this.pageTb.add(  {
19923             
19924             text: 'Done',
19925             handler: function()
19926             {
19927                 _this.collapse();
19928             }
19929         });
19930         
19931         if ( this.allowBlank && !this.disableClear) {
19932             
19933             this.pageTb.add(new Roo.Toolbar.Fill(), {
19934                 cls: 'x-btn-icon x-btn-clear',
19935                 text: '&#160;',
19936                 handler: function()
19937                 {
19938                     _this.collapse();
19939                     _this.clearValue();
19940                     _this.onSelect(false, -1);
19941                 }
19942             });
19943         }
19944         if (this.footer) {
19945             this.assetHeight += this.footer.getHeight();
19946         }
19947         
19948     },
19949     onRenderList : function (  cls, i)
19950     {
19951         
19952         var lw = Math.floor(
19953                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19954         );
19955         
19956         this.list.setWidth(lw); // default to '1'
19957
19958         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19959         //il.on('mouseover', this.onViewOver, this, { list:  i });
19960         //il.on('mousemove', this.onViewMove, this, { list:  i });
19961         il.setWidth(lw);
19962         il.setStyle({ 'overflow-x' : 'hidden'});
19963
19964         if(!this.tpl){
19965             this.tpl = new Roo.Template({
19966                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19967                 isEmpty: function (value, allValues) {
19968                     //Roo.log(value);
19969                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19970                     return dl ? 'has-children' : 'no-children'
19971                 }
19972             });
19973         }
19974         
19975         var store  = this.store;
19976         if (i > 0) {
19977             store  = new Roo.data.SimpleStore({
19978                 //fields : this.store.reader.meta.fields,
19979                 reader : this.store.reader,
19980                 data : [ ]
19981             });
19982         }
19983         this.stores[i]  = store;
19984                   
19985         var view = this.views[i] = new Roo.View(
19986             il,
19987             this.tpl,
19988             {
19989                 singleSelect:true,
19990                 store: store,
19991                 selectedClass: this.selectedClass
19992             }
19993         );
19994         view.getEl().setWidth(lw);
19995         view.getEl().setStyle({
19996             position: i < 1 ? 'relative' : 'absolute',
19997             top: 0,
19998             left: (i * lw ) + 'px',
19999             display : i > 0 ? 'none' : 'block'
20000         });
20001         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20002         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20003         //view.on('click', this.onViewClick, this, { list : i });
20004
20005         store.on('beforeload', this.onBeforeLoad, this);
20006         store.on('load',  this.onLoad, this, { list  : i});
20007         store.on('loadexception', this.onLoadException, this);
20008
20009         // hide the other vies..
20010         
20011         
20012         
20013     },
20014       
20015     restrictHeight : function()
20016     {
20017         var mh = 0;
20018         Roo.each(this.innerLists, function(il,i) {
20019             var el = this.views[i].getEl();
20020             el.dom.style.height = '';
20021             var inner = el.dom;
20022             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20023             // only adjust heights on other ones..
20024             mh = Math.max(h, mh);
20025             if (i < 1) {
20026                 
20027                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20028                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20029                
20030             }
20031             
20032             
20033         }, this);
20034         
20035         this.list.beginUpdate();
20036         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20037         this.list.alignTo(this.el, this.listAlign);
20038         this.list.endUpdate();
20039         
20040     },
20041      
20042     
20043     // -- store handlers..
20044     // private
20045     onBeforeLoad : function()
20046     {
20047         if(!this.hasFocus){
20048             return;
20049         }
20050         this.innerLists[0].update(this.loadingText ?
20051                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20052         this.restrictHeight();
20053         this.selectedIndex = -1;
20054     },
20055     // private
20056     onLoad : function(a,b,c,d)
20057     {
20058         if (!this.loadingChildren) {
20059             // then we are loading the top level. - hide the children
20060             for (var i = 1;i < this.views.length; i++) {
20061                 this.views[i].getEl().setStyle({ display : 'none' });
20062             }
20063             var lw = Math.floor(
20064                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20065             );
20066         
20067              this.list.setWidth(lw); // default to '1'
20068
20069             
20070         }
20071         if(!this.hasFocus){
20072             return;
20073         }
20074         
20075         if(this.store.getCount() > 0) {
20076             this.expand();
20077             this.restrictHeight();   
20078         } else {
20079             this.onEmptyResults();
20080         }
20081         
20082         if (!this.loadingChildren) {
20083             this.selectActive();
20084         }
20085         /*
20086         this.stores[1].loadData([]);
20087         this.stores[2].loadData([]);
20088         this.views
20089         */    
20090     
20091         //this.el.focus();
20092     },
20093     
20094     
20095     // private
20096     onLoadException : function()
20097     {
20098         this.collapse();
20099         Roo.log(this.store.reader.jsonData);
20100         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20101             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20102         }
20103         
20104         
20105     },
20106     // no cleaning of leading spaces on blur here.
20107     cleanLeadingSpace : function(e) { },
20108     
20109
20110     onSelectChange : function (view, sels, opts )
20111     {
20112         var ix = view.getSelectedIndexes();
20113          
20114         if (opts.list > this.maxColumns - 2) {
20115             if (view.store.getCount()<  1) {
20116                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20117
20118             } else  {
20119                 if (ix.length) {
20120                     // used to clear ?? but if we are loading unselected 
20121                     this.setFromData(view.store.getAt(ix[0]).data);
20122                 }
20123                 
20124             }
20125             
20126             return;
20127         }
20128         
20129         if (!ix.length) {
20130             // this get's fired when trigger opens..
20131            // this.setFromData({});
20132             var str = this.stores[opts.list+1];
20133             str.data.clear(); // removeall wihtout the fire events..
20134             return;
20135         }
20136         
20137         var rec = view.store.getAt(ix[0]);
20138          
20139         this.setFromData(rec.data);
20140         this.fireEvent('select', this, rec, ix[0]);
20141         
20142         var lw = Math.floor(
20143              (
20144                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20145              ) / this.maxColumns
20146         );
20147         this.loadingChildren = true;
20148         this.stores[opts.list+1].loadDataFromChildren( rec );
20149         this.loadingChildren = false;
20150         var dl = this.stores[opts.list+1]. getTotalCount();
20151         
20152         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20153         
20154         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20155         for (var i = opts.list+2; i < this.views.length;i++) {
20156             this.views[i].getEl().setStyle({ display : 'none' });
20157         }
20158         
20159         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20160         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20161         
20162         if (this.isLoading) {
20163            // this.selectActive(opts.list);
20164         }
20165          
20166     },
20167     
20168     
20169     
20170     
20171     onDoubleClick : function()
20172     {
20173         this.collapse(); //??
20174     },
20175     
20176      
20177     
20178     
20179     
20180     // private
20181     recordToStack : function(store, prop, value, stack)
20182     {
20183         var cstore = new Roo.data.SimpleStore({
20184             //fields : this.store.reader.meta.fields, // we need array reader.. for
20185             reader : this.store.reader,
20186             data : [ ]
20187         });
20188         var _this = this;
20189         var record  = false;
20190         var srec = false;
20191         if(store.getCount() < 1){
20192             return false;
20193         }
20194         store.each(function(r){
20195             if(r.data[prop] == value){
20196                 record = r;
20197             srec = r;
20198                 return false;
20199             }
20200             if (r.data.cn && r.data.cn.length) {
20201                 cstore.loadDataFromChildren( r);
20202                 var cret = _this.recordToStack(cstore, prop, value, stack);
20203                 if (cret !== false) {
20204                     record = cret;
20205                     srec = r;
20206                     return false;
20207                 }
20208             }
20209              
20210             return true;
20211         });
20212         if (record == false) {
20213             return false
20214         }
20215         stack.unshift(srec);
20216         return record;
20217     },
20218     
20219     /*
20220      * find the stack of stores that match our value.
20221      *
20222      * 
20223      */
20224     
20225     selectActive : function ()
20226     {
20227         // if store is not loaded, then we will need to wait for that to happen first.
20228         var stack = [];
20229         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20230         for (var i = 0; i < stack.length; i++ ) {
20231             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20232         }
20233         
20234     }
20235         
20236          
20237     
20238     
20239     
20240     
20241 });/*
20242  * Based on:
20243  * Ext JS Library 1.1.1
20244  * Copyright(c) 2006-2007, Ext JS, LLC.
20245  *
20246  * Originally Released Under LGPL - original licence link has changed is not relivant.
20247  *
20248  * Fork - LGPL
20249  * <script type="text/javascript">
20250  */
20251 /**
20252  * @class Roo.form.Checkbox
20253  * @extends Roo.form.Field
20254  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20255  * @constructor
20256  * Creates a new Checkbox
20257  * @param {Object} config Configuration options
20258  */
20259 Roo.form.Checkbox = function(config){
20260     Roo.form.Checkbox.superclass.constructor.call(this, config);
20261     this.addEvents({
20262         /**
20263          * @event check
20264          * Fires when the checkbox is checked or unchecked.
20265              * @param {Roo.form.Checkbox} this This checkbox
20266              * @param {Boolean} checked The new checked value
20267              */
20268         check : true
20269     });
20270 };
20271
20272 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20273     /**
20274      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20275      */
20276     focusClass : undefined,
20277     /**
20278      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20279      */
20280     fieldClass: "x-form-field",
20281     /**
20282      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20283      */
20284     checked: false,
20285     /**
20286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20287      * {tag: "input", type: "checkbox", autocomplete: "off"})
20288      */
20289     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20290     /**
20291      * @cfg {String} boxLabel The text that appears beside the checkbox
20292      */
20293     boxLabel : "",
20294     /**
20295      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20296      */  
20297     inputValue : '1',
20298     /**
20299      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20300      */
20301      valueOff: '0', // value when not checked..
20302
20303     actionMode : 'viewEl', 
20304     //
20305     // private
20306     itemCls : 'x-menu-check-item x-form-item',
20307     groupClass : 'x-menu-group-item',
20308     inputType : 'hidden',
20309     
20310     
20311     inSetChecked: false, // check that we are not calling self...
20312     
20313     inputElement: false, // real input element?
20314     basedOn: false, // ????
20315     
20316     isFormField: true, // not sure where this is needed!!!!
20317
20318     onResize : function(){
20319         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20320         if(!this.boxLabel){
20321             this.el.alignTo(this.wrap, 'c-c');
20322         }
20323     },
20324
20325     initEvents : function(){
20326         Roo.form.Checkbox.superclass.initEvents.call(this);
20327         this.el.on("click", this.onClick,  this);
20328         this.el.on("change", this.onClick,  this);
20329     },
20330
20331
20332     getResizeEl : function(){
20333         return this.wrap;
20334     },
20335
20336     getPositionEl : function(){
20337         return this.wrap;
20338     },
20339
20340     // private
20341     onRender : function(ct, position){
20342         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20343         /*
20344         if(this.inputValue !== undefined){
20345             this.el.dom.value = this.inputValue;
20346         }
20347         */
20348         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20349         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20350         var viewEl = this.wrap.createChild({ 
20351             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20352         this.viewEl = viewEl;   
20353         this.wrap.on('click', this.onClick,  this); 
20354         
20355         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20356         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20357         
20358         
20359         
20360         if(this.boxLabel){
20361             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20362         //    viewEl.on('click', this.onClick,  this); 
20363         }
20364         //if(this.checked){
20365             this.setChecked(this.checked);
20366         //}else{
20367             //this.checked = this.el.dom;
20368         //}
20369
20370     },
20371
20372     // private
20373     initValue : Roo.emptyFn,
20374
20375     /**
20376      * Returns the checked state of the checkbox.
20377      * @return {Boolean} True if checked, else false
20378      */
20379     getValue : function(){
20380         if(this.el){
20381             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20382         }
20383         return this.valueOff;
20384         
20385     },
20386
20387         // private
20388     onClick : function(){ 
20389         if (this.disabled) {
20390             return;
20391         }
20392         this.setChecked(!this.checked);
20393
20394         //if(this.el.dom.checked != this.checked){
20395         //    this.setValue(this.el.dom.checked);
20396        // }
20397     },
20398
20399     /**
20400      * Sets the checked state of the checkbox.
20401      * On is always based on a string comparison between inputValue and the param.
20402      * @param {Boolean/String} value - the value to set 
20403      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20404      */
20405     setValue : function(v,suppressEvent){
20406         
20407         
20408         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20409         //if(this.el && this.el.dom){
20410         //    this.el.dom.checked = this.checked;
20411         //    this.el.dom.defaultChecked = this.checked;
20412         //}
20413         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20414         //this.fireEvent("check", this, this.checked);
20415     },
20416     // private..
20417     setChecked : function(state,suppressEvent)
20418     {
20419         if (this.inSetChecked) {
20420             this.checked = state;
20421             return;
20422         }
20423         
20424     
20425         if(this.wrap){
20426             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20427         }
20428         this.checked = state;
20429         if(suppressEvent !== true){
20430             this.fireEvent('check', this, state);
20431         }
20432         this.inSetChecked = true;
20433         this.el.dom.value = state ? this.inputValue : this.valueOff;
20434         this.inSetChecked = false;
20435         
20436     },
20437     // handle setting of hidden value by some other method!!?!?
20438     setFromHidden: function()
20439     {
20440         if(!this.el){
20441             return;
20442         }
20443         //console.log("SET FROM HIDDEN");
20444         //alert('setFrom hidden');
20445         this.setValue(this.el.dom.value);
20446     },
20447     
20448     onDestroy : function()
20449     {
20450         if(this.viewEl){
20451             Roo.get(this.viewEl).remove();
20452         }
20453          
20454         Roo.form.Checkbox.superclass.onDestroy.call(this);
20455     },
20456     
20457     setBoxLabel : function(str)
20458     {
20459         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20460     }
20461
20462 });/*
20463  * Based on:
20464  * Ext JS Library 1.1.1
20465  * Copyright(c) 2006-2007, Ext JS, LLC.
20466  *
20467  * Originally Released Under LGPL - original licence link has changed is not relivant.
20468  *
20469  * Fork - LGPL
20470  * <script type="text/javascript">
20471  */
20472  
20473 /**
20474  * @class Roo.form.Radio
20475  * @extends Roo.form.Checkbox
20476  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20477  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20478  * @constructor
20479  * Creates a new Radio
20480  * @param {Object} config Configuration options
20481  */
20482 Roo.form.Radio = function(){
20483     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20484 };
20485 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20486     inputType: 'radio',
20487
20488     /**
20489      * If this radio is part of a group, it will return the selected value
20490      * @return {String}
20491      */
20492     getGroupValue : function(){
20493         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20494     },
20495     
20496     
20497     onRender : function(ct, position){
20498         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20499         
20500         if(this.inputValue !== undefined){
20501             this.el.dom.value = this.inputValue;
20502         }
20503          
20504         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20505         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20506         //var viewEl = this.wrap.createChild({ 
20507         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20508         //this.viewEl = viewEl;   
20509         //this.wrap.on('click', this.onClick,  this); 
20510         
20511         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20512         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20513         
20514         
20515         
20516         if(this.boxLabel){
20517             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20518         //    viewEl.on('click', this.onClick,  this); 
20519         }
20520          if(this.checked){
20521             this.el.dom.checked =   'checked' ;
20522         }
20523          
20524     } 
20525     
20526     
20527 });//<script type="text/javascript">
20528
20529 /*
20530  * Based  Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  * LGPL
20533  *
20534  */
20535  
20536 /**
20537  * @class Roo.HtmlEditorCore
20538  * @extends Roo.Component
20539  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20540  *
20541  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20542  */
20543
20544 Roo.HtmlEditorCore = function(config){
20545     
20546     
20547     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20548     
20549     
20550     this.addEvents({
20551         /**
20552          * @event initialize
20553          * Fires when the editor is fully initialized (including the iframe)
20554          * @param {Roo.HtmlEditorCore} this
20555          */
20556         initialize: true,
20557         /**
20558          * @event activate
20559          * Fires when the editor is first receives the focus. Any insertion must wait
20560          * until after this event.
20561          * @param {Roo.HtmlEditorCore} this
20562          */
20563         activate: true,
20564          /**
20565          * @event beforesync
20566          * Fires before the textarea is updated with content from the editor iframe. Return false
20567          * to cancel the sync.
20568          * @param {Roo.HtmlEditorCore} this
20569          * @param {String} html
20570          */
20571         beforesync: true,
20572          /**
20573          * @event beforepush
20574          * Fires before the iframe editor is updated with content from the textarea. Return false
20575          * to cancel the push.
20576          * @param {Roo.HtmlEditorCore} this
20577          * @param {String} html
20578          */
20579         beforepush: true,
20580          /**
20581          * @event sync
20582          * Fires when the textarea is updated with content from the editor iframe.
20583          * @param {Roo.HtmlEditorCore} this
20584          * @param {String} html
20585          */
20586         sync: true,
20587          /**
20588          * @event push
20589          * Fires when the iframe editor is updated with content from the textarea.
20590          * @param {Roo.HtmlEditorCore} this
20591          * @param {String} html
20592          */
20593         push: true,
20594         
20595         /**
20596          * @event editorevent
20597          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20598          * @param {Roo.HtmlEditorCore} this
20599          */
20600         editorevent: true
20601         
20602     });
20603     
20604     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20605     
20606     // defaults : white / black...
20607     this.applyBlacklists();
20608     
20609     
20610     
20611 };
20612
20613
20614 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20615
20616
20617      /**
20618      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20619      */
20620     
20621     owner : false,
20622     
20623      /**
20624      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20625      *                        Roo.resizable.
20626      */
20627     resizable : false,
20628      /**
20629      * @cfg {Number} height (in pixels)
20630      */   
20631     height: 300,
20632    /**
20633      * @cfg {Number} width (in pixels)
20634      */   
20635     width: 500,
20636     
20637     /**
20638      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20639      * 
20640      */
20641     stylesheets: false,
20642     
20643     /**
20644      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20645      */
20646     allowComments: false,
20647     // id of frame..
20648     frameId: false,
20649     
20650     // private properties
20651     validationEvent : false,
20652     deferHeight: true,
20653     initialized : false,
20654     activated : false,
20655     sourceEditMode : false,
20656     onFocus : Roo.emptyFn,
20657     iframePad:3,
20658     hideMode:'offsets',
20659     
20660     clearUp: true,
20661     
20662     // blacklist + whitelisted elements..
20663     black: false,
20664     white: false,
20665      
20666     bodyCls : '',
20667
20668     /**
20669      * Protected method that will not generally be called directly. It
20670      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20671      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20672      */
20673     getDocMarkup : function(){
20674         // body styles..
20675         var st = '';
20676         
20677         // inherit styels from page...?? 
20678         if (this.stylesheets === false) {
20679             
20680             Roo.get(document.head).select('style').each(function(node) {
20681                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20682             });
20683             
20684             Roo.get(document.head).select('link').each(function(node) { 
20685                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20686             });
20687             
20688         } else if (!this.stylesheets.length) {
20689                 // simple..
20690                 st = '<style type="text/css">' +
20691                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20692                    '</style>';
20693         } else {
20694             for (var i in this.stylesheets) { 
20695                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20696             }
20697             
20698         }
20699         
20700         st +=  '<style type="text/css">' +
20701             'IMG { cursor: pointer } ' +
20702         '</style>';
20703
20704         var cls = 'roo-htmleditor-body';
20705         
20706         if(this.bodyCls.length){
20707             cls += ' ' + this.bodyCls;
20708         }
20709         
20710         return '<html><head>' + st  +
20711             //<style type="text/css">' +
20712             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20713             //'</style>' +
20714             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20715     },
20716
20717     // private
20718     onRender : function(ct, position)
20719     {
20720         var _t = this;
20721         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20722         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20723         
20724         
20725         this.el.dom.style.border = '0 none';
20726         this.el.dom.setAttribute('tabIndex', -1);
20727         this.el.addClass('x-hidden hide');
20728         
20729         
20730         
20731         if(Roo.isIE){ // fix IE 1px bogus margin
20732             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20733         }
20734        
20735         
20736         this.frameId = Roo.id();
20737         
20738          
20739         
20740         var iframe = this.owner.wrap.createChild({
20741             tag: 'iframe',
20742             cls: 'form-control', // bootstrap..
20743             id: this.frameId,
20744             name: this.frameId,
20745             frameBorder : 'no',
20746             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20747         }, this.el
20748         );
20749         
20750         
20751         this.iframe = iframe.dom;
20752
20753          this.assignDocWin();
20754         
20755         this.doc.designMode = 'on';
20756        
20757         this.doc.open();
20758         this.doc.write(this.getDocMarkup());
20759         this.doc.close();
20760
20761         
20762         var task = { // must defer to wait for browser to be ready
20763             run : function(){
20764                 //console.log("run task?" + this.doc.readyState);
20765                 this.assignDocWin();
20766                 if(this.doc.body || this.doc.readyState == 'complete'){
20767                     try {
20768                         this.doc.designMode="on";
20769                     } catch (e) {
20770                         return;
20771                     }
20772                     Roo.TaskMgr.stop(task);
20773                     this.initEditor.defer(10, this);
20774                 }
20775             },
20776             interval : 10,
20777             duration: 10000,
20778             scope: this
20779         };
20780         Roo.TaskMgr.start(task);
20781
20782     },
20783
20784     // private
20785     onResize : function(w, h)
20786     {
20787          Roo.log('resize: ' +w + ',' + h );
20788         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20789         if(!this.iframe){
20790             return;
20791         }
20792         if(typeof w == 'number'){
20793             
20794             this.iframe.style.width = w + 'px';
20795         }
20796         if(typeof h == 'number'){
20797             
20798             this.iframe.style.height = h + 'px';
20799             if(this.doc){
20800                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20801             }
20802         }
20803         
20804     },
20805
20806     /**
20807      * Toggles the editor between standard and source edit mode.
20808      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20809      */
20810     toggleSourceEdit : function(sourceEditMode){
20811         
20812         this.sourceEditMode = sourceEditMode === true;
20813         
20814         if(this.sourceEditMode){
20815  
20816             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20817             
20818         }else{
20819             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20820             //this.iframe.className = '';
20821             this.deferFocus();
20822         }
20823         //this.setSize(this.owner.wrap.getSize());
20824         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20825     },
20826
20827     
20828   
20829
20830     /**
20831      * Protected method that will not generally be called directly. If you need/want
20832      * custom HTML cleanup, this is the method you should override.
20833      * @param {String} html The HTML to be cleaned
20834      * return {String} The cleaned HTML
20835      */
20836     cleanHtml : function(html){
20837         html = String(html);
20838         if(html.length > 5){
20839             if(Roo.isSafari){ // strip safari nonsense
20840                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20841             }
20842         }
20843         if(html == '&nbsp;'){
20844             html = '';
20845         }
20846         return html;
20847     },
20848
20849     /**
20850      * HTML Editor -> Textarea
20851      * Protected method that will not generally be called directly. Syncs the contents
20852      * of the editor iframe with the textarea.
20853      */
20854     syncValue : function(){
20855         if(this.initialized){
20856             var bd = (this.doc.body || this.doc.documentElement);
20857             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20858             var html = bd.innerHTML;
20859             if(Roo.isSafari){
20860                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20861                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20862                 if(m && m[1]){
20863                     html = '<div style="'+m[0]+'">' + html + '</div>';
20864                 }
20865             }
20866             html = this.cleanHtml(html);
20867             // fix up the special chars.. normaly like back quotes in word...
20868             // however we do not want to do this with chinese..
20869             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20870                 
20871                 var cc = match.charCodeAt();
20872
20873                 // Get the character value, handling surrogate pairs
20874                 if (match.length == 2) {
20875                     // It's a surrogate pair, calculate the Unicode code point
20876                     var high = match.charCodeAt(0) - 0xD800;
20877                     var low  = match.charCodeAt(1) - 0xDC00;
20878                     cc = (high * 0x400) + low + 0x10000;
20879                 }  else if (
20880                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20881                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20882                     (cc >= 0xf900 && cc < 0xfb00 )
20883                 ) {
20884                         return match;
20885                 }  
20886          
20887                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20888                 return "&#" + cc + ";";
20889                 
20890                 
20891             });
20892             
20893             
20894              
20895             if(this.owner.fireEvent('beforesync', this, html) !== false){
20896                 this.el.dom.value = html;
20897                 this.owner.fireEvent('sync', this, html);
20898             }
20899         }
20900     },
20901
20902     /**
20903      * Protected method that will not generally be called directly. Pushes the value of the textarea
20904      * into the iframe editor.
20905      */
20906     pushValue : function(){
20907         if(this.initialized){
20908             var v = this.el.dom.value.trim();
20909             
20910 //            if(v.length < 1){
20911 //                v = '&#160;';
20912 //            }
20913             
20914             if(this.owner.fireEvent('beforepush', this, v) !== false){
20915                 var d = (this.doc.body || this.doc.documentElement);
20916                 d.innerHTML = v;
20917                 this.cleanUpPaste();
20918                 this.el.dom.value = d.innerHTML;
20919                 this.owner.fireEvent('push', this, v);
20920             }
20921         }
20922     },
20923
20924     // private
20925     deferFocus : function(){
20926         this.focus.defer(10, this);
20927     },
20928
20929     // doc'ed in Field
20930     focus : function(){
20931         if(this.win && !this.sourceEditMode){
20932             this.win.focus();
20933         }else{
20934             this.el.focus();
20935         }
20936     },
20937     
20938     assignDocWin: function()
20939     {
20940         var iframe = this.iframe;
20941         
20942          if(Roo.isIE){
20943             this.doc = iframe.contentWindow.document;
20944             this.win = iframe.contentWindow;
20945         } else {
20946 //            if (!Roo.get(this.frameId)) {
20947 //                return;
20948 //            }
20949 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20950 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20951             
20952             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20953                 return;
20954             }
20955             
20956             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20957             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20958         }
20959     },
20960     
20961     // private
20962     initEditor : function(){
20963         //console.log("INIT EDITOR");
20964         this.assignDocWin();
20965         
20966         
20967         
20968         this.doc.designMode="on";
20969         this.doc.open();
20970         this.doc.write(this.getDocMarkup());
20971         this.doc.close();
20972         
20973         var dbody = (this.doc.body || this.doc.documentElement);
20974         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20975         // this copies styles from the containing element into thsi one..
20976         // not sure why we need all of this..
20977         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20978         
20979         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20980         //ss['background-attachment'] = 'fixed'; // w3c
20981         dbody.bgProperties = 'fixed'; // ie
20982         //Roo.DomHelper.applyStyles(dbody, ss);
20983         Roo.EventManager.on(this.doc, {
20984             //'mousedown': this.onEditorEvent,
20985             'mouseup': this.onEditorEvent,
20986             'dblclick': this.onEditorEvent,
20987             'click': this.onEditorEvent,
20988             'keyup': this.onEditorEvent,
20989             buffer:100,
20990             scope: this
20991         });
20992         if(Roo.isGecko){
20993             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20994         }
20995         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20996             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20997         }
20998         this.initialized = true;
20999
21000         this.owner.fireEvent('initialize', this);
21001         this.pushValue();
21002     },
21003
21004     // private
21005     onDestroy : function(){
21006         
21007         
21008         
21009         if(this.rendered){
21010             
21011             //for (var i =0; i < this.toolbars.length;i++) {
21012             //    // fixme - ask toolbars for heights?
21013             //    this.toolbars[i].onDestroy();
21014            // }
21015             
21016             //this.wrap.dom.innerHTML = '';
21017             //this.wrap.remove();
21018         }
21019     },
21020
21021     // private
21022     onFirstFocus : function(){
21023         
21024         this.assignDocWin();
21025         
21026         
21027         this.activated = true;
21028          
21029     
21030         if(Roo.isGecko){ // prevent silly gecko errors
21031             this.win.focus();
21032             var s = this.win.getSelection();
21033             if(!s.focusNode || s.focusNode.nodeType != 3){
21034                 var r = s.getRangeAt(0);
21035                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21036                 r.collapse(true);
21037                 this.deferFocus();
21038             }
21039             try{
21040                 this.execCmd('useCSS', true);
21041                 this.execCmd('styleWithCSS', false);
21042             }catch(e){}
21043         }
21044         this.owner.fireEvent('activate', this);
21045     },
21046
21047     // private
21048     adjustFont: function(btn){
21049         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21050         //if(Roo.isSafari){ // safari
21051         //    adjust *= 2;
21052        // }
21053         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21054         if(Roo.isSafari){ // safari
21055             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21056             v =  (v < 10) ? 10 : v;
21057             v =  (v > 48) ? 48 : v;
21058             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21059             
21060         }
21061         
21062         
21063         v = Math.max(1, v+adjust);
21064         
21065         this.execCmd('FontSize', v  );
21066     },
21067
21068     onEditorEvent : function(e)
21069     {
21070         this.owner.fireEvent('editorevent', this, e);
21071       //  this.updateToolbar();
21072         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21073     },
21074
21075     insertTag : function(tg)
21076     {
21077         // could be a bit smarter... -> wrap the current selected tRoo..
21078         if (tg.toLowerCase() == 'span' ||
21079             tg.toLowerCase() == 'code' ||
21080             tg.toLowerCase() == 'sup' ||
21081             tg.toLowerCase() == 'sub' 
21082             ) {
21083             
21084             range = this.createRange(this.getSelection());
21085             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21086             wrappingNode.appendChild(range.extractContents());
21087             range.insertNode(wrappingNode);
21088
21089             return;
21090             
21091             
21092             
21093         }
21094         this.execCmd("formatblock",   tg);
21095         
21096     },
21097     
21098     insertText : function(txt)
21099     {
21100         
21101         
21102         var range = this.createRange();
21103         range.deleteContents();
21104                //alert(Sender.getAttribute('label'));
21105                
21106         range.insertNode(this.doc.createTextNode(txt));
21107     } ,
21108     
21109      
21110
21111     /**
21112      * Executes a Midas editor command on the editor document and performs necessary focus and
21113      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21114      * @param {String} cmd The Midas command
21115      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21116      */
21117     relayCmd : function(cmd, value){
21118         this.win.focus();
21119         this.execCmd(cmd, value);
21120         this.owner.fireEvent('editorevent', this);
21121         //this.updateToolbar();
21122         this.owner.deferFocus();
21123     },
21124
21125     /**
21126      * Executes a Midas editor command directly on the editor document.
21127      * For visual commands, you should use {@link #relayCmd} instead.
21128      * <b>This should only be called after the editor is initialized.</b>
21129      * @param {String} cmd The Midas command
21130      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21131      */
21132     execCmd : function(cmd, value){
21133         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21134         this.syncValue();
21135     },
21136  
21137  
21138    
21139     /**
21140      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21141      * to insert tRoo.
21142      * @param {String} text | dom node.. 
21143      */
21144     insertAtCursor : function(text)
21145     {
21146         
21147         if(!this.activated){
21148             return;
21149         }
21150         /*
21151         if(Roo.isIE){
21152             this.win.focus();
21153             var r = this.doc.selection.createRange();
21154             if(r){
21155                 r.collapse(true);
21156                 r.pasteHTML(text);
21157                 this.syncValue();
21158                 this.deferFocus();
21159             
21160             }
21161             return;
21162         }
21163         */
21164         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21165             this.win.focus();
21166             
21167             
21168             // from jquery ui (MIT licenced)
21169             var range, node;
21170             var win = this.win;
21171             
21172             if (win.getSelection && win.getSelection().getRangeAt) {
21173                 range = win.getSelection().getRangeAt(0);
21174                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21175                 range.insertNode(node);
21176             } else if (win.document.selection && win.document.selection.createRange) {
21177                 // no firefox support
21178                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21179                 win.document.selection.createRange().pasteHTML(txt);
21180             } else {
21181                 // no firefox support
21182                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21183                 this.execCmd('InsertHTML', txt);
21184             } 
21185             
21186             this.syncValue();
21187             
21188             this.deferFocus();
21189         }
21190     },
21191  // private
21192     mozKeyPress : function(e){
21193         if(e.ctrlKey){
21194             var c = e.getCharCode(), cmd;
21195           
21196             if(c > 0){
21197                 c = String.fromCharCode(c).toLowerCase();
21198                 switch(c){
21199                     case 'b':
21200                         cmd = 'bold';
21201                         break;
21202                     case 'i':
21203                         cmd = 'italic';
21204                         break;
21205                     
21206                     case 'u':
21207                         cmd = 'underline';
21208                         break;
21209                     
21210                     case 'v':
21211                         this.cleanUpPaste.defer(100, this);
21212                         return;
21213                         
21214                 }
21215                 if(cmd){
21216                     this.win.focus();
21217                     this.execCmd(cmd);
21218                     this.deferFocus();
21219                     e.preventDefault();
21220                 }
21221                 
21222             }
21223         }
21224     },
21225
21226     // private
21227     fixKeys : function(){ // load time branching for fastest keydown performance
21228         if(Roo.isIE){
21229             return function(e){
21230                 var k = e.getKey(), r;
21231                 if(k == e.TAB){
21232                     e.stopEvent();
21233                     r = this.doc.selection.createRange();
21234                     if(r){
21235                         r.collapse(true);
21236                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21237                         this.deferFocus();
21238                     }
21239                     return;
21240                 }
21241                 
21242                 if(k == e.ENTER){
21243                     r = this.doc.selection.createRange();
21244                     if(r){
21245                         var target = r.parentElement();
21246                         if(!target || target.tagName.toLowerCase() != 'li'){
21247                             e.stopEvent();
21248                             r.pasteHTML('<br />');
21249                             r.collapse(false);
21250                             r.select();
21251                         }
21252                     }
21253                 }
21254                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21255                     this.cleanUpPaste.defer(100, this);
21256                     return;
21257                 }
21258                 
21259                 
21260             };
21261         }else if(Roo.isOpera){
21262             return function(e){
21263                 var k = e.getKey();
21264                 if(k == e.TAB){
21265                     e.stopEvent();
21266                     this.win.focus();
21267                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21268                     this.deferFocus();
21269                 }
21270                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21271                     this.cleanUpPaste.defer(100, this);
21272                     return;
21273                 }
21274                 
21275             };
21276         }else if(Roo.isSafari){
21277             return function(e){
21278                 var k = e.getKey();
21279                 
21280                 if(k == e.TAB){
21281                     e.stopEvent();
21282                     this.execCmd('InsertText','\t');
21283                     this.deferFocus();
21284                     return;
21285                 }
21286                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21287                     this.cleanUpPaste.defer(100, this);
21288                     return;
21289                 }
21290                 
21291              };
21292         }
21293     }(),
21294     
21295     getAllAncestors: function()
21296     {
21297         var p = this.getSelectedNode();
21298         var a = [];
21299         if (!p) {
21300             a.push(p); // push blank onto stack..
21301             p = this.getParentElement();
21302         }
21303         
21304         
21305         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21306             a.push(p);
21307             p = p.parentNode;
21308         }
21309         a.push(this.doc.body);
21310         return a;
21311     },
21312     lastSel : false,
21313     lastSelNode : false,
21314     
21315     
21316     getSelection : function() 
21317     {
21318         this.assignDocWin();
21319         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21320     },
21321     
21322     getSelectedNode: function() 
21323     {
21324         // this may only work on Gecko!!!
21325         
21326         // should we cache this!!!!
21327         
21328         
21329         
21330          
21331         var range = this.createRange(this.getSelection()).cloneRange();
21332         
21333         if (Roo.isIE) {
21334             var parent = range.parentElement();
21335             while (true) {
21336                 var testRange = range.duplicate();
21337                 testRange.moveToElementText(parent);
21338                 if (testRange.inRange(range)) {
21339                     break;
21340                 }
21341                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21342                     break;
21343                 }
21344                 parent = parent.parentElement;
21345             }
21346             return parent;
21347         }
21348         
21349         // is ancestor a text element.
21350         var ac =  range.commonAncestorContainer;
21351         if (ac.nodeType == 3) {
21352             ac = ac.parentNode;
21353         }
21354         
21355         var ar = ac.childNodes;
21356          
21357         var nodes = [];
21358         var other_nodes = [];
21359         var has_other_nodes = false;
21360         for (var i=0;i<ar.length;i++) {
21361             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21362                 continue;
21363             }
21364             // fullly contained node.
21365             
21366             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21367                 nodes.push(ar[i]);
21368                 continue;
21369             }
21370             
21371             // probably selected..
21372             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21373                 other_nodes.push(ar[i]);
21374                 continue;
21375             }
21376             // outer..
21377             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21378                 continue;
21379             }
21380             
21381             
21382             has_other_nodes = true;
21383         }
21384         if (!nodes.length && other_nodes.length) {
21385             nodes= other_nodes;
21386         }
21387         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21388             return false;
21389         }
21390         
21391         return nodes[0];
21392     },
21393     createRange: function(sel)
21394     {
21395         // this has strange effects when using with 
21396         // top toolbar - not sure if it's a great idea.
21397         //this.editor.contentWindow.focus();
21398         if (typeof sel != "undefined") {
21399             try {
21400                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21401             } catch(e) {
21402                 return this.doc.createRange();
21403             }
21404         } else {
21405             return this.doc.createRange();
21406         }
21407     },
21408     getParentElement: function()
21409     {
21410         
21411         this.assignDocWin();
21412         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21413         
21414         var range = this.createRange(sel);
21415          
21416         try {
21417             var p = range.commonAncestorContainer;
21418             while (p.nodeType == 3) { // text node
21419                 p = p.parentNode;
21420             }
21421             return p;
21422         } catch (e) {
21423             return null;
21424         }
21425     
21426     },
21427     /***
21428      *
21429      * Range intersection.. the hard stuff...
21430      *  '-1' = before
21431      *  '0' = hits..
21432      *  '1' = after.
21433      *         [ -- selected range --- ]
21434      *   [fail]                        [fail]
21435      *
21436      *    basically..
21437      *      if end is before start or  hits it. fail.
21438      *      if start is after end or hits it fail.
21439      *
21440      *   if either hits (but other is outside. - then it's not 
21441      *   
21442      *    
21443      **/
21444     
21445     
21446     // @see http://www.thismuchiknow.co.uk/?p=64.
21447     rangeIntersectsNode : function(range, node)
21448     {
21449         var nodeRange = node.ownerDocument.createRange();
21450         try {
21451             nodeRange.selectNode(node);
21452         } catch (e) {
21453             nodeRange.selectNodeContents(node);
21454         }
21455     
21456         var rangeStartRange = range.cloneRange();
21457         rangeStartRange.collapse(true);
21458     
21459         var rangeEndRange = range.cloneRange();
21460         rangeEndRange.collapse(false);
21461     
21462         var nodeStartRange = nodeRange.cloneRange();
21463         nodeStartRange.collapse(true);
21464     
21465         var nodeEndRange = nodeRange.cloneRange();
21466         nodeEndRange.collapse(false);
21467     
21468         return rangeStartRange.compareBoundaryPoints(
21469                  Range.START_TO_START, nodeEndRange) == -1 &&
21470                rangeEndRange.compareBoundaryPoints(
21471                  Range.START_TO_START, nodeStartRange) == 1;
21472         
21473          
21474     },
21475     rangeCompareNode : function(range, node)
21476     {
21477         var nodeRange = node.ownerDocument.createRange();
21478         try {
21479             nodeRange.selectNode(node);
21480         } catch (e) {
21481             nodeRange.selectNodeContents(node);
21482         }
21483         
21484         
21485         range.collapse(true);
21486     
21487         nodeRange.collapse(true);
21488      
21489         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21490         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21491          
21492         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21493         
21494         var nodeIsBefore   =  ss == 1;
21495         var nodeIsAfter    = ee == -1;
21496         
21497         if (nodeIsBefore && nodeIsAfter) {
21498             return 0; // outer
21499         }
21500         if (!nodeIsBefore && nodeIsAfter) {
21501             return 1; //right trailed.
21502         }
21503         
21504         if (nodeIsBefore && !nodeIsAfter) {
21505             return 2;  // left trailed.
21506         }
21507         // fully contined.
21508         return 3;
21509     },
21510
21511     // private? - in a new class?
21512     cleanUpPaste :  function()
21513     {
21514         // cleans up the whole document..
21515         Roo.log('cleanuppaste');
21516         
21517         this.cleanUpChildren(this.doc.body);
21518         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21519         if (clean != this.doc.body.innerHTML) {
21520             this.doc.body.innerHTML = clean;
21521         }
21522         
21523     },
21524     
21525     cleanWordChars : function(input) {// change the chars to hex code
21526         var he = Roo.HtmlEditorCore;
21527         
21528         var output = input;
21529         Roo.each(he.swapCodes, function(sw) { 
21530             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21531             
21532             output = output.replace(swapper, sw[1]);
21533         });
21534         
21535         return output;
21536     },
21537     
21538     
21539     cleanUpChildren : function (n)
21540     {
21541         if (!n.childNodes.length) {
21542             return;
21543         }
21544         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21545            this.cleanUpChild(n.childNodes[i]);
21546         }
21547     },
21548     
21549     
21550         
21551     
21552     cleanUpChild : function (node)
21553     {
21554         var ed = this;
21555         //console.log(node);
21556         if (node.nodeName == "#text") {
21557             // clean up silly Windows -- stuff?
21558             return; 
21559         }
21560         if (node.nodeName == "#comment") {
21561             if (!this.allowComments) {
21562                 node.parentNode.removeChild(node);
21563             }
21564             // clean up silly Windows -- stuff?
21565             return; 
21566         }
21567         var lcname = node.tagName.toLowerCase();
21568         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21569         // whitelist of tags..
21570         
21571         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21572             // remove node.
21573             node.parentNode.removeChild(node);
21574             return;
21575             
21576         }
21577         
21578         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21579         
21580         // spans with no attributes - just remove them..
21581         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21582             remove_keep_children = true;
21583         }
21584         
21585         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21586         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21587         
21588         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21589         //    remove_keep_children = true;
21590         //}
21591         
21592         if (remove_keep_children) {
21593             this.cleanUpChildren(node);
21594             // inserts everything just before this node...
21595             while (node.childNodes.length) {
21596                 var cn = node.childNodes[0];
21597                 node.removeChild(cn);
21598                 node.parentNode.insertBefore(cn, node);
21599             }
21600             node.parentNode.removeChild(node);
21601             return;
21602         }
21603         
21604         if (!node.attributes || !node.attributes.length) {
21605             
21606           
21607             
21608             
21609             this.cleanUpChildren(node);
21610             return;
21611         }
21612         
21613         function cleanAttr(n,v)
21614         {
21615             
21616             if (v.match(/^\./) || v.match(/^\//)) {
21617                 return;
21618             }
21619             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21620                 return;
21621             }
21622             if (v.match(/^#/)) {
21623                 return;
21624             }
21625             if (v.match(/^\{/)) { // allow template editing.
21626                 return;
21627             }
21628 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21629             node.removeAttribute(n);
21630             
21631         }
21632         
21633         var cwhite = this.cwhite;
21634         var cblack = this.cblack;
21635             
21636         function cleanStyle(n,v)
21637         {
21638             if (v.match(/expression/)) { //XSS?? should we even bother..
21639                 node.removeAttribute(n);
21640                 return;
21641             }
21642             
21643             var parts = v.split(/;/);
21644             var clean = [];
21645             
21646             Roo.each(parts, function(p) {
21647                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21648                 if (!p.length) {
21649                     return true;
21650                 }
21651                 var l = p.split(':').shift().replace(/\s+/g,'');
21652                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21653                 
21654                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21655 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21656                     //node.removeAttribute(n);
21657                     return true;
21658                 }
21659                 //Roo.log()
21660                 // only allow 'c whitelisted system attributes'
21661                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21662 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21663                     //node.removeAttribute(n);
21664                     return true;
21665                 }
21666                 
21667                 
21668                  
21669                 
21670                 clean.push(p);
21671                 return true;
21672             });
21673             if (clean.length) { 
21674                 node.setAttribute(n, clean.join(';'));
21675             } else {
21676                 node.removeAttribute(n);
21677             }
21678             
21679         }
21680         
21681         
21682         for (var i = node.attributes.length-1; i > -1 ; i--) {
21683             var a = node.attributes[i];
21684             //console.log(a);
21685             
21686             if (a.name.toLowerCase().substr(0,2)=='on')  {
21687                 node.removeAttribute(a.name);
21688                 continue;
21689             }
21690             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21691                 node.removeAttribute(a.name);
21692                 continue;
21693             }
21694             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21695                 cleanAttr(a.name,a.value); // fixme..
21696                 continue;
21697             }
21698             if (a.name == 'style') {
21699                 cleanStyle(a.name,a.value);
21700                 continue;
21701             }
21702             /// clean up MS crap..
21703             // tecnically this should be a list of valid class'es..
21704             
21705             
21706             if (a.name == 'class') {
21707                 if (a.value.match(/^Mso/)) {
21708                     node.removeAttribute('class');
21709                 }
21710                 
21711                 if (a.value.match(/^body$/)) {
21712                     node.removeAttribute('class');
21713                 }
21714                 continue;
21715             }
21716             
21717             // style cleanup!?
21718             // class cleanup?
21719             
21720         }
21721         
21722         
21723         this.cleanUpChildren(node);
21724         
21725         
21726     },
21727     
21728     /**
21729      * Clean up MS wordisms...
21730      */
21731     cleanWord : function(node)
21732     {
21733         if (!node) {
21734             this.cleanWord(this.doc.body);
21735             return;
21736         }
21737         
21738         if(
21739                 node.nodeName == 'SPAN' &&
21740                 !node.hasAttributes() &&
21741                 node.childNodes.length == 1 &&
21742                 node.firstChild.nodeName == "#text"  
21743         ) {
21744             var textNode = node.firstChild;
21745             node.removeChild(textNode);
21746             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21747                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21748             }
21749             node.parentNode.insertBefore(textNode, node);
21750             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21751                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21752             }
21753             node.parentNode.removeChild(node);
21754         }
21755         
21756         if (node.nodeName == "#text") {
21757             // clean up silly Windows -- stuff?
21758             return; 
21759         }
21760         if (node.nodeName == "#comment") {
21761             node.parentNode.removeChild(node);
21762             // clean up silly Windows -- stuff?
21763             return; 
21764         }
21765         
21766         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21767             node.parentNode.removeChild(node);
21768             return;
21769         }
21770         //Roo.log(node.tagName);
21771         // remove - but keep children..
21772         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21773             //Roo.log('-- removed');
21774             while (node.childNodes.length) {
21775                 var cn = node.childNodes[0];
21776                 node.removeChild(cn);
21777                 node.parentNode.insertBefore(cn, node);
21778                 // move node to parent - and clean it..
21779                 this.cleanWord(cn);
21780             }
21781             node.parentNode.removeChild(node);
21782             /// no need to iterate chidlren = it's got none..
21783             //this.iterateChildren(node, this.cleanWord);
21784             return;
21785         }
21786         // clean styles
21787         if (node.className.length) {
21788             
21789             var cn = node.className.split(/\W+/);
21790             var cna = [];
21791             Roo.each(cn, function(cls) {
21792                 if (cls.match(/Mso[a-zA-Z]+/)) {
21793                     return;
21794                 }
21795                 cna.push(cls);
21796             });
21797             node.className = cna.length ? cna.join(' ') : '';
21798             if (!cna.length) {
21799                 node.removeAttribute("class");
21800             }
21801         }
21802         
21803         if (node.hasAttribute("lang")) {
21804             node.removeAttribute("lang");
21805         }
21806         
21807         if (node.hasAttribute("style")) {
21808             
21809             var styles = node.getAttribute("style").split(";");
21810             var nstyle = [];
21811             Roo.each(styles, function(s) {
21812                 if (!s.match(/:/)) {
21813                     return;
21814                 }
21815                 var kv = s.split(":");
21816                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21817                     return;
21818                 }
21819                 // what ever is left... we allow.
21820                 nstyle.push(s);
21821             });
21822             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21823             if (!nstyle.length) {
21824                 node.removeAttribute('style');
21825             }
21826         }
21827         this.iterateChildren(node, this.cleanWord);
21828         
21829         
21830         
21831     },
21832     /**
21833      * iterateChildren of a Node, calling fn each time, using this as the scole..
21834      * @param {DomNode} node node to iterate children of.
21835      * @param {Function} fn method of this class to call on each item.
21836      */
21837     iterateChildren : function(node, fn)
21838     {
21839         if (!node.childNodes.length) {
21840                 return;
21841         }
21842         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21843            fn.call(this, node.childNodes[i])
21844         }
21845     },
21846     
21847     
21848     /**
21849      * cleanTableWidths.
21850      *
21851      * Quite often pasting from word etc.. results in tables with column and widths.
21852      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21853      *
21854      */
21855     cleanTableWidths : function(node)
21856     {
21857          
21858          
21859         if (!node) {
21860             this.cleanTableWidths(this.doc.body);
21861             return;
21862         }
21863         
21864         // ignore list...
21865         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21866             return; 
21867         }
21868         Roo.log(node.tagName);
21869         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21870             this.iterateChildren(node, this.cleanTableWidths);
21871             return;
21872         }
21873         if (node.hasAttribute('width')) {
21874             node.removeAttribute('width');
21875         }
21876         
21877          
21878         if (node.hasAttribute("style")) {
21879             // pretty basic...
21880             
21881             var styles = node.getAttribute("style").split(";");
21882             var nstyle = [];
21883             Roo.each(styles, function(s) {
21884                 if (!s.match(/:/)) {
21885                     return;
21886                 }
21887                 var kv = s.split(":");
21888                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21889                     return;
21890                 }
21891                 // what ever is left... we allow.
21892                 nstyle.push(s);
21893             });
21894             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21895             if (!nstyle.length) {
21896                 node.removeAttribute('style');
21897             }
21898         }
21899         
21900         this.iterateChildren(node, this.cleanTableWidths);
21901         
21902         
21903     },
21904     
21905     
21906     
21907     
21908     domToHTML : function(currentElement, depth, nopadtext) {
21909         
21910         depth = depth || 0;
21911         nopadtext = nopadtext || false;
21912     
21913         if (!currentElement) {
21914             return this.domToHTML(this.doc.body);
21915         }
21916         
21917         //Roo.log(currentElement);
21918         var j;
21919         var allText = false;
21920         var nodeName = currentElement.nodeName;
21921         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21922         
21923         if  (nodeName == '#text') {
21924             
21925             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21926         }
21927         
21928         
21929         var ret = '';
21930         if (nodeName != 'BODY') {
21931              
21932             var i = 0;
21933             // Prints the node tagName, such as <A>, <IMG>, etc
21934             if (tagName) {
21935                 var attr = [];
21936                 for(i = 0; i < currentElement.attributes.length;i++) {
21937                     // quoting?
21938                     var aname = currentElement.attributes.item(i).name;
21939                     if (!currentElement.attributes.item(i).value.length) {
21940                         continue;
21941                     }
21942                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21943                 }
21944                 
21945                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21946             } 
21947             else {
21948                 
21949                 // eack
21950             }
21951         } else {
21952             tagName = false;
21953         }
21954         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21955             return ret;
21956         }
21957         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21958             nopadtext = true;
21959         }
21960         
21961         
21962         // Traverse the tree
21963         i = 0;
21964         var currentElementChild = currentElement.childNodes.item(i);
21965         var allText = true;
21966         var innerHTML  = '';
21967         lastnode = '';
21968         while (currentElementChild) {
21969             // Formatting code (indent the tree so it looks nice on the screen)
21970             var nopad = nopadtext;
21971             if (lastnode == 'SPAN') {
21972                 nopad  = true;
21973             }
21974             // text
21975             if  (currentElementChild.nodeName == '#text') {
21976                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21977                 toadd = nopadtext ? toadd : toadd.trim();
21978                 if (!nopad && toadd.length > 80) {
21979                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21980                 }
21981                 innerHTML  += toadd;
21982                 
21983                 i++;
21984                 currentElementChild = currentElement.childNodes.item(i);
21985                 lastNode = '';
21986                 continue;
21987             }
21988             allText = false;
21989             
21990             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21991                 
21992             // Recursively traverse the tree structure of the child node
21993             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21994             lastnode = currentElementChild.nodeName;
21995             i++;
21996             currentElementChild=currentElement.childNodes.item(i);
21997         }
21998         
21999         ret += innerHTML;
22000         
22001         if (!allText) {
22002                 // The remaining code is mostly for formatting the tree
22003             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22004         }
22005         
22006         
22007         if (tagName) {
22008             ret+= "</"+tagName+">";
22009         }
22010         return ret;
22011         
22012     },
22013         
22014     applyBlacklists : function()
22015     {
22016         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22017         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22018         
22019         this.white = [];
22020         this.black = [];
22021         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22022             if (b.indexOf(tag) > -1) {
22023                 return;
22024             }
22025             this.white.push(tag);
22026             
22027         }, this);
22028         
22029         Roo.each(w, function(tag) {
22030             if (b.indexOf(tag) > -1) {
22031                 return;
22032             }
22033             if (this.white.indexOf(tag) > -1) {
22034                 return;
22035             }
22036             this.white.push(tag);
22037             
22038         }, this);
22039         
22040         
22041         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22042             if (w.indexOf(tag) > -1) {
22043                 return;
22044             }
22045             this.black.push(tag);
22046             
22047         }, this);
22048         
22049         Roo.each(b, function(tag) {
22050             if (w.indexOf(tag) > -1) {
22051                 return;
22052             }
22053             if (this.black.indexOf(tag) > -1) {
22054                 return;
22055             }
22056             this.black.push(tag);
22057             
22058         }, this);
22059         
22060         
22061         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22062         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22063         
22064         this.cwhite = [];
22065         this.cblack = [];
22066         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22067             if (b.indexOf(tag) > -1) {
22068                 return;
22069             }
22070             this.cwhite.push(tag);
22071             
22072         }, this);
22073         
22074         Roo.each(w, function(tag) {
22075             if (b.indexOf(tag) > -1) {
22076                 return;
22077             }
22078             if (this.cwhite.indexOf(tag) > -1) {
22079                 return;
22080             }
22081             this.cwhite.push(tag);
22082             
22083         }, this);
22084         
22085         
22086         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22087             if (w.indexOf(tag) > -1) {
22088                 return;
22089             }
22090             this.cblack.push(tag);
22091             
22092         }, this);
22093         
22094         Roo.each(b, function(tag) {
22095             if (w.indexOf(tag) > -1) {
22096                 return;
22097             }
22098             if (this.cblack.indexOf(tag) > -1) {
22099                 return;
22100             }
22101             this.cblack.push(tag);
22102             
22103         }, this);
22104     },
22105     
22106     setStylesheets : function(stylesheets)
22107     {
22108         if(typeof(stylesheets) == 'string'){
22109             Roo.get(this.iframe.contentDocument.head).createChild({
22110                 tag : 'link',
22111                 rel : 'stylesheet',
22112                 type : 'text/css',
22113                 href : stylesheets
22114             });
22115             
22116             return;
22117         }
22118         var _this = this;
22119      
22120         Roo.each(stylesheets, function(s) {
22121             if(!s.length){
22122                 return;
22123             }
22124             
22125             Roo.get(_this.iframe.contentDocument.head).createChild({
22126                 tag : 'link',
22127                 rel : 'stylesheet',
22128                 type : 'text/css',
22129                 href : s
22130             });
22131         });
22132
22133         
22134     },
22135     
22136     removeStylesheets : function()
22137     {
22138         var _this = this;
22139         
22140         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22141             s.remove();
22142         });
22143     },
22144     
22145     setStyle : function(style)
22146     {
22147         Roo.get(this.iframe.contentDocument.head).createChild({
22148             tag : 'style',
22149             type : 'text/css',
22150             html : style
22151         });
22152
22153         return;
22154     }
22155     
22156     // hide stuff that is not compatible
22157     /**
22158      * @event blur
22159      * @hide
22160      */
22161     /**
22162      * @event change
22163      * @hide
22164      */
22165     /**
22166      * @event focus
22167      * @hide
22168      */
22169     /**
22170      * @event specialkey
22171      * @hide
22172      */
22173     /**
22174      * @cfg {String} fieldClass @hide
22175      */
22176     /**
22177      * @cfg {String} focusClass @hide
22178      */
22179     /**
22180      * @cfg {String} autoCreate @hide
22181      */
22182     /**
22183      * @cfg {String} inputType @hide
22184      */
22185     /**
22186      * @cfg {String} invalidClass @hide
22187      */
22188     /**
22189      * @cfg {String} invalidText @hide
22190      */
22191     /**
22192      * @cfg {String} msgFx @hide
22193      */
22194     /**
22195      * @cfg {String} validateOnBlur @hide
22196      */
22197 });
22198
22199 Roo.HtmlEditorCore.white = [
22200         'area', 'br', 'img', 'input', 'hr', 'wbr',
22201         
22202        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22203        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22204        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22205        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22206        'table',   'ul',         'xmp', 
22207        
22208        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22209       'thead',   'tr', 
22210      
22211       'dir', 'menu', 'ol', 'ul', 'dl',
22212        
22213       'embed',  'object'
22214 ];
22215
22216
22217 Roo.HtmlEditorCore.black = [
22218     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22219         'applet', // 
22220         'base',   'basefont', 'bgsound', 'blink',  'body', 
22221         'frame',  'frameset', 'head',    'html',   'ilayer', 
22222         'iframe', 'layer',  'link',     'meta',    'object',   
22223         'script', 'style' ,'title',  'xml' // clean later..
22224 ];
22225 Roo.HtmlEditorCore.clean = [
22226     'script', 'style', 'title', 'xml'
22227 ];
22228 Roo.HtmlEditorCore.remove = [
22229     'font'
22230 ];
22231 // attributes..
22232
22233 Roo.HtmlEditorCore.ablack = [
22234     'on'
22235 ];
22236     
22237 Roo.HtmlEditorCore.aclean = [ 
22238     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22239 ];
22240
22241 // protocols..
22242 Roo.HtmlEditorCore.pwhite= [
22243         'http',  'https',  'mailto'
22244 ];
22245
22246 // white listed style attributes.
22247 Roo.HtmlEditorCore.cwhite= [
22248       //  'text-align', /// default is to allow most things..
22249       
22250          
22251 //        'font-size'//??
22252 ];
22253
22254 // black listed style attributes.
22255 Roo.HtmlEditorCore.cblack= [
22256       //  'font-size' -- this can be set by the project 
22257 ];
22258
22259
22260 Roo.HtmlEditorCore.swapCodes   =[ 
22261     [    8211, "&#8211;" ], 
22262     [    8212, "&#8212;" ], 
22263     [    8216,  "'" ],  
22264     [    8217, "'" ],  
22265     [    8220, '"' ],  
22266     [    8221, '"' ],  
22267     [    8226, "*" ],  
22268     [    8230, "..." ]
22269 ]; 
22270
22271     //<script type="text/javascript">
22272
22273 /*
22274  * Ext JS Library 1.1.1
22275  * Copyright(c) 2006-2007, Ext JS, LLC.
22276  * Licence LGPL
22277  * 
22278  */
22279  
22280  
22281 Roo.form.HtmlEditor = function(config){
22282     
22283     
22284     
22285     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22286     
22287     if (!this.toolbars) {
22288         this.toolbars = [];
22289     }
22290     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22291     
22292     
22293 };
22294
22295 /**
22296  * @class Roo.form.HtmlEditor
22297  * @extends Roo.form.Field
22298  * Provides a lightweight HTML Editor component.
22299  *
22300  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22301  * 
22302  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22303  * supported by this editor.</b><br/><br/>
22304  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22305  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22306  */
22307 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22308     /**
22309      * @cfg {Boolean} clearUp
22310      */
22311     clearUp : true,
22312       /**
22313      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22314      */
22315     toolbars : false,
22316    
22317      /**
22318      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22319      *                        Roo.resizable.
22320      */
22321     resizable : false,
22322      /**
22323      * @cfg {Number} height (in pixels)
22324      */   
22325     height: 300,
22326    /**
22327      * @cfg {Number} width (in pixels)
22328      */   
22329     width: 500,
22330     
22331     /**
22332      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22333      * 
22334      */
22335     stylesheets: false,
22336     
22337     
22338      /**
22339      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22340      * 
22341      */
22342     cblack: false,
22343     /**
22344      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22345      * 
22346      */
22347     cwhite: false,
22348     
22349      /**
22350      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22351      * 
22352      */
22353     black: false,
22354     /**
22355      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22356      * 
22357      */
22358     white: false,
22359     /**
22360      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22361      */
22362     allowComments: false,
22363     
22364     // id of frame..
22365     frameId: false,
22366     
22367     // private properties
22368     validationEvent : false,
22369     deferHeight: true,
22370     initialized : false,
22371     activated : false,
22372     
22373     onFocus : Roo.emptyFn,
22374     iframePad:3,
22375     hideMode:'offsets',
22376     
22377     actionMode : 'container', // defaults to hiding it...
22378     
22379     defaultAutoCreate : { // modified by initCompnoent..
22380         tag: "textarea",
22381         style:"width:500px;height:300px;",
22382         autocomplete: "new-password"
22383     },
22384
22385     // private
22386     initComponent : function(){
22387         this.addEvents({
22388             /**
22389              * @event initialize
22390              * Fires when the editor is fully initialized (including the iframe)
22391              * @param {HtmlEditor} this
22392              */
22393             initialize: true,
22394             /**
22395              * @event activate
22396              * Fires when the editor is first receives the focus. Any insertion must wait
22397              * until after this event.
22398              * @param {HtmlEditor} this
22399              */
22400             activate: true,
22401              /**
22402              * @event beforesync
22403              * Fires before the textarea is updated with content from the editor iframe. Return false
22404              * to cancel the sync.
22405              * @param {HtmlEditor} this
22406              * @param {String} html
22407              */
22408             beforesync: true,
22409              /**
22410              * @event beforepush
22411              * Fires before the iframe editor is updated with content from the textarea. Return false
22412              * to cancel the push.
22413              * @param {HtmlEditor} this
22414              * @param {String} html
22415              */
22416             beforepush: true,
22417              /**
22418              * @event sync
22419              * Fires when the textarea is updated with content from the editor iframe.
22420              * @param {HtmlEditor} this
22421              * @param {String} html
22422              */
22423             sync: true,
22424              /**
22425              * @event push
22426              * Fires when the iframe editor is updated with content from the textarea.
22427              * @param {HtmlEditor} this
22428              * @param {String} html
22429              */
22430             push: true,
22431              /**
22432              * @event editmodechange
22433              * Fires when the editor switches edit modes
22434              * @param {HtmlEditor} this
22435              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22436              */
22437             editmodechange: true,
22438             /**
22439              * @event editorevent
22440              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22441              * @param {HtmlEditor} this
22442              */
22443             editorevent: true,
22444             /**
22445              * @event firstfocus
22446              * Fires when on first focus - needed by toolbars..
22447              * @param {HtmlEditor} this
22448              */
22449             firstfocus: true,
22450             /**
22451              * @event autosave
22452              * Auto save the htmlEditor value as a file into Events
22453              * @param {HtmlEditor} this
22454              */
22455             autosave: true,
22456             /**
22457              * @event savedpreview
22458              * preview the saved version of htmlEditor
22459              * @param {HtmlEditor} this
22460              */
22461             savedpreview: true,
22462             
22463             /**
22464             * @event stylesheetsclick
22465             * Fires when press the Sytlesheets button
22466             * @param {Roo.HtmlEditorCore} this
22467             */
22468             stylesheetsclick: true
22469         });
22470         this.defaultAutoCreate =  {
22471             tag: "textarea",
22472             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22473             autocomplete: "new-password"
22474         };
22475     },
22476
22477     /**
22478      * Protected method that will not generally be called directly. It
22479      * is called when the editor creates its toolbar. Override this method if you need to
22480      * add custom toolbar buttons.
22481      * @param {HtmlEditor} editor
22482      */
22483     createToolbar : function(editor){
22484         Roo.log("create toolbars");
22485         if (!editor.toolbars || !editor.toolbars.length) {
22486             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22487         }
22488         
22489         for (var i =0 ; i < editor.toolbars.length;i++) {
22490             editor.toolbars[i] = Roo.factory(
22491                     typeof(editor.toolbars[i]) == 'string' ?
22492                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22493                 Roo.form.HtmlEditor);
22494             editor.toolbars[i].init(editor);
22495         }
22496          
22497         
22498     },
22499
22500      
22501     // private
22502     onRender : function(ct, position)
22503     {
22504         var _t = this;
22505         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22506         
22507         this.wrap = this.el.wrap({
22508             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22509         });
22510         
22511         this.editorcore.onRender(ct, position);
22512          
22513         if (this.resizable) {
22514             this.resizeEl = new Roo.Resizable(this.wrap, {
22515                 pinned : true,
22516                 wrap: true,
22517                 dynamic : true,
22518                 minHeight : this.height,
22519                 height: this.height,
22520                 handles : this.resizable,
22521                 width: this.width,
22522                 listeners : {
22523                     resize : function(r, w, h) {
22524                         _t.onResize(w,h); // -something
22525                     }
22526                 }
22527             });
22528             
22529         }
22530         this.createToolbar(this);
22531        
22532         
22533         if(!this.width){
22534             this.setSize(this.wrap.getSize());
22535         }
22536         if (this.resizeEl) {
22537             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22538             // should trigger onReize..
22539         }
22540         
22541         this.keyNav = new Roo.KeyNav(this.el, {
22542             
22543             "tab" : function(e){
22544                 e.preventDefault();
22545                 
22546                 var value = this.getValue();
22547                 
22548                 var start = this.el.dom.selectionStart;
22549                 var end = this.el.dom.selectionEnd;
22550                 
22551                 if(!e.shiftKey){
22552                     
22553                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22554                     this.el.dom.setSelectionRange(end + 1, end + 1);
22555                     return;
22556                 }
22557                 
22558                 var f = value.substring(0, start).split("\t");
22559                 
22560                 if(f.pop().length != 0){
22561                     return;
22562                 }
22563                 
22564                 this.setValue(f.join("\t") + value.substring(end));
22565                 this.el.dom.setSelectionRange(start - 1, start - 1);
22566                 
22567             },
22568             
22569             "home" : function(e){
22570                 e.preventDefault();
22571                 
22572                 var curr = this.el.dom.selectionStart;
22573                 var lines = this.getValue().split("\n");
22574                 
22575                 if(!lines.length){
22576                     return;
22577                 }
22578                 
22579                 if(e.ctrlKey){
22580                     this.el.dom.setSelectionRange(0, 0);
22581                     return;
22582                 }
22583                 
22584                 var pos = 0;
22585                 
22586                 for (var i = 0; i < lines.length;i++) {
22587                     pos += lines[i].length;
22588                     
22589                     if(i != 0){
22590                         pos += 1;
22591                     }
22592                     
22593                     if(pos < curr){
22594                         continue;
22595                     }
22596                     
22597                     pos -= lines[i].length;
22598                     
22599                     break;
22600                 }
22601                 
22602                 if(!e.shiftKey){
22603                     this.el.dom.setSelectionRange(pos, pos);
22604                     return;
22605                 }
22606                 
22607                 this.el.dom.selectionStart = pos;
22608                 this.el.dom.selectionEnd = curr;
22609             },
22610             
22611             "end" : function(e){
22612                 e.preventDefault();
22613                 
22614                 var curr = this.el.dom.selectionStart;
22615                 var lines = this.getValue().split("\n");
22616                 
22617                 if(!lines.length){
22618                     return;
22619                 }
22620                 
22621                 if(e.ctrlKey){
22622                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22623                     return;
22624                 }
22625                 
22626                 var pos = 0;
22627                 
22628                 for (var i = 0; i < lines.length;i++) {
22629                     
22630                     pos += lines[i].length;
22631                     
22632                     if(i != 0){
22633                         pos += 1;
22634                     }
22635                     
22636                     if(pos < curr){
22637                         continue;
22638                     }
22639                     
22640                     break;
22641                 }
22642                 
22643                 if(!e.shiftKey){
22644                     this.el.dom.setSelectionRange(pos, pos);
22645                     return;
22646                 }
22647                 
22648                 this.el.dom.selectionStart = curr;
22649                 this.el.dom.selectionEnd = pos;
22650             },
22651
22652             scope : this,
22653
22654             doRelay : function(foo, bar, hname){
22655                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22656             },
22657
22658             forceKeyDown: true
22659         });
22660         
22661 //        if(this.autosave && this.w){
22662 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22663 //        }
22664     },
22665
22666     // private
22667     onResize : function(w, h)
22668     {
22669         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22670         var ew = false;
22671         var eh = false;
22672         
22673         if(this.el ){
22674             if(typeof w == 'number'){
22675                 var aw = w - this.wrap.getFrameWidth('lr');
22676                 this.el.setWidth(this.adjustWidth('textarea', aw));
22677                 ew = aw;
22678             }
22679             if(typeof h == 'number'){
22680                 var tbh = 0;
22681                 for (var i =0; i < this.toolbars.length;i++) {
22682                     // fixme - ask toolbars for heights?
22683                     tbh += this.toolbars[i].tb.el.getHeight();
22684                     if (this.toolbars[i].footer) {
22685                         tbh += this.toolbars[i].footer.el.getHeight();
22686                     }
22687                 }
22688                 
22689                 
22690                 
22691                 
22692                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22693                 ah -= 5; // knock a few pixes off for look..
22694 //                Roo.log(ah);
22695                 this.el.setHeight(this.adjustWidth('textarea', ah));
22696                 var eh = ah;
22697             }
22698         }
22699         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22700         this.editorcore.onResize(ew,eh);
22701         
22702     },
22703
22704     /**
22705      * Toggles the editor between standard and source edit mode.
22706      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22707      */
22708     toggleSourceEdit : function(sourceEditMode)
22709     {
22710         this.editorcore.toggleSourceEdit(sourceEditMode);
22711         
22712         if(this.editorcore.sourceEditMode){
22713             Roo.log('editor - showing textarea');
22714             
22715 //            Roo.log('in');
22716 //            Roo.log(this.syncValue());
22717             this.editorcore.syncValue();
22718             this.el.removeClass('x-hidden');
22719             this.el.dom.removeAttribute('tabIndex');
22720             this.el.focus();
22721             
22722             for (var i = 0; i < this.toolbars.length; i++) {
22723                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22724                     this.toolbars[i].tb.hide();
22725                     this.toolbars[i].footer.hide();
22726                 }
22727             }
22728             
22729         }else{
22730             Roo.log('editor - hiding textarea');
22731 //            Roo.log('out')
22732 //            Roo.log(this.pushValue()); 
22733             this.editorcore.pushValue();
22734             
22735             this.el.addClass('x-hidden');
22736             this.el.dom.setAttribute('tabIndex', -1);
22737             
22738             for (var i = 0; i < this.toolbars.length; i++) {
22739                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22740                     this.toolbars[i].tb.show();
22741                     this.toolbars[i].footer.show();
22742                 }
22743             }
22744             
22745             //this.deferFocus();
22746         }
22747         
22748         this.setSize(this.wrap.getSize());
22749         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22750         
22751         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22752     },
22753  
22754     // private (for BoxComponent)
22755     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22756
22757     // private (for BoxComponent)
22758     getResizeEl : function(){
22759         return this.wrap;
22760     },
22761
22762     // private (for BoxComponent)
22763     getPositionEl : function(){
22764         return this.wrap;
22765     },
22766
22767     // private
22768     initEvents : function(){
22769         this.originalValue = this.getValue();
22770     },
22771
22772     /**
22773      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22774      * @method
22775      */
22776     markInvalid : Roo.emptyFn,
22777     /**
22778      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22779      * @method
22780      */
22781     clearInvalid : Roo.emptyFn,
22782
22783     setValue : function(v){
22784         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22785         this.editorcore.pushValue();
22786     },
22787
22788      
22789     // private
22790     deferFocus : function(){
22791         this.focus.defer(10, this);
22792     },
22793
22794     // doc'ed in Field
22795     focus : function(){
22796         this.editorcore.focus();
22797         
22798     },
22799       
22800
22801     // private
22802     onDestroy : function(){
22803         
22804         
22805         
22806         if(this.rendered){
22807             
22808             for (var i =0; i < this.toolbars.length;i++) {
22809                 // fixme - ask toolbars for heights?
22810                 this.toolbars[i].onDestroy();
22811             }
22812             
22813             this.wrap.dom.innerHTML = '';
22814             this.wrap.remove();
22815         }
22816     },
22817
22818     // private
22819     onFirstFocus : function(){
22820         //Roo.log("onFirstFocus");
22821         this.editorcore.onFirstFocus();
22822          for (var i =0; i < this.toolbars.length;i++) {
22823             this.toolbars[i].onFirstFocus();
22824         }
22825         
22826     },
22827     
22828     // private
22829     syncValue : function()
22830     {
22831         this.editorcore.syncValue();
22832     },
22833     
22834     pushValue : function()
22835     {
22836         this.editorcore.pushValue();
22837     },
22838     
22839     setStylesheets : function(stylesheets)
22840     {
22841         this.editorcore.setStylesheets(stylesheets);
22842     },
22843     
22844     removeStylesheets : function()
22845     {
22846         this.editorcore.removeStylesheets();
22847     }
22848      
22849     
22850     // hide stuff that is not compatible
22851     /**
22852      * @event blur
22853      * @hide
22854      */
22855     /**
22856      * @event change
22857      * @hide
22858      */
22859     /**
22860      * @event focus
22861      * @hide
22862      */
22863     /**
22864      * @event specialkey
22865      * @hide
22866      */
22867     /**
22868      * @cfg {String} fieldClass @hide
22869      */
22870     /**
22871      * @cfg {String} focusClass @hide
22872      */
22873     /**
22874      * @cfg {String} autoCreate @hide
22875      */
22876     /**
22877      * @cfg {String} inputType @hide
22878      */
22879     /**
22880      * @cfg {String} invalidClass @hide
22881      */
22882     /**
22883      * @cfg {String} invalidText @hide
22884      */
22885     /**
22886      * @cfg {String} msgFx @hide
22887      */
22888     /**
22889      * @cfg {String} validateOnBlur @hide
22890      */
22891 });
22892  
22893     // <script type="text/javascript">
22894 /*
22895  * Based on
22896  * Ext JS Library 1.1.1
22897  * Copyright(c) 2006-2007, Ext JS, LLC.
22898  *  
22899  
22900  */
22901
22902 /**
22903  * @class Roo.form.HtmlEditorToolbar1
22904  * Basic Toolbar
22905  * 
22906  * Usage:
22907  *
22908  new Roo.form.HtmlEditor({
22909     ....
22910     toolbars : [
22911         new Roo.form.HtmlEditorToolbar1({
22912             disable : { fonts: 1 , format: 1, ..., ... , ...],
22913             btns : [ .... ]
22914         })
22915     }
22916      
22917  * 
22918  * @cfg {Object} disable List of elements to disable..
22919  * @cfg {Array} btns List of additional buttons.
22920  * 
22921  * 
22922  * NEEDS Extra CSS? 
22923  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22924  */
22925  
22926 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22927 {
22928     
22929     Roo.apply(this, config);
22930     
22931     // default disabled, based on 'good practice'..
22932     this.disable = this.disable || {};
22933     Roo.applyIf(this.disable, {
22934         fontSize : true,
22935         colors : true,
22936         specialElements : true
22937     });
22938     
22939     
22940     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22941     // dont call parent... till later.
22942 }
22943
22944 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22945     
22946     tb: false,
22947     
22948     rendered: false,
22949     
22950     editor : false,
22951     editorcore : false,
22952     /**
22953      * @cfg {Object} disable  List of toolbar elements to disable
22954          
22955      */
22956     disable : false,
22957     
22958     
22959      /**
22960      * @cfg {String} createLinkText The default text for the create link prompt
22961      */
22962     createLinkText : 'Please enter the URL for the link:',
22963     /**
22964      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22965      */
22966     defaultLinkValue : 'http:/'+'/',
22967    
22968     
22969       /**
22970      * @cfg {Array} fontFamilies An array of available font families
22971      */
22972     fontFamilies : [
22973         'Arial',
22974         'Courier New',
22975         'Tahoma',
22976         'Times New Roman',
22977         'Verdana'
22978     ],
22979     
22980     specialChars : [
22981            "&#169;",
22982           "&#174;",     
22983           "&#8482;",    
22984           "&#163;" ,    
22985          // "&#8212;",    
22986           "&#8230;",    
22987           "&#247;" ,    
22988         //  "&#225;" ,     ?? a acute?
22989            "&#8364;"    , //Euro
22990        //   "&#8220;"    ,
22991         //  "&#8221;"    ,
22992         //  "&#8226;"    ,
22993           "&#176;"  //   , // degrees
22994
22995          // "&#233;"     , // e ecute
22996          // "&#250;"     , // u ecute?
22997     ],
22998     
22999     specialElements : [
23000         {
23001             text: "Insert Table",
23002             xtype: 'MenuItem',
23003             xns : Roo.Menu,
23004             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23005                 
23006         },
23007         {    
23008             text: "Insert Image",
23009             xtype: 'MenuItem',
23010             xns : Roo.Menu,
23011             ihtml : '<img src="about:blank"/>'
23012             
23013         }
23014         
23015          
23016     ],
23017     
23018     
23019     inputElements : [ 
23020             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23021             "input:submit", "input:button", "select", "textarea", "label" ],
23022     formats : [
23023         ["p"] ,  
23024         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23025         ["pre"],[ "code"], 
23026         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23027         ['div'],['span'],
23028         ['sup'],['sub']
23029     ],
23030     
23031     cleanStyles : [
23032         "font-size"
23033     ],
23034      /**
23035      * @cfg {String} defaultFont default font to use.
23036      */
23037     defaultFont: 'tahoma',
23038    
23039     fontSelect : false,
23040     
23041     
23042     formatCombo : false,
23043     
23044     init : function(editor)
23045     {
23046         this.editor = editor;
23047         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23048         var editorcore = this.editorcore;
23049         
23050         var _t = this;
23051         
23052         var fid = editorcore.frameId;
23053         var etb = this;
23054         function btn(id, toggle, handler){
23055             var xid = fid + '-'+ id ;
23056             return {
23057                 id : xid,
23058                 cmd : id,
23059                 cls : 'x-btn-icon x-edit-'+id,
23060                 enableToggle:toggle !== false,
23061                 scope: _t, // was editor...
23062                 handler:handler||_t.relayBtnCmd,
23063                 clickEvent:'mousedown',
23064                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23065                 tabIndex:-1
23066             };
23067         }
23068         
23069         
23070         
23071         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23072         this.tb = tb;
23073          // stop form submits
23074         tb.el.on('click', function(e){
23075             e.preventDefault(); // what does this do?
23076         });
23077
23078         if(!this.disable.font) { // && !Roo.isSafari){
23079             /* why no safari for fonts 
23080             editor.fontSelect = tb.el.createChild({
23081                 tag:'select',
23082                 tabIndex: -1,
23083                 cls:'x-font-select',
23084                 html: this.createFontOptions()
23085             });
23086             
23087             editor.fontSelect.on('change', function(){
23088                 var font = editor.fontSelect.dom.value;
23089                 editor.relayCmd('fontname', font);
23090                 editor.deferFocus();
23091             }, editor);
23092             
23093             tb.add(
23094                 editor.fontSelect.dom,
23095                 '-'
23096             );
23097             */
23098             
23099         };
23100         if(!this.disable.formats){
23101             this.formatCombo = new Roo.form.ComboBox({
23102                 store: new Roo.data.SimpleStore({
23103                     id : 'tag',
23104                     fields: ['tag'],
23105                     data : this.formats // from states.js
23106                 }),
23107                 blockFocus : true,
23108                 name : '',
23109                 //autoCreate : {tag: "div",  size: "20"},
23110                 displayField:'tag',
23111                 typeAhead: false,
23112                 mode: 'local',
23113                 editable : false,
23114                 triggerAction: 'all',
23115                 emptyText:'Add tag',
23116                 selectOnFocus:true,
23117                 width:135,
23118                 listeners : {
23119                     'select': function(c, r, i) {
23120                         editorcore.insertTag(r.get('tag'));
23121                         editor.focus();
23122                     }
23123                 }
23124
23125             });
23126             tb.addField(this.formatCombo);
23127             
23128         }
23129         
23130         if(!this.disable.format){
23131             tb.add(
23132                 btn('bold'),
23133                 btn('italic'),
23134                 btn('underline'),
23135                 btn('strikethrough')
23136             );
23137         };
23138         if(!this.disable.fontSize){
23139             tb.add(
23140                 '-',
23141                 
23142                 
23143                 btn('increasefontsize', false, editorcore.adjustFont),
23144                 btn('decreasefontsize', false, editorcore.adjustFont)
23145             );
23146         };
23147         
23148         
23149         if(!this.disable.colors){
23150             tb.add(
23151                 '-', {
23152                     id:editorcore.frameId +'-forecolor',
23153                     cls:'x-btn-icon x-edit-forecolor',
23154                     clickEvent:'mousedown',
23155                     tooltip: this.buttonTips['forecolor'] || undefined,
23156                     tabIndex:-1,
23157                     menu : new Roo.menu.ColorMenu({
23158                         allowReselect: true,
23159                         focus: Roo.emptyFn,
23160                         value:'000000',
23161                         plain:true,
23162                         selectHandler: function(cp, color){
23163                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23164                             editor.deferFocus();
23165                         },
23166                         scope: editorcore,
23167                         clickEvent:'mousedown'
23168                     })
23169                 }, {
23170                     id:editorcore.frameId +'backcolor',
23171                     cls:'x-btn-icon x-edit-backcolor',
23172                     clickEvent:'mousedown',
23173                     tooltip: this.buttonTips['backcolor'] || undefined,
23174                     tabIndex:-1,
23175                     menu : new Roo.menu.ColorMenu({
23176                         focus: Roo.emptyFn,
23177                         value:'FFFFFF',
23178                         plain:true,
23179                         allowReselect: true,
23180                         selectHandler: function(cp, color){
23181                             if(Roo.isGecko){
23182                                 editorcore.execCmd('useCSS', false);
23183                                 editorcore.execCmd('hilitecolor', color);
23184                                 editorcore.execCmd('useCSS', true);
23185                                 editor.deferFocus();
23186                             }else{
23187                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23188                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23189                                 editor.deferFocus();
23190                             }
23191                         },
23192                         scope:editorcore,
23193                         clickEvent:'mousedown'
23194                     })
23195                 }
23196             );
23197         };
23198         // now add all the items...
23199         
23200
23201         if(!this.disable.alignments){
23202             tb.add(
23203                 '-',
23204                 btn('justifyleft'),
23205                 btn('justifycenter'),
23206                 btn('justifyright')
23207             );
23208         };
23209
23210         //if(!Roo.isSafari){
23211             if(!this.disable.links){
23212                 tb.add(
23213                     '-',
23214                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23215                 );
23216             };
23217
23218             if(!this.disable.lists){
23219                 tb.add(
23220                     '-',
23221                     btn('insertorderedlist'),
23222                     btn('insertunorderedlist')
23223                 );
23224             }
23225             if(!this.disable.sourceEdit){
23226                 tb.add(
23227                     '-',
23228                     btn('sourceedit', true, function(btn){
23229                         this.toggleSourceEdit(btn.pressed);
23230                     })
23231                 );
23232             }
23233         //}
23234         
23235         var smenu = { };
23236         // special menu.. - needs to be tidied up..
23237         if (!this.disable.special) {
23238             smenu = {
23239                 text: "&#169;",
23240                 cls: 'x-edit-none',
23241                 
23242                 menu : {
23243                     items : []
23244                 }
23245             };
23246             for (var i =0; i < this.specialChars.length; i++) {
23247                 smenu.menu.items.push({
23248                     
23249                     html: this.specialChars[i],
23250                     handler: function(a,b) {
23251                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23252                         //editor.insertAtCursor(a.html);
23253                         
23254                     },
23255                     tabIndex:-1
23256                 });
23257             }
23258             
23259             
23260             tb.add(smenu);
23261             
23262             
23263         }
23264         
23265         var cmenu = { };
23266         if (!this.disable.cleanStyles) {
23267             cmenu = {
23268                 cls: 'x-btn-icon x-btn-clear',
23269                 
23270                 menu : {
23271                     items : []
23272                 }
23273             };
23274             for (var i =0; i < this.cleanStyles.length; i++) {
23275                 cmenu.menu.items.push({
23276                     actiontype : this.cleanStyles[i],
23277                     html: 'Remove ' + this.cleanStyles[i],
23278                     handler: function(a,b) {
23279 //                        Roo.log(a);
23280 //                        Roo.log(b);
23281                         var c = Roo.get(editorcore.doc.body);
23282                         c.select('[style]').each(function(s) {
23283                             s.dom.style.removeProperty(a.actiontype);
23284                         });
23285                         editorcore.syncValue();
23286                     },
23287                     tabIndex:-1
23288                 });
23289             }
23290              cmenu.menu.items.push({
23291                 actiontype : 'tablewidths',
23292                 html: 'Remove Table Widths',
23293                 handler: function(a,b) {
23294                     editorcore.cleanTableWidths();
23295                     editorcore.syncValue();
23296                 },
23297                 tabIndex:-1
23298             });
23299             cmenu.menu.items.push({
23300                 actiontype : 'word',
23301                 html: 'Remove MS Word Formating',
23302                 handler: function(a,b) {
23303                     editorcore.cleanWord();
23304                     editorcore.syncValue();
23305                 },
23306                 tabIndex:-1
23307             });
23308             
23309             cmenu.menu.items.push({
23310                 actiontype : 'all',
23311                 html: 'Remove All Styles',
23312                 handler: function(a,b) {
23313                     
23314                     var c = Roo.get(editorcore.doc.body);
23315                     c.select('[style]').each(function(s) {
23316                         s.dom.removeAttribute('style');
23317                     });
23318                     editorcore.syncValue();
23319                 },
23320                 tabIndex:-1
23321             });
23322             
23323             cmenu.menu.items.push({
23324                 actiontype : 'all',
23325                 html: 'Remove All CSS Classes',
23326                 handler: function(a,b) {
23327                     
23328                     var c = Roo.get(editorcore.doc.body);
23329                     c.select('[class]').each(function(s) {
23330                         s.dom.removeAttribute('class');
23331                     });
23332                     editorcore.cleanWord();
23333                     editorcore.syncValue();
23334                 },
23335                 tabIndex:-1
23336             });
23337             
23338              cmenu.menu.items.push({
23339                 actiontype : 'tidy',
23340                 html: 'Tidy HTML Source',
23341                 handler: function(a,b) {
23342                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23343                     editorcore.syncValue();
23344                 },
23345                 tabIndex:-1
23346             });
23347             
23348             
23349             tb.add(cmenu);
23350         }
23351          
23352         if (!this.disable.specialElements) {
23353             var semenu = {
23354                 text: "Other;",
23355                 cls: 'x-edit-none',
23356                 menu : {
23357                     items : []
23358                 }
23359             };
23360             for (var i =0; i < this.specialElements.length; i++) {
23361                 semenu.menu.items.push(
23362                     Roo.apply({ 
23363                         handler: function(a,b) {
23364                             editor.insertAtCursor(this.ihtml);
23365                         }
23366                     }, this.specialElements[i])
23367                 );
23368                     
23369             }
23370             
23371             tb.add(semenu);
23372             
23373             
23374         }
23375          
23376         
23377         if (this.btns) {
23378             for(var i =0; i< this.btns.length;i++) {
23379                 var b = Roo.factory(this.btns[i],Roo.form);
23380                 b.cls =  'x-edit-none';
23381                 
23382                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23383                     b.cls += ' x-init-enable';
23384                 }
23385                 
23386                 b.scope = editorcore;
23387                 tb.add(b);
23388             }
23389         
23390         }
23391         
23392         
23393         
23394         // disable everything...
23395         
23396         this.tb.items.each(function(item){
23397             
23398            if(
23399                 item.id != editorcore.frameId+ '-sourceedit' && 
23400                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23401             ){
23402                 
23403                 item.disable();
23404             }
23405         });
23406         this.rendered = true;
23407         
23408         // the all the btns;
23409         editor.on('editorevent', this.updateToolbar, this);
23410         // other toolbars need to implement this..
23411         //editor.on('editmodechange', this.updateToolbar, this);
23412     },
23413     
23414     
23415     relayBtnCmd : function(btn) {
23416         this.editorcore.relayCmd(btn.cmd);
23417     },
23418     // private used internally
23419     createLink : function(){
23420         Roo.log("create link?");
23421         var url = prompt(this.createLinkText, this.defaultLinkValue);
23422         if(url && url != 'http:/'+'/'){
23423             this.editorcore.relayCmd('createlink', url);
23424         }
23425     },
23426
23427     
23428     /**
23429      * Protected method that will not generally be called directly. It triggers
23430      * a toolbar update by reading the markup state of the current selection in the editor.
23431      */
23432     updateToolbar: function(){
23433
23434         if(!this.editorcore.activated){
23435             this.editor.onFirstFocus();
23436             return;
23437         }
23438
23439         var btns = this.tb.items.map, 
23440             doc = this.editorcore.doc,
23441             frameId = this.editorcore.frameId;
23442
23443         if(!this.disable.font && !Roo.isSafari){
23444             /*
23445             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23446             if(name != this.fontSelect.dom.value){
23447                 this.fontSelect.dom.value = name;
23448             }
23449             */
23450         }
23451         if(!this.disable.format){
23452             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23453             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23454             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23455             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23456         }
23457         if(!this.disable.alignments){
23458             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23459             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23460             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23461         }
23462         if(!Roo.isSafari && !this.disable.lists){
23463             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23464             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23465         }
23466         
23467         var ans = this.editorcore.getAllAncestors();
23468         if (this.formatCombo) {
23469             
23470             
23471             var store = this.formatCombo.store;
23472             this.formatCombo.setValue("");
23473             for (var i =0; i < ans.length;i++) {
23474                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23475                     // select it..
23476                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23477                     break;
23478                 }
23479             }
23480         }
23481         
23482         
23483         
23484         // hides menus... - so this cant be on a menu...
23485         Roo.menu.MenuMgr.hideAll();
23486
23487         //this.editorsyncValue();
23488     },
23489    
23490     
23491     createFontOptions : function(){
23492         var buf = [], fs = this.fontFamilies, ff, lc;
23493         
23494         
23495         
23496         for(var i = 0, len = fs.length; i< len; i++){
23497             ff = fs[i];
23498             lc = ff.toLowerCase();
23499             buf.push(
23500                 '<option value="',lc,'" style="font-family:',ff,';"',
23501                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23502                     ff,
23503                 '</option>'
23504             );
23505         }
23506         return buf.join('');
23507     },
23508     
23509     toggleSourceEdit : function(sourceEditMode){
23510         
23511         Roo.log("toolbar toogle");
23512         if(sourceEditMode === undefined){
23513             sourceEditMode = !this.sourceEditMode;
23514         }
23515         this.sourceEditMode = sourceEditMode === true;
23516         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23517         // just toggle the button?
23518         if(btn.pressed !== this.sourceEditMode){
23519             btn.toggle(this.sourceEditMode);
23520             return;
23521         }
23522         
23523         if(sourceEditMode){
23524             Roo.log("disabling buttons");
23525             this.tb.items.each(function(item){
23526                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23527                     item.disable();
23528                 }
23529             });
23530           
23531         }else{
23532             Roo.log("enabling buttons");
23533             if(this.editorcore.initialized){
23534                 this.tb.items.each(function(item){
23535                     item.enable();
23536                 });
23537             }
23538             
23539         }
23540         Roo.log("calling toggole on editor");
23541         // tell the editor that it's been pressed..
23542         this.editor.toggleSourceEdit(sourceEditMode);
23543        
23544     },
23545      /**
23546      * Object collection of toolbar tooltips for the buttons in the editor. The key
23547      * is the command id associated with that button and the value is a valid QuickTips object.
23548      * For example:
23549 <pre><code>
23550 {
23551     bold : {
23552         title: 'Bold (Ctrl+B)',
23553         text: 'Make the selected text bold.',
23554         cls: 'x-html-editor-tip'
23555     },
23556     italic : {
23557         title: 'Italic (Ctrl+I)',
23558         text: 'Make the selected text italic.',
23559         cls: 'x-html-editor-tip'
23560     },
23561     ...
23562 </code></pre>
23563     * @type Object
23564      */
23565     buttonTips : {
23566         bold : {
23567             title: 'Bold (Ctrl+B)',
23568             text: 'Make the selected text bold.',
23569             cls: 'x-html-editor-tip'
23570         },
23571         italic : {
23572             title: 'Italic (Ctrl+I)',
23573             text: 'Make the selected text italic.',
23574             cls: 'x-html-editor-tip'
23575         },
23576         underline : {
23577             title: 'Underline (Ctrl+U)',
23578             text: 'Underline the selected text.',
23579             cls: 'x-html-editor-tip'
23580         },
23581         strikethrough : {
23582             title: 'Strikethrough',
23583             text: 'Strikethrough the selected text.',
23584             cls: 'x-html-editor-tip'
23585         },
23586         increasefontsize : {
23587             title: 'Grow Text',
23588             text: 'Increase the font size.',
23589             cls: 'x-html-editor-tip'
23590         },
23591         decreasefontsize : {
23592             title: 'Shrink Text',
23593             text: 'Decrease the font size.',
23594             cls: 'x-html-editor-tip'
23595         },
23596         backcolor : {
23597             title: 'Text Highlight Color',
23598             text: 'Change the background color of the selected text.',
23599             cls: 'x-html-editor-tip'
23600         },
23601         forecolor : {
23602             title: 'Font Color',
23603             text: 'Change the color of the selected text.',
23604             cls: 'x-html-editor-tip'
23605         },
23606         justifyleft : {
23607             title: 'Align Text Left',
23608             text: 'Align text to the left.',
23609             cls: 'x-html-editor-tip'
23610         },
23611         justifycenter : {
23612             title: 'Center Text',
23613             text: 'Center text in the editor.',
23614             cls: 'x-html-editor-tip'
23615         },
23616         justifyright : {
23617             title: 'Align Text Right',
23618             text: 'Align text to the right.',
23619             cls: 'x-html-editor-tip'
23620         },
23621         insertunorderedlist : {
23622             title: 'Bullet List',
23623             text: 'Start a bulleted list.',
23624             cls: 'x-html-editor-tip'
23625         },
23626         insertorderedlist : {
23627             title: 'Numbered List',
23628             text: 'Start a numbered list.',
23629             cls: 'x-html-editor-tip'
23630         },
23631         createlink : {
23632             title: 'Hyperlink',
23633             text: 'Make the selected text a hyperlink.',
23634             cls: 'x-html-editor-tip'
23635         },
23636         sourceedit : {
23637             title: 'Source Edit',
23638             text: 'Switch to source editing mode.',
23639             cls: 'x-html-editor-tip'
23640         }
23641     },
23642     // private
23643     onDestroy : function(){
23644         if(this.rendered){
23645             
23646             this.tb.items.each(function(item){
23647                 if(item.menu){
23648                     item.menu.removeAll();
23649                     if(item.menu.el){
23650                         item.menu.el.destroy();
23651                     }
23652                 }
23653                 item.destroy();
23654             });
23655              
23656         }
23657     },
23658     onFirstFocus: function() {
23659         this.tb.items.each(function(item){
23660            item.enable();
23661         });
23662     }
23663 });
23664
23665
23666
23667
23668 // <script type="text/javascript">
23669 /*
23670  * Based on
23671  * Ext JS Library 1.1.1
23672  * Copyright(c) 2006-2007, Ext JS, LLC.
23673  *  
23674  
23675  */
23676
23677  
23678 /**
23679  * @class Roo.form.HtmlEditor.ToolbarContext
23680  * Context Toolbar
23681  * 
23682  * Usage:
23683  *
23684  new Roo.form.HtmlEditor({
23685     ....
23686     toolbars : [
23687         { xtype: 'ToolbarStandard', styles : {} }
23688         { xtype: 'ToolbarContext', disable : {} }
23689     ]
23690 })
23691
23692      
23693  * 
23694  * @config : {Object} disable List of elements to disable.. (not done yet.)
23695  * @config : {Object} styles  Map of styles available.
23696  * 
23697  */
23698
23699 Roo.form.HtmlEditor.ToolbarContext = function(config)
23700 {
23701     
23702     Roo.apply(this, config);
23703     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23704     // dont call parent... till later.
23705     this.styles = this.styles || {};
23706 }
23707
23708  
23709
23710 Roo.form.HtmlEditor.ToolbarContext.types = {
23711     'IMG' : {
23712         width : {
23713             title: "Width",
23714             width: 40
23715         },
23716         height:  {
23717             title: "Height",
23718             width: 40
23719         },
23720         align: {
23721             title: "Align",
23722             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23723             width : 80
23724             
23725         },
23726         border: {
23727             title: "Border",
23728             width: 40
23729         },
23730         alt: {
23731             title: "Alt",
23732             width: 120
23733         },
23734         src : {
23735             title: "Src",
23736             width: 220
23737         }
23738         
23739     },
23740     'A' : {
23741         name : {
23742             title: "Name",
23743             width: 50
23744         },
23745         target:  {
23746             title: "Target",
23747             width: 120
23748         },
23749         href:  {
23750             title: "Href",
23751             width: 220
23752         } // border?
23753         
23754     },
23755     'TABLE' : {
23756         rows : {
23757             title: "Rows",
23758             width: 20
23759         },
23760         cols : {
23761             title: "Cols",
23762             width: 20
23763         },
23764         width : {
23765             title: "Width",
23766             width: 40
23767         },
23768         height : {
23769             title: "Height",
23770             width: 40
23771         },
23772         border : {
23773             title: "Border",
23774             width: 20
23775         }
23776     },
23777     'TD' : {
23778         width : {
23779             title: "Width",
23780             width: 40
23781         },
23782         height : {
23783             title: "Height",
23784             width: 40
23785         },   
23786         align: {
23787             title: "Align",
23788             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23789             width: 80
23790         },
23791         valign: {
23792             title: "Valign",
23793             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23794             width: 80
23795         },
23796         colspan: {
23797             title: "Colspan",
23798             width: 20
23799             
23800         },
23801          'font-family'  : {
23802             title : "Font",
23803             style : 'fontFamily',
23804             displayField: 'display',
23805             optname : 'font-family',
23806             width: 140
23807         }
23808     },
23809     'INPUT' : {
23810         name : {
23811             title: "name",
23812             width: 120
23813         },
23814         value : {
23815             title: "Value",
23816             width: 120
23817         },
23818         width : {
23819             title: "Width",
23820             width: 40
23821         }
23822     },
23823     'LABEL' : {
23824         'for' : {
23825             title: "For",
23826             width: 120
23827         }
23828     },
23829     'TEXTAREA' : {
23830           name : {
23831             title: "name",
23832             width: 120
23833         },
23834         rows : {
23835             title: "Rows",
23836             width: 20
23837         },
23838         cols : {
23839             title: "Cols",
23840             width: 20
23841         }
23842     },
23843     'SELECT' : {
23844         name : {
23845             title: "name",
23846             width: 120
23847         },
23848         selectoptions : {
23849             title: "Options",
23850             width: 200
23851         }
23852     },
23853     
23854     // should we really allow this??
23855     // should this just be 
23856     'BODY' : {
23857         title : {
23858             title: "Title",
23859             width: 200,
23860             disabled : true
23861         }
23862     },
23863     'SPAN' : {
23864         'font-family'  : {
23865             title : "Font",
23866             style : 'fontFamily',
23867             displayField: 'display',
23868             optname : 'font-family',
23869             width: 140
23870         }
23871     },
23872     'DIV' : {
23873         'font-family'  : {
23874             title : "Font",
23875             style : 'fontFamily',
23876             displayField: 'display',
23877             optname : 'font-family',
23878             width: 140
23879         }
23880     },
23881      'P' : {
23882         'font-family'  : {
23883             title : "Font",
23884             style : 'fontFamily',
23885             displayField: 'display',
23886             optname : 'font-family',
23887             width: 140
23888         }
23889     },
23890     
23891     '*' : {
23892         // empty..
23893     }
23894
23895 };
23896
23897 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23898 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23899
23900 Roo.form.HtmlEditor.ToolbarContext.options = {
23901         'font-family'  : [ 
23902                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23903                 [ 'Courier New', 'Courier New'],
23904                 [ 'Tahoma', 'Tahoma'],
23905                 [ 'Times New Roman,serif', 'Times'],
23906                 [ 'Verdana','Verdana' ]
23907         ]
23908 };
23909
23910 // fixme - these need to be configurable..
23911  
23912
23913 //Roo.form.HtmlEditor.ToolbarContext.types
23914
23915
23916 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23917     
23918     tb: false,
23919     
23920     rendered: false,
23921     
23922     editor : false,
23923     editorcore : false,
23924     /**
23925      * @cfg {Object} disable  List of toolbar elements to disable
23926          
23927      */
23928     disable : false,
23929     /**
23930      * @cfg {Object} styles List of styles 
23931      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23932      *
23933      * These must be defined in the page, so they get rendered correctly..
23934      * .headline { }
23935      * TD.underline { }
23936      * 
23937      */
23938     styles : false,
23939     
23940     options: false,
23941     
23942     toolbars : false,
23943     
23944     init : function(editor)
23945     {
23946         this.editor = editor;
23947         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23948         var editorcore = this.editorcore;
23949         
23950         var fid = editorcore.frameId;
23951         var etb = this;
23952         function btn(id, toggle, handler){
23953             var xid = fid + '-'+ id ;
23954             return {
23955                 id : xid,
23956                 cmd : id,
23957                 cls : 'x-btn-icon x-edit-'+id,
23958                 enableToggle:toggle !== false,
23959                 scope: editorcore, // was editor...
23960                 handler:handler||editorcore.relayBtnCmd,
23961                 clickEvent:'mousedown',
23962                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23963                 tabIndex:-1
23964             };
23965         }
23966         // create a new element.
23967         var wdiv = editor.wrap.createChild({
23968                 tag: 'div'
23969             }, editor.wrap.dom.firstChild.nextSibling, true);
23970         
23971         // can we do this more than once??
23972         
23973          // stop form submits
23974       
23975  
23976         // disable everything...
23977         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23978         this.toolbars = {};
23979            
23980         for (var i in  ty) {
23981           
23982             this.toolbars[i] = this.buildToolbar(ty[i],i);
23983         }
23984         this.tb = this.toolbars.BODY;
23985         this.tb.el.show();
23986         this.buildFooter();
23987         this.footer.show();
23988         editor.on('hide', function( ) { this.footer.hide() }, this);
23989         editor.on('show', function( ) { this.footer.show() }, this);
23990         
23991          
23992         this.rendered = true;
23993         
23994         // the all the btns;
23995         editor.on('editorevent', this.updateToolbar, this);
23996         // other toolbars need to implement this..
23997         //editor.on('editmodechange', this.updateToolbar, this);
23998     },
23999     
24000     
24001     
24002     /**
24003      * Protected method that will not generally be called directly. It triggers
24004      * a toolbar update by reading the markup state of the current selection in the editor.
24005      *
24006      * Note you can force an update by calling on('editorevent', scope, false)
24007      */
24008     updateToolbar: function(editor,ev,sel){
24009
24010         //Roo.log(ev);
24011         // capture mouse up - this is handy for selecting images..
24012         // perhaps should go somewhere else...
24013         if(!this.editorcore.activated){
24014              this.editor.onFirstFocus();
24015             return;
24016         }
24017         
24018         
24019         
24020         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24021         // selectNode - might want to handle IE?
24022         if (ev &&
24023             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24024             ev.target && ev.target.tagName == 'IMG') {
24025             // they have click on an image...
24026             // let's see if we can change the selection...
24027             sel = ev.target;
24028          
24029               var nodeRange = sel.ownerDocument.createRange();
24030             try {
24031                 nodeRange.selectNode(sel);
24032             } catch (e) {
24033                 nodeRange.selectNodeContents(sel);
24034             }
24035             //nodeRange.collapse(true);
24036             var s = this.editorcore.win.getSelection();
24037             s.removeAllRanges();
24038             s.addRange(nodeRange);
24039         }  
24040         
24041       
24042         var updateFooter = sel ? false : true;
24043         
24044         
24045         var ans = this.editorcore.getAllAncestors();
24046         
24047         // pick
24048         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24049         
24050         if (!sel) { 
24051             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24052             sel = sel ? sel : this.editorcore.doc.body;
24053             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24054             
24055         }
24056         // pick a menu that exists..
24057         var tn = sel.tagName.toUpperCase();
24058         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24059         
24060         tn = sel.tagName.toUpperCase();
24061         
24062         var lastSel = this.tb.selectedNode;
24063         
24064         this.tb.selectedNode = sel;
24065         
24066         // if current menu does not match..
24067         
24068         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24069                 
24070             this.tb.el.hide();
24071             ///console.log("show: " + tn);
24072             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24073             this.tb.el.show();
24074             // update name
24075             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24076             
24077             
24078             // update attributes
24079             if (this.tb.fields) {
24080                 this.tb.fields.each(function(e) {
24081                     if (e.stylename) {
24082                         e.setValue(sel.style[e.stylename]);
24083                         return;
24084                     } 
24085                    e.setValue(sel.getAttribute(e.attrname));
24086                 });
24087             }
24088             
24089             var hasStyles = false;
24090             for(var i in this.styles) {
24091                 hasStyles = true;
24092                 break;
24093             }
24094             
24095             // update styles
24096             if (hasStyles) { 
24097                 var st = this.tb.fields.item(0);
24098                 
24099                 st.store.removeAll();
24100                
24101                 
24102                 var cn = sel.className.split(/\s+/);
24103                 
24104                 var avs = [];
24105                 if (this.styles['*']) {
24106                     
24107                     Roo.each(this.styles['*'], function(v) {
24108                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24109                     });
24110                 }
24111                 if (this.styles[tn]) { 
24112                     Roo.each(this.styles[tn], function(v) {
24113                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24114                     });
24115                 }
24116                 
24117                 st.store.loadData(avs);
24118                 st.collapse();
24119                 st.setValue(cn);
24120             }
24121             // flag our selected Node.
24122             this.tb.selectedNode = sel;
24123            
24124            
24125             Roo.menu.MenuMgr.hideAll();
24126
24127         }
24128         
24129         if (!updateFooter) {
24130             //this.footDisp.dom.innerHTML = ''; 
24131             return;
24132         }
24133         // update the footer
24134         //
24135         var html = '';
24136         
24137         this.footerEls = ans.reverse();
24138         Roo.each(this.footerEls, function(a,i) {
24139             if (!a) { return; }
24140             html += html.length ? ' &gt; '  :  '';
24141             
24142             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24143             
24144         });
24145        
24146         // 
24147         var sz = this.footDisp.up('td').getSize();
24148         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24149         this.footDisp.dom.style.marginLeft = '5px';
24150         
24151         this.footDisp.dom.style.overflow = 'hidden';
24152         
24153         this.footDisp.dom.innerHTML = html;
24154             
24155         //this.editorsyncValue();
24156     },
24157      
24158     
24159    
24160        
24161     // private
24162     onDestroy : function(){
24163         if(this.rendered){
24164             
24165             this.tb.items.each(function(item){
24166                 if(item.menu){
24167                     item.menu.removeAll();
24168                     if(item.menu.el){
24169                         item.menu.el.destroy();
24170                     }
24171                 }
24172                 item.destroy();
24173             });
24174              
24175         }
24176     },
24177     onFirstFocus: function() {
24178         // need to do this for all the toolbars..
24179         this.tb.items.each(function(item){
24180            item.enable();
24181         });
24182     },
24183     buildToolbar: function(tlist, nm)
24184     {
24185         var editor = this.editor;
24186         var editorcore = this.editorcore;
24187          // create a new element.
24188         var wdiv = editor.wrap.createChild({
24189                 tag: 'div'
24190             }, editor.wrap.dom.firstChild.nextSibling, true);
24191         
24192        
24193         var tb = new Roo.Toolbar(wdiv);
24194         // add the name..
24195         
24196         tb.add(nm+ ":&nbsp;");
24197         
24198         var styles = [];
24199         for(var i in this.styles) {
24200             styles.push(i);
24201         }
24202         
24203         // styles...
24204         if (styles && styles.length) {
24205             
24206             // this needs a multi-select checkbox...
24207             tb.addField( new Roo.form.ComboBox({
24208                 store: new Roo.data.SimpleStore({
24209                     id : 'val',
24210                     fields: ['val', 'selected'],
24211                     data : [] 
24212                 }),
24213                 name : '-roo-edit-className',
24214                 attrname : 'className',
24215                 displayField: 'val',
24216                 typeAhead: false,
24217                 mode: 'local',
24218                 editable : false,
24219                 triggerAction: 'all',
24220                 emptyText:'Select Style',
24221                 selectOnFocus:true,
24222                 width: 130,
24223                 listeners : {
24224                     'select': function(c, r, i) {
24225                         // initial support only for on class per el..
24226                         tb.selectedNode.className =  r ? r.get('val') : '';
24227                         editorcore.syncValue();
24228                     }
24229                 }
24230     
24231             }));
24232         }
24233         
24234         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24235         var tbops = tbc.options;
24236         
24237         for (var i in tlist) {
24238             
24239             var item = tlist[i];
24240             tb.add(item.title + ":&nbsp;");
24241             
24242             
24243             //optname == used so you can configure the options available..
24244             var opts = item.opts ? item.opts : false;
24245             if (item.optname) {
24246                 opts = tbops[item.optname];
24247            
24248             }
24249             
24250             if (opts) {
24251                 // opts == pulldown..
24252                 tb.addField( new Roo.form.ComboBox({
24253                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24254                         id : 'val',
24255                         fields: ['val', 'display'],
24256                         data : opts  
24257                     }),
24258                     name : '-roo-edit-' + i,
24259                     attrname : i,
24260                     stylename : item.style ? item.style : false,
24261                     displayField: item.displayField ? item.displayField : 'val',
24262                     valueField :  'val',
24263                     typeAhead: false,
24264                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24265                     editable : false,
24266                     triggerAction: 'all',
24267                     emptyText:'Select',
24268                     selectOnFocus:true,
24269                     width: item.width ? item.width  : 130,
24270                     listeners : {
24271                         'select': function(c, r, i) {
24272                             if (c.stylename) {
24273                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24274                                 return;
24275                             }
24276                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24277                         }
24278                     }
24279
24280                 }));
24281                 continue;
24282                     
24283                  
24284                 
24285                 tb.addField( new Roo.form.TextField({
24286                     name: i,
24287                     width: 100,
24288                     //allowBlank:false,
24289                     value: ''
24290                 }));
24291                 continue;
24292             }
24293             tb.addField( new Roo.form.TextField({
24294                 name: '-roo-edit-' + i,
24295                 attrname : i,
24296                 
24297                 width: item.width,
24298                 //allowBlank:true,
24299                 value: '',
24300                 listeners: {
24301                     'change' : function(f, nv, ov) {
24302                         tb.selectedNode.setAttribute(f.attrname, nv);
24303                         editorcore.syncValue();
24304                     }
24305                 }
24306             }));
24307              
24308         }
24309         
24310         var _this = this;
24311         
24312         if(nm == 'BODY'){
24313             tb.addSeparator();
24314         
24315             tb.addButton( {
24316                 text: 'Stylesheets',
24317
24318                 listeners : {
24319                     click : function ()
24320                     {
24321                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24322                     }
24323                 }
24324             });
24325         }
24326         
24327         tb.addFill();
24328         tb.addButton( {
24329             text: 'Remove Tag',
24330     
24331             listeners : {
24332                 click : function ()
24333                 {
24334                     // remove
24335                     // undo does not work.
24336                      
24337                     var sn = tb.selectedNode;
24338                     
24339                     var pn = sn.parentNode;
24340                     
24341                     var stn =  sn.childNodes[0];
24342                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24343                     while (sn.childNodes.length) {
24344                         var node = sn.childNodes[0];
24345                         sn.removeChild(node);
24346                         //Roo.log(node);
24347                         pn.insertBefore(node, sn);
24348                         
24349                     }
24350                     pn.removeChild(sn);
24351                     var range = editorcore.createRange();
24352         
24353                     range.setStart(stn,0);
24354                     range.setEnd(en,0); //????
24355                     //range.selectNode(sel);
24356                     
24357                     
24358                     var selection = editorcore.getSelection();
24359                     selection.removeAllRanges();
24360                     selection.addRange(range);
24361                     
24362                     
24363                     
24364                     //_this.updateToolbar(null, null, pn);
24365                     _this.updateToolbar(null, null, null);
24366                     _this.footDisp.dom.innerHTML = ''; 
24367                 }
24368             }
24369             
24370                     
24371                 
24372             
24373         });
24374         
24375         
24376         tb.el.on('click', function(e){
24377             e.preventDefault(); // what does this do?
24378         });
24379         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24380         tb.el.hide();
24381         tb.name = nm;
24382         // dont need to disable them... as they will get hidden
24383         return tb;
24384          
24385         
24386     },
24387     buildFooter : function()
24388     {
24389         
24390         var fel = this.editor.wrap.createChild();
24391         this.footer = new Roo.Toolbar(fel);
24392         // toolbar has scrolly on left / right?
24393         var footDisp= new Roo.Toolbar.Fill();
24394         var _t = this;
24395         this.footer.add(
24396             {
24397                 text : '&lt;',
24398                 xtype: 'Button',
24399                 handler : function() {
24400                     _t.footDisp.scrollTo('left',0,true)
24401                 }
24402             }
24403         );
24404         this.footer.add( footDisp );
24405         this.footer.add( 
24406             {
24407                 text : '&gt;',
24408                 xtype: 'Button',
24409                 handler : function() {
24410                     // no animation..
24411                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24412                 }
24413             }
24414         );
24415         var fel = Roo.get(footDisp.el);
24416         fel.addClass('x-editor-context');
24417         this.footDispWrap = fel; 
24418         this.footDispWrap.overflow  = 'hidden';
24419         
24420         this.footDisp = fel.createChild();
24421         this.footDispWrap.on('click', this.onContextClick, this)
24422         
24423         
24424     },
24425     onContextClick : function (ev,dom)
24426     {
24427         ev.preventDefault();
24428         var  cn = dom.className;
24429         //Roo.log(cn);
24430         if (!cn.match(/x-ed-loc-/)) {
24431             return;
24432         }
24433         var n = cn.split('-').pop();
24434         var ans = this.footerEls;
24435         var sel = ans[n];
24436         
24437          // pick
24438         var range = this.editorcore.createRange();
24439         
24440         range.selectNodeContents(sel);
24441         //range.selectNode(sel);
24442         
24443         
24444         var selection = this.editorcore.getSelection();
24445         selection.removeAllRanges();
24446         selection.addRange(range);
24447         
24448         
24449         
24450         this.updateToolbar(null, null, sel);
24451         
24452         
24453     }
24454     
24455     
24456     
24457     
24458     
24459 });
24460
24461
24462
24463
24464
24465 /*
24466  * Based on:
24467  * Ext JS Library 1.1.1
24468  * Copyright(c) 2006-2007, Ext JS, LLC.
24469  *
24470  * Originally Released Under LGPL - original licence link has changed is not relivant.
24471  *
24472  * Fork - LGPL
24473  * <script type="text/javascript">
24474  */
24475  
24476 /**
24477  * @class Roo.form.BasicForm
24478  * @extends Roo.util.Observable
24479  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24480  * @constructor
24481  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24482  * @param {Object} config Configuration options
24483  */
24484 Roo.form.BasicForm = function(el, config){
24485     this.allItems = [];
24486     this.childForms = [];
24487     Roo.apply(this, config);
24488     /*
24489      * The Roo.form.Field items in this form.
24490      * @type MixedCollection
24491      */
24492      
24493      
24494     this.items = new Roo.util.MixedCollection(false, function(o){
24495         return o.id || (o.id = Roo.id());
24496     });
24497     this.addEvents({
24498         /**
24499          * @event beforeaction
24500          * Fires before any action is performed. Return false to cancel the action.
24501          * @param {Form} this
24502          * @param {Action} action The action to be performed
24503          */
24504         beforeaction: true,
24505         /**
24506          * @event actionfailed
24507          * Fires when an action fails.
24508          * @param {Form} this
24509          * @param {Action} action The action that failed
24510          */
24511         actionfailed : true,
24512         /**
24513          * @event actioncomplete
24514          * Fires when an action is completed.
24515          * @param {Form} this
24516          * @param {Action} action The action that completed
24517          */
24518         actioncomplete : true
24519     });
24520     if(el){
24521         this.initEl(el);
24522     }
24523     Roo.form.BasicForm.superclass.constructor.call(this);
24524     
24525     Roo.form.BasicForm.popover.apply();
24526 };
24527
24528 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24529     /**
24530      * @cfg {String} method
24531      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24532      */
24533     /**
24534      * @cfg {DataReader} reader
24535      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24536      * This is optional as there is built-in support for processing JSON.
24537      */
24538     /**
24539      * @cfg {DataReader} errorReader
24540      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24541      * This is completely optional as there is built-in support for processing JSON.
24542      */
24543     /**
24544      * @cfg {String} url
24545      * The URL to use for form actions if one isn't supplied in the action options.
24546      */
24547     /**
24548      * @cfg {Boolean} fileUpload
24549      * Set to true if this form is a file upload.
24550      */
24551      
24552     /**
24553      * @cfg {Object} baseParams
24554      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24555      */
24556      /**
24557      
24558     /**
24559      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24560      */
24561     timeout: 30,
24562
24563     // private
24564     activeAction : null,
24565
24566     /**
24567      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24568      * or setValues() data instead of when the form was first created.
24569      */
24570     trackResetOnLoad : false,
24571     
24572     
24573     /**
24574      * childForms - used for multi-tab forms
24575      * @type {Array}
24576      */
24577     childForms : false,
24578     
24579     /**
24580      * allItems - full list of fields.
24581      * @type {Array}
24582      */
24583     allItems : false,
24584     
24585     /**
24586      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24587      * element by passing it or its id or mask the form itself by passing in true.
24588      * @type Mixed
24589      */
24590     waitMsgTarget : false,
24591     
24592     /**
24593      * @type Boolean
24594      */
24595     disableMask : false,
24596     
24597     /**
24598      * @cfg {Boolean} errorMask (true|false) default false
24599      */
24600     errorMask : false,
24601     
24602     /**
24603      * @cfg {Number} maskOffset Default 100
24604      */
24605     maskOffset : 100,
24606
24607     // private
24608     initEl : function(el){
24609         this.el = Roo.get(el);
24610         this.id = this.el.id || Roo.id();
24611         this.el.on('submit', this.onSubmit, this);
24612         this.el.addClass('x-form');
24613     },
24614
24615     // private
24616     onSubmit : function(e){
24617         e.stopEvent();
24618     },
24619
24620     /**
24621      * Returns true if client-side validation on the form is successful.
24622      * @return Boolean
24623      */
24624     isValid : function(){
24625         var valid = true;
24626         var target = false;
24627         this.items.each(function(f){
24628             if(f.validate()){
24629                 return;
24630             }
24631             
24632             valid = false;
24633                 
24634             if(!target && f.el.isVisible(true)){
24635                 target = f;
24636             }
24637         });
24638         
24639         if(this.errorMask && !valid){
24640             Roo.form.BasicForm.popover.mask(this, target);
24641         }
24642         
24643         return valid;
24644     },
24645     /**
24646      * Returns array of invalid form fields.
24647      * @return Array
24648      */
24649     
24650     invalidFields : function()
24651     {
24652         var ret = [];
24653         this.items.each(function(f){
24654             if(f.validate()){
24655                 return;
24656             }
24657             ret.push(f);
24658             
24659         });
24660         
24661         return ret;
24662     },
24663     
24664     
24665     /**
24666      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24667      * @return Boolean
24668      */
24669     isDirty : function(){
24670         var dirty = false;
24671         this.items.each(function(f){
24672            if(f.isDirty()){
24673                dirty = true;
24674                return false;
24675            }
24676         });
24677         return dirty;
24678     },
24679     
24680     /**
24681      * Returns true if any fields in this form have changed since their original load. (New version)
24682      * @return Boolean
24683      */
24684     
24685     hasChanged : function()
24686     {
24687         var dirty = false;
24688         this.items.each(function(f){
24689            if(f.hasChanged()){
24690                dirty = true;
24691                return false;
24692            }
24693         });
24694         return dirty;
24695         
24696     },
24697     /**
24698      * Resets all hasChanged to 'false' -
24699      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24700      * So hasChanged storage is only to be used for this purpose
24701      * @return Boolean
24702      */
24703     resetHasChanged : function()
24704     {
24705         this.items.each(function(f){
24706            f.resetHasChanged();
24707         });
24708         
24709     },
24710     
24711     
24712     /**
24713      * Performs a predefined action (submit or load) or custom actions you define on this form.
24714      * @param {String} actionName The name of the action type
24715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24717      * accept other config options):
24718      * <pre>
24719 Property          Type             Description
24720 ----------------  ---------------  ----------------------------------------------------------------------------------
24721 url               String           The url for the action (defaults to the form's url)
24722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24725                                    validate the form on the client (defaults to false)
24726      * </pre>
24727      * @return {BasicForm} this
24728      */
24729     doAction : function(action, options){
24730         if(typeof action == 'string'){
24731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24732         }
24733         if(this.fireEvent('beforeaction', this, action) !== false){
24734             this.beforeAction(action);
24735             action.run.defer(100, action);
24736         }
24737         return this;
24738     },
24739
24740     /**
24741      * Shortcut to do a submit action.
24742      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24743      * @return {BasicForm} this
24744      */
24745     submit : function(options){
24746         this.doAction('submit', options);
24747         return this;
24748     },
24749
24750     /**
24751      * Shortcut to do a load action.
24752      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24753      * @return {BasicForm} this
24754      */
24755     load : function(options){
24756         this.doAction('load', options);
24757         return this;
24758     },
24759
24760     /**
24761      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24762      * @param {Record} record The record to edit
24763      * @return {BasicForm} this
24764      */
24765     updateRecord : function(record){
24766         record.beginEdit();
24767         var fs = record.fields;
24768         fs.each(function(f){
24769             var field = this.findField(f.name);
24770             if(field){
24771                 record.set(f.name, field.getValue());
24772             }
24773         }, this);
24774         record.endEdit();
24775         return this;
24776     },
24777
24778     /**
24779      * Loads an Roo.data.Record into this form.
24780      * @param {Record} record The record to load
24781      * @return {BasicForm} this
24782      */
24783     loadRecord : function(record){
24784         this.setValues(record.data);
24785         return this;
24786     },
24787
24788     // private
24789     beforeAction : function(action){
24790         var o = action.options;
24791         
24792         if(!this.disableMask) {
24793             if(this.waitMsgTarget === true){
24794                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24795             }else if(this.waitMsgTarget){
24796                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24797                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24798             }else {
24799                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24800             }
24801         }
24802         
24803          
24804     },
24805
24806     // private
24807     afterAction : function(action, success){
24808         this.activeAction = null;
24809         var o = action.options;
24810         
24811         if(!this.disableMask) {
24812             if(this.waitMsgTarget === true){
24813                 this.el.unmask();
24814             }else if(this.waitMsgTarget){
24815                 this.waitMsgTarget.unmask();
24816             }else{
24817                 Roo.MessageBox.updateProgress(1);
24818                 Roo.MessageBox.hide();
24819             }
24820         }
24821         
24822         if(success){
24823             if(o.reset){
24824                 this.reset();
24825             }
24826             Roo.callback(o.success, o.scope, [this, action]);
24827             this.fireEvent('actioncomplete', this, action);
24828             
24829         }else{
24830             
24831             // failure condition..
24832             // we have a scenario where updates need confirming.
24833             // eg. if a locking scenario exists..
24834             // we look for { errors : { needs_confirm : true }} in the response.
24835             if (
24836                 (typeof(action.result) != 'undefined')  &&
24837                 (typeof(action.result.errors) != 'undefined')  &&
24838                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24839            ){
24840                 var _t = this;
24841                 Roo.MessageBox.confirm(
24842                     "Change requires confirmation",
24843                     action.result.errorMsg,
24844                     function(r) {
24845                         if (r != 'yes') {
24846                             return;
24847                         }
24848                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24849                     }
24850                     
24851                 );
24852                 
24853                 
24854                 
24855                 return;
24856             }
24857             
24858             Roo.callback(o.failure, o.scope, [this, action]);
24859             // show an error message if no failed handler is set..
24860             if (!this.hasListener('actionfailed')) {
24861                 Roo.MessageBox.alert("Error",
24862                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24863                         action.result.errorMsg :
24864                         "Saving Failed, please check your entries or try again"
24865                 );
24866             }
24867             
24868             this.fireEvent('actionfailed', this, action);
24869         }
24870         
24871     },
24872
24873     /**
24874      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24875      * @param {String} id The value to search for
24876      * @return Field
24877      */
24878     findField : function(id){
24879         var field = this.items.get(id);
24880         if(!field){
24881             this.items.each(function(f){
24882                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24883                     field = f;
24884                     return false;
24885                 }
24886             });
24887         }
24888         return field || null;
24889     },
24890
24891     /**
24892      * Add a secondary form to this one, 
24893      * Used to provide tabbed forms. One form is primary, with hidden values 
24894      * which mirror the elements from the other forms.
24895      * 
24896      * @param {Roo.form.Form} form to add.
24897      * 
24898      */
24899     addForm : function(form)
24900     {
24901        
24902         if (this.childForms.indexOf(form) > -1) {
24903             // already added..
24904             return;
24905         }
24906         this.childForms.push(form);
24907         var n = '';
24908         Roo.each(form.allItems, function (fe) {
24909             
24910             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24911             if (this.findField(n)) { // already added..
24912                 return;
24913             }
24914             var add = new Roo.form.Hidden({
24915                 name : n
24916             });
24917             add.render(this.el);
24918             
24919             this.add( add );
24920         }, this);
24921         
24922     },
24923     /**
24924      * Mark fields in this form invalid in bulk.
24925      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24926      * @return {BasicForm} this
24927      */
24928     markInvalid : function(errors){
24929         if(errors instanceof Array){
24930             for(var i = 0, len = errors.length; i < len; i++){
24931                 var fieldError = errors[i];
24932                 var f = this.findField(fieldError.id);
24933                 if(f){
24934                     f.markInvalid(fieldError.msg);
24935                 }
24936             }
24937         }else{
24938             var field, id;
24939             for(id in errors){
24940                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24941                     field.markInvalid(errors[id]);
24942                 }
24943             }
24944         }
24945         Roo.each(this.childForms || [], function (f) {
24946             f.markInvalid(errors);
24947         });
24948         
24949         return this;
24950     },
24951
24952     /**
24953      * Set values for fields in this form in bulk.
24954      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24955      * @return {BasicForm} this
24956      */
24957     setValues : function(values){
24958         if(values instanceof Array){ // array of objects
24959             for(var i = 0, len = values.length; i < len; i++){
24960                 var v = values[i];
24961                 var f = this.findField(v.id);
24962                 if(f){
24963                     f.setValue(v.value);
24964                     if(this.trackResetOnLoad){
24965                         f.originalValue = f.getValue();
24966                     }
24967                 }
24968             }
24969         }else{ // object hash
24970             var field, id;
24971             for(id in values){
24972                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24973                     
24974                     if (field.setFromData && 
24975                         field.valueField && 
24976                         field.displayField &&
24977                         // combos' with local stores can 
24978                         // be queried via setValue()
24979                         // to set their value..
24980                         (field.store && !field.store.isLocal)
24981                         ) {
24982                         // it's a combo
24983                         var sd = { };
24984                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24985                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24986                         field.setFromData(sd);
24987                         
24988                     } else {
24989                         field.setValue(values[id]);
24990                     }
24991                     
24992                     
24993                     if(this.trackResetOnLoad){
24994                         field.originalValue = field.getValue();
24995                     }
24996                 }
24997             }
24998         }
24999         this.resetHasChanged();
25000         
25001         
25002         Roo.each(this.childForms || [], function (f) {
25003             f.setValues(values);
25004             f.resetHasChanged();
25005         });
25006                 
25007         return this;
25008     },
25009  
25010     /**
25011      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25012      * they are returned as an array.
25013      * @param {Boolean} asString
25014      * @return {Object}
25015      */
25016     getValues : function(asString){
25017         if (this.childForms) {
25018             // copy values from the child forms
25019             Roo.each(this.childForms, function (f) {
25020                 this.setValues(f.getValues());
25021             }, this);
25022         }
25023         
25024         // use formdata
25025         if (typeof(FormData) != 'undefined' && asString !== true) {
25026             // this relies on a 'recent' version of chrome apparently...
25027             try {
25028                 var fd = (new FormData(this.el.dom)).entries();
25029                 var ret = {};
25030                 var ent = fd.next();
25031                 while (!ent.done) {
25032                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25033                     ent = fd.next();
25034                 };
25035                 return ret;
25036             } catch(e) {
25037                 
25038             }
25039             
25040         }
25041         
25042         
25043         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25044         if(asString === true){
25045             return fs;
25046         }
25047         return Roo.urlDecode(fs);
25048     },
25049     
25050     /**
25051      * Returns the fields in this form as an object with key/value pairs. 
25052      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25053      * @return {Object}
25054      */
25055     getFieldValues : function(with_hidden)
25056     {
25057         if (this.childForms) {
25058             // copy values from the child forms
25059             // should this call getFieldValues - probably not as we do not currently copy
25060             // hidden fields when we generate..
25061             Roo.each(this.childForms, function (f) {
25062                 this.setValues(f.getValues());
25063             }, this);
25064         }
25065         
25066         var ret = {};
25067         this.items.each(function(f){
25068             if (!f.getName()) {
25069                 return;
25070             }
25071             var v = f.getValue();
25072             if (f.inputType =='radio') {
25073                 if (typeof(ret[f.getName()]) == 'undefined') {
25074                     ret[f.getName()] = ''; // empty..
25075                 }
25076                 
25077                 if (!f.el.dom.checked) {
25078                     return;
25079                     
25080                 }
25081                 v = f.el.dom.value;
25082                 
25083             }
25084             
25085             // not sure if this supported any more..
25086             if ((typeof(v) == 'object') && f.getRawValue) {
25087                 v = f.getRawValue() ; // dates..
25088             }
25089             // combo boxes where name != hiddenName...
25090             if (f.name != f.getName()) {
25091                 ret[f.name] = f.getRawValue();
25092             }
25093             ret[f.getName()] = v;
25094         });
25095         
25096         return ret;
25097     },
25098
25099     /**
25100      * Clears all invalid messages in this form.
25101      * @return {BasicForm} this
25102      */
25103     clearInvalid : function(){
25104         this.items.each(function(f){
25105            f.clearInvalid();
25106         });
25107         
25108         Roo.each(this.childForms || [], function (f) {
25109             f.clearInvalid();
25110         });
25111         
25112         
25113         return this;
25114     },
25115
25116     /**
25117      * Resets this form.
25118      * @return {BasicForm} this
25119      */
25120     reset : function(){
25121         this.items.each(function(f){
25122             f.reset();
25123         });
25124         
25125         Roo.each(this.childForms || [], function (f) {
25126             f.reset();
25127         });
25128         this.resetHasChanged();
25129         
25130         return this;
25131     },
25132
25133     /**
25134      * Add Roo.form components to this form.
25135      * @param {Field} field1
25136      * @param {Field} field2 (optional)
25137      * @param {Field} etc (optional)
25138      * @return {BasicForm} this
25139      */
25140     add : function(){
25141         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25142         return this;
25143     },
25144
25145
25146     /**
25147      * Removes a field from the items collection (does NOT remove its markup).
25148      * @param {Field} field
25149      * @return {BasicForm} this
25150      */
25151     remove : function(field){
25152         this.items.remove(field);
25153         return this;
25154     },
25155
25156     /**
25157      * Looks at the fields in this form, checks them for an id attribute,
25158      * and calls applyTo on the existing dom element with that id.
25159      * @return {BasicForm} this
25160      */
25161     render : function(){
25162         this.items.each(function(f){
25163             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25164                 f.applyTo(f.id);
25165             }
25166         });
25167         return this;
25168     },
25169
25170     /**
25171      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25172      * @param {Object} values
25173      * @return {BasicForm} this
25174      */
25175     applyToFields : function(o){
25176         this.items.each(function(f){
25177            Roo.apply(f, o);
25178         });
25179         return this;
25180     },
25181
25182     /**
25183      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25184      * @param {Object} values
25185      * @return {BasicForm} this
25186      */
25187     applyIfToFields : function(o){
25188         this.items.each(function(f){
25189            Roo.applyIf(f, o);
25190         });
25191         return this;
25192     }
25193 });
25194
25195 // back compat
25196 Roo.BasicForm = Roo.form.BasicForm;
25197
25198 Roo.apply(Roo.form.BasicForm, {
25199     
25200     popover : {
25201         
25202         padding : 5,
25203         
25204         isApplied : false,
25205         
25206         isMasked : false,
25207         
25208         form : false,
25209         
25210         target : false,
25211         
25212         intervalID : false,
25213         
25214         maskEl : false,
25215         
25216         apply : function()
25217         {
25218             if(this.isApplied){
25219                 return;
25220             }
25221             
25222             this.maskEl = {
25223                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25224                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25225                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25226                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25227             };
25228             
25229             this.maskEl.top.enableDisplayMode("block");
25230             this.maskEl.left.enableDisplayMode("block");
25231             this.maskEl.bottom.enableDisplayMode("block");
25232             this.maskEl.right.enableDisplayMode("block");
25233             
25234             Roo.get(document.body).on('click', function(){
25235                 this.unmask();
25236             }, this);
25237             
25238             Roo.get(document.body).on('touchstart', function(){
25239                 this.unmask();
25240             }, this);
25241             
25242             this.isApplied = true
25243         },
25244         
25245         mask : function(form, target)
25246         {
25247             this.form = form;
25248             
25249             this.target = target;
25250             
25251             if(!this.form.errorMask || !target.el){
25252                 return;
25253             }
25254             
25255             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25256             
25257             var ot = this.target.el.calcOffsetsTo(scrollable);
25258             
25259             var scrollTo = ot[1] - this.form.maskOffset;
25260             
25261             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25262             
25263             scrollable.scrollTo('top', scrollTo);
25264             
25265             var el = this.target.wrap || this.target.el;
25266             
25267             var box = el.getBox();
25268             
25269             this.maskEl.top.setStyle('position', 'absolute');
25270             this.maskEl.top.setStyle('z-index', 10000);
25271             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25272             this.maskEl.top.setLeft(0);
25273             this.maskEl.top.setTop(0);
25274             this.maskEl.top.show();
25275             
25276             this.maskEl.left.setStyle('position', 'absolute');
25277             this.maskEl.left.setStyle('z-index', 10000);
25278             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25279             this.maskEl.left.setLeft(0);
25280             this.maskEl.left.setTop(box.y - this.padding);
25281             this.maskEl.left.show();
25282
25283             this.maskEl.bottom.setStyle('position', 'absolute');
25284             this.maskEl.bottom.setStyle('z-index', 10000);
25285             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25286             this.maskEl.bottom.setLeft(0);
25287             this.maskEl.bottom.setTop(box.bottom + this.padding);
25288             this.maskEl.bottom.show();
25289
25290             this.maskEl.right.setStyle('position', 'absolute');
25291             this.maskEl.right.setStyle('z-index', 10000);
25292             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25293             this.maskEl.right.setLeft(box.right + this.padding);
25294             this.maskEl.right.setTop(box.y - this.padding);
25295             this.maskEl.right.show();
25296
25297             this.intervalID = window.setInterval(function() {
25298                 Roo.form.BasicForm.popover.unmask();
25299             }, 10000);
25300
25301             window.onwheel = function(){ return false;};
25302             
25303             (function(){ this.isMasked = true; }).defer(500, this);
25304             
25305         },
25306         
25307         unmask : function()
25308         {
25309             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25310                 return;
25311             }
25312             
25313             this.maskEl.top.setStyle('position', 'absolute');
25314             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25315             this.maskEl.top.hide();
25316
25317             this.maskEl.left.setStyle('position', 'absolute');
25318             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25319             this.maskEl.left.hide();
25320
25321             this.maskEl.bottom.setStyle('position', 'absolute');
25322             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25323             this.maskEl.bottom.hide();
25324
25325             this.maskEl.right.setStyle('position', 'absolute');
25326             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25327             this.maskEl.right.hide();
25328             
25329             window.onwheel = function(){ return true;};
25330             
25331             if(this.intervalID){
25332                 window.clearInterval(this.intervalID);
25333                 this.intervalID = false;
25334             }
25335             
25336             this.isMasked = false;
25337             
25338         }
25339         
25340     }
25341     
25342 });/*
25343  * Based on:
25344  * Ext JS Library 1.1.1
25345  * Copyright(c) 2006-2007, Ext JS, LLC.
25346  *
25347  * Originally Released Under LGPL - original licence link has changed is not relivant.
25348  *
25349  * Fork - LGPL
25350  * <script type="text/javascript">
25351  */
25352
25353 /**
25354  * @class Roo.form.Form
25355  * @extends Roo.form.BasicForm
25356  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25357  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25358  * @constructor
25359  * @param {Object} config Configuration options
25360  */
25361 Roo.form.Form = function(config){
25362     var xitems =  [];
25363     if (config.items) {
25364         xitems = config.items;
25365         delete config.items;
25366     }
25367    
25368     
25369     Roo.form.Form.superclass.constructor.call(this, null, config);
25370     this.url = this.url || this.action;
25371     if(!this.root){
25372         this.root = new Roo.form.Layout(Roo.applyIf({
25373             id: Roo.id()
25374         }, config));
25375     }
25376     this.active = this.root;
25377     /**
25378      * Array of all the buttons that have been added to this form via {@link addButton}
25379      * @type Array
25380      */
25381     this.buttons = [];
25382     this.allItems = [];
25383     this.addEvents({
25384         /**
25385          * @event clientvalidation
25386          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25387          * @param {Form} this
25388          * @param {Boolean} valid true if the form has passed client-side validation
25389          */
25390         clientvalidation: true,
25391         /**
25392          * @event rendered
25393          * Fires when the form is rendered
25394          * @param {Roo.form.Form} form
25395          */
25396         rendered : true
25397     });
25398     
25399     if (this.progressUrl) {
25400             // push a hidden field onto the list of fields..
25401             this.addxtype( {
25402                     xns: Roo.form, 
25403                     xtype : 'Hidden', 
25404                     name : 'UPLOAD_IDENTIFIER' 
25405             });
25406         }
25407         
25408     
25409     Roo.each(xitems, this.addxtype, this);
25410     
25411 };
25412
25413 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25414      /**
25415      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25416      */
25417     
25418     /**
25419      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25420      */
25421     /**
25422      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25423      */
25424     /**
25425      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25426      */
25427     buttonAlign:'center',
25428
25429     /**
25430      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25431      */
25432     minButtonWidth:75,
25433
25434     /**
25435      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25436      * This property cascades to child containers if not set.
25437      */
25438     labelAlign:'left',
25439
25440     /**
25441      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25442      * fires a looping event with that state. This is required to bind buttons to the valid
25443      * state using the config value formBind:true on the button.
25444      */
25445     monitorValid : false,
25446
25447     /**
25448      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25449      */
25450     monitorPoll : 200,
25451     
25452     /**
25453      * @cfg {String} progressUrl - Url to return progress data 
25454      */
25455     
25456     progressUrl : false,
25457     /**
25458      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25459      * sending a formdata with extra parameters - eg uploaded elements.
25460      */
25461     
25462     formData : false,
25463     
25464     /**
25465      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25466      * fields are added and the column is closed. If no fields are passed the column remains open
25467      * until end() is called.
25468      * @param {Object} config The config to pass to the column
25469      * @param {Field} field1 (optional)
25470      * @param {Field} field2 (optional)
25471      * @param {Field} etc (optional)
25472      * @return Column The column container object
25473      */
25474     column : function(c){
25475         var col = new Roo.form.Column(c);
25476         this.start(col);
25477         if(arguments.length > 1){ // duplicate code required because of Opera
25478             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25479             this.end();
25480         }
25481         return col;
25482     },
25483
25484     /**
25485      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25486      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25487      * until end() is called.
25488      * @param {Object} config The config to pass to the fieldset
25489      * @param {Field} field1 (optional)
25490      * @param {Field} field2 (optional)
25491      * @param {Field} etc (optional)
25492      * @return FieldSet The fieldset container object
25493      */
25494     fieldset : function(c){
25495         var fs = new Roo.form.FieldSet(c);
25496         this.start(fs);
25497         if(arguments.length > 1){ // duplicate code required because of Opera
25498             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25499             this.end();
25500         }
25501         return fs;
25502     },
25503
25504     /**
25505      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25506      * fields are added and the container is closed. If no fields are passed the container remains open
25507      * until end() is called.
25508      * @param {Object} config The config to pass to the Layout
25509      * @param {Field} field1 (optional)
25510      * @param {Field} field2 (optional)
25511      * @param {Field} etc (optional)
25512      * @return Layout The container object
25513      */
25514     container : function(c){
25515         var l = new Roo.form.Layout(c);
25516         this.start(l);
25517         if(arguments.length > 1){ // duplicate code required because of Opera
25518             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25519             this.end();
25520         }
25521         return l;
25522     },
25523
25524     /**
25525      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25526      * @param {Object} container A Roo.form.Layout or subclass of Layout
25527      * @return {Form} this
25528      */
25529     start : function(c){
25530         // cascade label info
25531         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25532         this.active.stack.push(c);
25533         c.ownerCt = this.active;
25534         this.active = c;
25535         return this;
25536     },
25537
25538     /**
25539      * Closes the current open container
25540      * @return {Form} this
25541      */
25542     end : function(){
25543         if(this.active == this.root){
25544             return this;
25545         }
25546         this.active = this.active.ownerCt;
25547         return this;
25548     },
25549
25550     /**
25551      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25552      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25553      * as the label of the field.
25554      * @param {Field} field1
25555      * @param {Field} field2 (optional)
25556      * @param {Field} etc. (optional)
25557      * @return {Form} this
25558      */
25559     add : function(){
25560         this.active.stack.push.apply(this.active.stack, arguments);
25561         this.allItems.push.apply(this.allItems,arguments);
25562         var r = [];
25563         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25564             if(a[i].isFormField){
25565                 r.push(a[i]);
25566             }
25567         }
25568         if(r.length > 0){
25569             Roo.form.Form.superclass.add.apply(this, r);
25570         }
25571         return this;
25572     },
25573     
25574
25575     
25576     
25577     
25578      /**
25579      * Find any element that has been added to a form, using it's ID or name
25580      * This can include framesets, columns etc. along with regular fields..
25581      * @param {String} id - id or name to find.
25582      
25583      * @return {Element} e - or false if nothing found.
25584      */
25585     findbyId : function(id)
25586     {
25587         var ret = false;
25588         if (!id) {
25589             return ret;
25590         }
25591         Roo.each(this.allItems, function(f){
25592             if (f.id == id || f.name == id ){
25593                 ret = f;
25594                 return false;
25595             }
25596         });
25597         return ret;
25598     },
25599
25600     
25601     
25602     /**
25603      * Render this form into the passed container. This should only be called once!
25604      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25605      * @return {Form} this
25606      */
25607     render : function(ct)
25608     {
25609         
25610         
25611         
25612         ct = Roo.get(ct);
25613         var o = this.autoCreate || {
25614             tag: 'form',
25615             method : this.method || 'POST',
25616             id : this.id || Roo.id()
25617         };
25618         this.initEl(ct.createChild(o));
25619
25620         this.root.render(this.el);
25621         
25622        
25623              
25624         this.items.each(function(f){
25625             f.render('x-form-el-'+f.id);
25626         });
25627
25628         if(this.buttons.length > 0){
25629             // tables are required to maintain order and for correct IE layout
25630             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25631                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25632                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25633             }}, null, true);
25634             var tr = tb.getElementsByTagName('tr')[0];
25635             for(var i = 0, len = this.buttons.length; i < len; i++) {
25636                 var b = this.buttons[i];
25637                 var td = document.createElement('td');
25638                 td.className = 'x-form-btn-td';
25639                 b.render(tr.appendChild(td));
25640             }
25641         }
25642         if(this.monitorValid){ // initialize after render
25643             this.startMonitoring();
25644         }
25645         this.fireEvent('rendered', this);
25646         return this;
25647     },
25648
25649     /**
25650      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25651      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25652      * object or a valid Roo.DomHelper element config
25653      * @param {Function} handler The function called when the button is clicked
25654      * @param {Object} scope (optional) The scope of the handler function
25655      * @return {Roo.Button}
25656      */
25657     addButton : function(config, handler, scope){
25658         var bc = {
25659             handler: handler,
25660             scope: scope,
25661             minWidth: this.minButtonWidth,
25662             hideParent:true
25663         };
25664         if(typeof config == "string"){
25665             bc.text = config;
25666         }else{
25667             Roo.apply(bc, config);
25668         }
25669         var btn = new Roo.Button(null, bc);
25670         this.buttons.push(btn);
25671         return btn;
25672     },
25673
25674      /**
25675      * Adds a series of form elements (using the xtype property as the factory method.
25676      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25677      * @param {Object} config 
25678      */
25679     
25680     addxtype : function()
25681     {
25682         var ar = Array.prototype.slice.call(arguments, 0);
25683         var ret = false;
25684         for(var i = 0; i < ar.length; i++) {
25685             if (!ar[i]) {
25686                 continue; // skip -- if this happends something invalid got sent, we 
25687                 // should ignore it, as basically that interface element will not show up
25688                 // and that should be pretty obvious!!
25689             }
25690             
25691             if (Roo.form[ar[i].xtype]) {
25692                 ar[i].form = this;
25693                 var fe = Roo.factory(ar[i], Roo.form);
25694                 if (!ret) {
25695                     ret = fe;
25696                 }
25697                 fe.form = this;
25698                 if (fe.store) {
25699                     fe.store.form = this;
25700                 }
25701                 if (fe.isLayout) {  
25702                          
25703                     this.start(fe);
25704                     this.allItems.push(fe);
25705                     if (fe.items && fe.addxtype) {
25706                         fe.addxtype.apply(fe, fe.items);
25707                         delete fe.items;
25708                     }
25709                      this.end();
25710                     continue;
25711                 }
25712                 
25713                 
25714                  
25715                 this.add(fe);
25716               //  console.log('adding ' + ar[i].xtype);
25717             }
25718             if (ar[i].xtype == 'Button') {  
25719                 //console.log('adding button');
25720                 //console.log(ar[i]);
25721                 this.addButton(ar[i]);
25722                 this.allItems.push(fe);
25723                 continue;
25724             }
25725             
25726             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25727                 alert('end is not supported on xtype any more, use items');
25728             //    this.end();
25729             //    //console.log('adding end');
25730             }
25731             
25732         }
25733         return ret;
25734     },
25735     
25736     /**
25737      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25738      * option "monitorValid"
25739      */
25740     startMonitoring : function(){
25741         if(!this.bound){
25742             this.bound = true;
25743             Roo.TaskMgr.start({
25744                 run : this.bindHandler,
25745                 interval : this.monitorPoll || 200,
25746                 scope: this
25747             });
25748         }
25749     },
25750
25751     /**
25752      * Stops monitoring of the valid state of this form
25753      */
25754     stopMonitoring : function(){
25755         this.bound = false;
25756     },
25757
25758     // private
25759     bindHandler : function(){
25760         if(!this.bound){
25761             return false; // stops binding
25762         }
25763         var valid = true;
25764         this.items.each(function(f){
25765             if(!f.isValid(true)){
25766                 valid = false;
25767                 return false;
25768             }
25769         });
25770         for(var i = 0, len = this.buttons.length; i < len; i++){
25771             var btn = this.buttons[i];
25772             if(btn.formBind === true && btn.disabled === valid){
25773                 btn.setDisabled(!valid);
25774             }
25775         }
25776         this.fireEvent('clientvalidation', this, valid);
25777     }
25778     
25779     
25780     
25781     
25782     
25783     
25784     
25785     
25786 });
25787
25788
25789 // back compat
25790 Roo.Form = Roo.form.Form;
25791 /*
25792  * Based on:
25793  * Ext JS Library 1.1.1
25794  * Copyright(c) 2006-2007, Ext JS, LLC.
25795  *
25796  * Originally Released Under LGPL - original licence link has changed is not relivant.
25797  *
25798  * Fork - LGPL
25799  * <script type="text/javascript">
25800  */
25801
25802 // as we use this in bootstrap.
25803 Roo.namespace('Roo.form');
25804  /**
25805  * @class Roo.form.Action
25806  * Internal Class used to handle form actions
25807  * @constructor
25808  * @param {Roo.form.BasicForm} el The form element or its id
25809  * @param {Object} config Configuration options
25810  */
25811
25812  
25813  
25814 // define the action interface
25815 Roo.form.Action = function(form, options){
25816     this.form = form;
25817     this.options = options || {};
25818 };
25819 /**
25820  * Client Validation Failed
25821  * @const 
25822  */
25823 Roo.form.Action.CLIENT_INVALID = 'client';
25824 /**
25825  * Server Validation Failed
25826  * @const 
25827  */
25828 Roo.form.Action.SERVER_INVALID = 'server';
25829  /**
25830  * Connect to Server Failed
25831  * @const 
25832  */
25833 Roo.form.Action.CONNECT_FAILURE = 'connect';
25834 /**
25835  * Reading Data from Server Failed
25836  * @const 
25837  */
25838 Roo.form.Action.LOAD_FAILURE = 'load';
25839
25840 Roo.form.Action.prototype = {
25841     type : 'default',
25842     failureType : undefined,
25843     response : undefined,
25844     result : undefined,
25845
25846     // interface method
25847     run : function(options){
25848
25849     },
25850
25851     // interface method
25852     success : function(response){
25853
25854     },
25855
25856     // interface method
25857     handleResponse : function(response){
25858
25859     },
25860
25861     // default connection failure
25862     failure : function(response){
25863         
25864         this.response = response;
25865         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25866         this.form.afterAction(this, false);
25867     },
25868
25869     processResponse : function(response){
25870         this.response = response;
25871         if(!response.responseText){
25872             return true;
25873         }
25874         this.result = this.handleResponse(response);
25875         return this.result;
25876     },
25877
25878     // utility functions used internally
25879     getUrl : function(appendParams){
25880         var url = this.options.url || this.form.url || this.form.el.dom.action;
25881         if(appendParams){
25882             var p = this.getParams();
25883             if(p){
25884                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25885             }
25886         }
25887         return url;
25888     },
25889
25890     getMethod : function(){
25891         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25892     },
25893
25894     getParams : function(){
25895         var bp = this.form.baseParams;
25896         var p = this.options.params;
25897         if(p){
25898             if(typeof p == "object"){
25899                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25900             }else if(typeof p == 'string' && bp){
25901                 p += '&' + Roo.urlEncode(bp);
25902             }
25903         }else if(bp){
25904             p = Roo.urlEncode(bp);
25905         }
25906         return p;
25907     },
25908
25909     createCallback : function(){
25910         return {
25911             success: this.success,
25912             failure: this.failure,
25913             scope: this,
25914             timeout: (this.form.timeout*1000),
25915             upload: this.form.fileUpload ? this.success : undefined
25916         };
25917     }
25918 };
25919
25920 Roo.form.Action.Submit = function(form, options){
25921     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25922 };
25923
25924 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25925     type : 'submit',
25926
25927     haveProgress : false,
25928     uploadComplete : false,
25929     
25930     // uploadProgress indicator.
25931     uploadProgress : function()
25932     {
25933         if (!this.form.progressUrl) {
25934             return;
25935         }
25936         
25937         if (!this.haveProgress) {
25938             Roo.MessageBox.progress("Uploading", "Uploading");
25939         }
25940         if (this.uploadComplete) {
25941            Roo.MessageBox.hide();
25942            return;
25943         }
25944         
25945         this.haveProgress = true;
25946    
25947         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25948         
25949         var c = new Roo.data.Connection();
25950         c.request({
25951             url : this.form.progressUrl,
25952             params: {
25953                 id : uid
25954             },
25955             method: 'GET',
25956             success : function(req){
25957                //console.log(data);
25958                 var rdata = false;
25959                 var edata;
25960                 try  {
25961                    rdata = Roo.decode(req.responseText)
25962                 } catch (e) {
25963                     Roo.log("Invalid data from server..");
25964                     Roo.log(edata);
25965                     return;
25966                 }
25967                 if (!rdata || !rdata.success) {
25968                     Roo.log(rdata);
25969                     Roo.MessageBox.alert(Roo.encode(rdata));
25970                     return;
25971                 }
25972                 var data = rdata.data;
25973                 
25974                 if (this.uploadComplete) {
25975                    Roo.MessageBox.hide();
25976                    return;
25977                 }
25978                    
25979                 if (data){
25980                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25981                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25982                     );
25983                 }
25984                 this.uploadProgress.defer(2000,this);
25985             },
25986        
25987             failure: function(data) {
25988                 Roo.log('progress url failed ');
25989                 Roo.log(data);
25990             },
25991             scope : this
25992         });
25993            
25994     },
25995     
25996     
25997     run : function()
25998     {
25999         // run get Values on the form, so it syncs any secondary forms.
26000         this.form.getValues();
26001         
26002         var o = this.options;
26003         var method = this.getMethod();
26004         var isPost = method == 'POST';
26005         if(o.clientValidation === false || this.form.isValid()){
26006             
26007             if (this.form.progressUrl) {
26008                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26009                     (new Date() * 1) + '' + Math.random());
26010                     
26011             } 
26012             
26013             
26014             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26015                 form:this.form.el.dom,
26016                 url:this.getUrl(!isPost),
26017                 method: method,
26018                 params:isPost ? this.getParams() : null,
26019                 isUpload: this.form.fileUpload,
26020                 formData : this.form.formData
26021             }));
26022             
26023             this.uploadProgress();
26024
26025         }else if (o.clientValidation !== false){ // client validation failed
26026             this.failureType = Roo.form.Action.CLIENT_INVALID;
26027             this.form.afterAction(this, false);
26028         }
26029     },
26030
26031     success : function(response)
26032     {
26033         this.uploadComplete= true;
26034         if (this.haveProgress) {
26035             Roo.MessageBox.hide();
26036         }
26037         
26038         
26039         var result = this.processResponse(response);
26040         if(result === true || result.success){
26041             this.form.afterAction(this, true);
26042             return;
26043         }
26044         if(result.errors){
26045             this.form.markInvalid(result.errors);
26046             this.failureType = Roo.form.Action.SERVER_INVALID;
26047         }
26048         this.form.afterAction(this, false);
26049     },
26050     failure : function(response)
26051     {
26052         this.uploadComplete= true;
26053         if (this.haveProgress) {
26054             Roo.MessageBox.hide();
26055         }
26056         
26057         this.response = response;
26058         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26059         this.form.afterAction(this, false);
26060     },
26061     
26062     handleResponse : function(response){
26063         if(this.form.errorReader){
26064             var rs = this.form.errorReader.read(response);
26065             var errors = [];
26066             if(rs.records){
26067                 for(var i = 0, len = rs.records.length; i < len; i++) {
26068                     var r = rs.records[i];
26069                     errors[i] = r.data;
26070                 }
26071             }
26072             if(errors.length < 1){
26073                 errors = null;
26074             }
26075             return {
26076                 success : rs.success,
26077                 errors : errors
26078             };
26079         }
26080         var ret = false;
26081         try {
26082             ret = Roo.decode(response.responseText);
26083         } catch (e) {
26084             ret = {
26085                 success: false,
26086                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26087                 errors : []
26088             };
26089         }
26090         return ret;
26091         
26092     }
26093 });
26094
26095
26096 Roo.form.Action.Load = function(form, options){
26097     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26098     this.reader = this.form.reader;
26099 };
26100
26101 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26102     type : 'load',
26103
26104     run : function(){
26105         
26106         Roo.Ajax.request(Roo.apply(
26107                 this.createCallback(), {
26108                     method:this.getMethod(),
26109                     url:this.getUrl(false),
26110                     params:this.getParams()
26111         }));
26112     },
26113
26114     success : function(response){
26115         
26116         var result = this.processResponse(response);
26117         if(result === true || !result.success || !result.data){
26118             this.failureType = Roo.form.Action.LOAD_FAILURE;
26119             this.form.afterAction(this, false);
26120             return;
26121         }
26122         this.form.clearInvalid();
26123         this.form.setValues(result.data);
26124         this.form.afterAction(this, true);
26125     },
26126
26127     handleResponse : function(response){
26128         if(this.form.reader){
26129             var rs = this.form.reader.read(response);
26130             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26131             return {
26132                 success : rs.success,
26133                 data : data
26134             };
26135         }
26136         return Roo.decode(response.responseText);
26137     }
26138 });
26139
26140 Roo.form.Action.ACTION_TYPES = {
26141     'load' : Roo.form.Action.Load,
26142     'submit' : Roo.form.Action.Submit
26143 };/*
26144  * Based on:
26145  * Ext JS Library 1.1.1
26146  * Copyright(c) 2006-2007, Ext JS, LLC.
26147  *
26148  * Originally Released Under LGPL - original licence link has changed is not relivant.
26149  *
26150  * Fork - LGPL
26151  * <script type="text/javascript">
26152  */
26153  
26154 /**
26155  * @class Roo.form.Layout
26156  * @extends Roo.Component
26157  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26158  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26159  * @constructor
26160  * @param {Object} config Configuration options
26161  */
26162 Roo.form.Layout = function(config){
26163     var xitems = [];
26164     if (config.items) {
26165         xitems = config.items;
26166         delete config.items;
26167     }
26168     Roo.form.Layout.superclass.constructor.call(this, config);
26169     this.stack = [];
26170     Roo.each(xitems, this.addxtype, this);
26171      
26172 };
26173
26174 Roo.extend(Roo.form.Layout, Roo.Component, {
26175     /**
26176      * @cfg {String/Object} autoCreate
26177      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26178      */
26179     /**
26180      * @cfg {String/Object/Function} style
26181      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26182      * a function which returns such a specification.
26183      */
26184     /**
26185      * @cfg {String} labelAlign
26186      * Valid values are "left," "top" and "right" (defaults to "left")
26187      */
26188     /**
26189      * @cfg {Number} labelWidth
26190      * Fixed width in pixels of all field labels (defaults to undefined)
26191      */
26192     /**
26193      * @cfg {Boolean} clear
26194      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26195      */
26196     clear : true,
26197     /**
26198      * @cfg {String} labelSeparator
26199      * The separator to use after field labels (defaults to ':')
26200      */
26201     labelSeparator : ':',
26202     /**
26203      * @cfg {Boolean} hideLabels
26204      * True to suppress the display of field labels in this layout (defaults to false)
26205      */
26206     hideLabels : false,
26207
26208     // private
26209     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26210     
26211     isLayout : true,
26212     
26213     // private
26214     onRender : function(ct, position){
26215         if(this.el){ // from markup
26216             this.el = Roo.get(this.el);
26217         }else {  // generate
26218             var cfg = this.getAutoCreate();
26219             this.el = ct.createChild(cfg, position);
26220         }
26221         if(this.style){
26222             this.el.applyStyles(this.style);
26223         }
26224         if(this.labelAlign){
26225             this.el.addClass('x-form-label-'+this.labelAlign);
26226         }
26227         if(this.hideLabels){
26228             this.labelStyle = "display:none";
26229             this.elementStyle = "padding-left:0;";
26230         }else{
26231             if(typeof this.labelWidth == 'number'){
26232                 this.labelStyle = "width:"+this.labelWidth+"px;";
26233                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26234             }
26235             if(this.labelAlign == 'top'){
26236                 this.labelStyle = "width:auto;";
26237                 this.elementStyle = "padding-left:0;";
26238             }
26239         }
26240         var stack = this.stack;
26241         var slen = stack.length;
26242         if(slen > 0){
26243             if(!this.fieldTpl){
26244                 var t = new Roo.Template(
26245                     '<div class="x-form-item {5}">',
26246                         '<label for="{0}" style="{2}">{1}{4}</label>',
26247                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26248                         '</div>',
26249                     '</div><div class="x-form-clear-left"></div>'
26250                 );
26251                 t.disableFormats = true;
26252                 t.compile();
26253                 Roo.form.Layout.prototype.fieldTpl = t;
26254             }
26255             for(var i = 0; i < slen; i++) {
26256                 if(stack[i].isFormField){
26257                     this.renderField(stack[i]);
26258                 }else{
26259                     this.renderComponent(stack[i]);
26260                 }
26261             }
26262         }
26263         if(this.clear){
26264             this.el.createChild({cls:'x-form-clear'});
26265         }
26266     },
26267
26268     // private
26269     renderField : function(f){
26270         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26271                f.id, //0
26272                f.fieldLabel, //1
26273                f.labelStyle||this.labelStyle||'', //2
26274                this.elementStyle||'', //3
26275                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26276                f.itemCls||this.itemCls||''  //5
26277        ], true).getPrevSibling());
26278     },
26279
26280     // private
26281     renderComponent : function(c){
26282         c.render(c.isLayout ? this.el : this.el.createChild());    
26283     },
26284     /**
26285      * Adds a object form elements (using the xtype property as the factory method.)
26286      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26287      * @param {Object} config 
26288      */
26289     addxtype : function(o)
26290     {
26291         // create the lement.
26292         o.form = this.form;
26293         var fe = Roo.factory(o, Roo.form);
26294         this.form.allItems.push(fe);
26295         this.stack.push(fe);
26296         
26297         if (fe.isFormField) {
26298             this.form.items.add(fe);
26299         }
26300          
26301         return fe;
26302     }
26303 });
26304
26305 /**
26306  * @class Roo.form.Column
26307  * @extends Roo.form.Layout
26308  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26309  * @constructor
26310  * @param {Object} config Configuration options
26311  */
26312 Roo.form.Column = function(config){
26313     Roo.form.Column.superclass.constructor.call(this, config);
26314 };
26315
26316 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26317     /**
26318      * @cfg {Number/String} width
26319      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26320      */
26321     /**
26322      * @cfg {String/Object} autoCreate
26323      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26324      */
26325
26326     // private
26327     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26328
26329     // private
26330     onRender : function(ct, position){
26331         Roo.form.Column.superclass.onRender.call(this, ct, position);
26332         if(this.width){
26333             this.el.setWidth(this.width);
26334         }
26335     }
26336 });
26337
26338
26339 /**
26340  * @class Roo.form.Row
26341  * @extends Roo.form.Layout
26342  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26343  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26344  * @constructor
26345  * @param {Object} config Configuration options
26346  */
26347
26348  
26349 Roo.form.Row = function(config){
26350     Roo.form.Row.superclass.constructor.call(this, config);
26351 };
26352  
26353 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26354       /**
26355      * @cfg {Number/String} width
26356      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26357      */
26358     /**
26359      * @cfg {Number/String} height
26360      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26361      */
26362     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26363     
26364     padWidth : 20,
26365     // private
26366     onRender : function(ct, position){
26367         //console.log('row render');
26368         if(!this.rowTpl){
26369             var t = new Roo.Template(
26370                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26371                     '<label for="{0}" style="{2}">{1}{4}</label>',
26372                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26373                     '</div>',
26374                 '</div>'
26375             );
26376             t.disableFormats = true;
26377             t.compile();
26378             Roo.form.Layout.prototype.rowTpl = t;
26379         }
26380         this.fieldTpl = this.rowTpl;
26381         
26382         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26383         var labelWidth = 100;
26384         
26385         if ((this.labelAlign != 'top')) {
26386             if (typeof this.labelWidth == 'number') {
26387                 labelWidth = this.labelWidth
26388             }
26389             this.padWidth =  20 + labelWidth;
26390             
26391         }
26392         
26393         Roo.form.Column.superclass.onRender.call(this, ct, position);
26394         if(this.width){
26395             this.el.setWidth(this.width);
26396         }
26397         if(this.height){
26398             this.el.setHeight(this.height);
26399         }
26400     },
26401     
26402     // private
26403     renderField : function(f){
26404         f.fieldEl = this.fieldTpl.append(this.el, [
26405                f.id, f.fieldLabel,
26406                f.labelStyle||this.labelStyle||'',
26407                this.elementStyle||'',
26408                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26409                f.itemCls||this.itemCls||'',
26410                f.width ? f.width + this.padWidth : 160 + this.padWidth
26411        ],true);
26412     }
26413 });
26414  
26415
26416 /**
26417  * @class Roo.form.FieldSet
26418  * @extends Roo.form.Layout
26419  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26420  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26421  * @constructor
26422  * @param {Object} config Configuration options
26423  */
26424 Roo.form.FieldSet = function(config){
26425     Roo.form.FieldSet.superclass.constructor.call(this, config);
26426 };
26427
26428 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26429     /**
26430      * @cfg {String} legend
26431      * The text to display as the legend for the FieldSet (defaults to '')
26432      */
26433     /**
26434      * @cfg {String/Object} autoCreate
26435      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26436      */
26437
26438     // private
26439     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26440
26441     // private
26442     onRender : function(ct, position){
26443         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26444         if(this.legend){
26445             this.setLegend(this.legend);
26446         }
26447     },
26448
26449     // private
26450     setLegend : function(text){
26451         if(this.rendered){
26452             this.el.child('legend').update(text);
26453         }
26454     }
26455 });/*
26456  * Based on:
26457  * Ext JS Library 1.1.1
26458  * Copyright(c) 2006-2007, Ext JS, LLC.
26459  *
26460  * Originally Released Under LGPL - original licence link has changed is not relivant.
26461  *
26462  * Fork - LGPL
26463  * <script type="text/javascript">
26464  */
26465 /**
26466  * @class Roo.form.VTypes
26467  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26468  * @singleton
26469  */
26470 Roo.form.VTypes = function(){
26471     // closure these in so they are only created once.
26472     var alpha = /^[a-zA-Z_]+$/;
26473     var alphanum = /^[a-zA-Z0-9_]+$/;
26474     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26475     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26476
26477     // All these messages and functions are configurable
26478     return {
26479         /**
26480          * The function used to validate email addresses
26481          * @param {String} value The email address
26482          */
26483         'email' : function(v){
26484             return email.test(v);
26485         },
26486         /**
26487          * The error text to display when the email validation function returns false
26488          * @type String
26489          */
26490         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26491         /**
26492          * The keystroke filter mask to be applied on email input
26493          * @type RegExp
26494          */
26495         'emailMask' : /[a-z0-9_\.\-@]/i,
26496
26497         /**
26498          * The function used to validate URLs
26499          * @param {String} value The URL
26500          */
26501         'url' : function(v){
26502             return url.test(v);
26503         },
26504         /**
26505          * The error text to display when the url validation function returns false
26506          * @type String
26507          */
26508         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26509         
26510         /**
26511          * The function used to validate alpha values
26512          * @param {String} value The value
26513          */
26514         'alpha' : function(v){
26515             return alpha.test(v);
26516         },
26517         /**
26518          * The error text to display when the alpha validation function returns false
26519          * @type String
26520          */
26521         'alphaText' : 'This field should only contain letters and _',
26522         /**
26523          * The keystroke filter mask to be applied on alpha input
26524          * @type RegExp
26525          */
26526         'alphaMask' : /[a-z_]/i,
26527
26528         /**
26529          * The function used to validate alphanumeric values
26530          * @param {String} value The value
26531          */
26532         'alphanum' : function(v){
26533             return alphanum.test(v);
26534         },
26535         /**
26536          * The error text to display when the alphanumeric validation function returns false
26537          * @type String
26538          */
26539         'alphanumText' : 'This field should only contain letters, numbers and _',
26540         /**
26541          * The keystroke filter mask to be applied on alphanumeric input
26542          * @type RegExp
26543          */
26544         'alphanumMask' : /[a-z0-9_]/i
26545     };
26546 }();//<script type="text/javascript">
26547
26548 /**
26549  * @class Roo.form.FCKeditor
26550  * @extends Roo.form.TextArea
26551  * Wrapper around the FCKEditor http://www.fckeditor.net
26552  * @constructor
26553  * Creates a new FCKeditor
26554  * @param {Object} config Configuration options
26555  */
26556 Roo.form.FCKeditor = function(config){
26557     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26558     this.addEvents({
26559          /**
26560          * @event editorinit
26561          * Fired when the editor is initialized - you can add extra handlers here..
26562          * @param {FCKeditor} this
26563          * @param {Object} the FCK object.
26564          */
26565         editorinit : true
26566     });
26567     
26568     
26569 };
26570 Roo.form.FCKeditor.editors = { };
26571 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26572 {
26573     //defaultAutoCreate : {
26574     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26575     //},
26576     // private
26577     /**
26578      * @cfg {Object} fck options - see fck manual for details.
26579      */
26580     fckconfig : false,
26581     
26582     /**
26583      * @cfg {Object} fck toolbar set (Basic or Default)
26584      */
26585     toolbarSet : 'Basic',
26586     /**
26587      * @cfg {Object} fck BasePath
26588      */ 
26589     basePath : '/fckeditor/',
26590     
26591     
26592     frame : false,
26593     
26594     value : '',
26595     
26596    
26597     onRender : function(ct, position)
26598     {
26599         if(!this.el){
26600             this.defaultAutoCreate = {
26601                 tag: "textarea",
26602                 style:"width:300px;height:60px;",
26603                 autocomplete: "new-password"
26604             };
26605         }
26606         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26607         /*
26608         if(this.grow){
26609             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26610             if(this.preventScrollbars){
26611                 this.el.setStyle("overflow", "hidden");
26612             }
26613             this.el.setHeight(this.growMin);
26614         }
26615         */
26616         //console.log('onrender' + this.getId() );
26617         Roo.form.FCKeditor.editors[this.getId()] = this;
26618          
26619
26620         this.replaceTextarea() ;
26621         
26622     },
26623     
26624     getEditor : function() {
26625         return this.fckEditor;
26626     },
26627     /**
26628      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26629      * @param {Mixed} value The value to set
26630      */
26631     
26632     
26633     setValue : function(value)
26634     {
26635         //console.log('setValue: ' + value);
26636         
26637         if(typeof(value) == 'undefined') { // not sure why this is happending...
26638             return;
26639         }
26640         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26641         
26642         //if(!this.el || !this.getEditor()) {
26643         //    this.value = value;
26644             //this.setValue.defer(100,this,[value]);    
26645         //    return;
26646         //} 
26647         
26648         if(!this.getEditor()) {
26649             return;
26650         }
26651         
26652         this.getEditor().SetData(value);
26653         
26654         //
26655
26656     },
26657
26658     /**
26659      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26660      * @return {Mixed} value The field value
26661      */
26662     getValue : function()
26663     {
26664         
26665         if (this.frame && this.frame.dom.style.display == 'none') {
26666             return Roo.form.FCKeditor.superclass.getValue.call(this);
26667         }
26668         
26669         if(!this.el || !this.getEditor()) {
26670            
26671            // this.getValue.defer(100,this); 
26672             return this.value;
26673         }
26674        
26675         
26676         var value=this.getEditor().GetData();
26677         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26678         return Roo.form.FCKeditor.superclass.getValue.call(this);
26679         
26680
26681     },
26682
26683     /**
26684      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26685      * @return {Mixed} value The field value
26686      */
26687     getRawValue : function()
26688     {
26689         if (this.frame && this.frame.dom.style.display == 'none') {
26690             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26691         }
26692         
26693         if(!this.el || !this.getEditor()) {
26694             //this.getRawValue.defer(100,this); 
26695             return this.value;
26696             return;
26697         }
26698         
26699         
26700         
26701         var value=this.getEditor().GetData();
26702         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26703         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26704          
26705     },
26706     
26707     setSize : function(w,h) {
26708         
26709         
26710         
26711         //if (this.frame && this.frame.dom.style.display == 'none') {
26712         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26713         //    return;
26714         //}
26715         //if(!this.el || !this.getEditor()) {
26716         //    this.setSize.defer(100,this, [w,h]); 
26717         //    return;
26718         //}
26719         
26720         
26721         
26722         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26723         
26724         this.frame.dom.setAttribute('width', w);
26725         this.frame.dom.setAttribute('height', h);
26726         this.frame.setSize(w,h);
26727         
26728     },
26729     
26730     toggleSourceEdit : function(value) {
26731         
26732       
26733          
26734         this.el.dom.style.display = value ? '' : 'none';
26735         this.frame.dom.style.display = value ?  'none' : '';
26736         
26737     },
26738     
26739     
26740     focus: function(tag)
26741     {
26742         if (this.frame.dom.style.display == 'none') {
26743             return Roo.form.FCKeditor.superclass.focus.call(this);
26744         }
26745         if(!this.el || !this.getEditor()) {
26746             this.focus.defer(100,this, [tag]); 
26747             return;
26748         }
26749         
26750         
26751         
26752         
26753         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26754         this.getEditor().Focus();
26755         if (tgs.length) {
26756             if (!this.getEditor().Selection.GetSelection()) {
26757                 this.focus.defer(100,this, [tag]); 
26758                 return;
26759             }
26760             
26761             
26762             var r = this.getEditor().EditorDocument.createRange();
26763             r.setStart(tgs[0],0);
26764             r.setEnd(tgs[0],0);
26765             this.getEditor().Selection.GetSelection().removeAllRanges();
26766             this.getEditor().Selection.GetSelection().addRange(r);
26767             this.getEditor().Focus();
26768         }
26769         
26770     },
26771     
26772     
26773     
26774     replaceTextarea : function()
26775     {
26776         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26777             return ;
26778         }
26779         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26780         //{
26781             // We must check the elements firstly using the Id and then the name.
26782         var oTextarea = document.getElementById( this.getId() );
26783         
26784         var colElementsByName = document.getElementsByName( this.getId() ) ;
26785          
26786         oTextarea.style.display = 'none' ;
26787
26788         if ( oTextarea.tabIndex ) {            
26789             this.TabIndex = oTextarea.tabIndex ;
26790         }
26791         
26792         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26793         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26794         this.frame = Roo.get(this.getId() + '___Frame')
26795     },
26796     
26797     _getConfigHtml : function()
26798     {
26799         var sConfig = '' ;
26800
26801         for ( var o in this.fckconfig ) {
26802             sConfig += sConfig.length > 0  ? '&amp;' : '';
26803             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26804         }
26805
26806         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26807     },
26808     
26809     
26810     _getIFrameHtml : function()
26811     {
26812         var sFile = 'fckeditor.html' ;
26813         /* no idea what this is about..
26814         try
26815         {
26816             if ( (/fcksource=true/i).test( window.top.location.search ) )
26817                 sFile = 'fckeditor.original.html' ;
26818         }
26819         catch (e) { 
26820         */
26821
26822         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26823         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26824         
26825         
26826         var html = '<iframe id="' + this.getId() +
26827             '___Frame" src="' + sLink +
26828             '" width="' + this.width +
26829             '" height="' + this.height + '"' +
26830             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26831             ' frameborder="0" scrolling="no"></iframe>' ;
26832
26833         return html ;
26834     },
26835     
26836     _insertHtmlBefore : function( html, element )
26837     {
26838         if ( element.insertAdjacentHTML )       {
26839             // IE
26840             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26841         } else { // Gecko
26842             var oRange = document.createRange() ;
26843             oRange.setStartBefore( element ) ;
26844             var oFragment = oRange.createContextualFragment( html );
26845             element.parentNode.insertBefore( oFragment, element ) ;
26846         }
26847     }
26848     
26849     
26850   
26851     
26852     
26853     
26854     
26855
26856 });
26857
26858 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26859
26860 function FCKeditor_OnComplete(editorInstance){
26861     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26862     f.fckEditor = editorInstance;
26863     //console.log("loaded");
26864     f.fireEvent('editorinit', f, editorInstance);
26865
26866   
26867
26868  
26869
26870
26871
26872
26873
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884 //<script type="text/javascript">
26885 /**
26886  * @class Roo.form.GridField
26887  * @extends Roo.form.Field
26888  * Embed a grid (or editable grid into a form)
26889  * STATUS ALPHA
26890  * 
26891  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26892  * it needs 
26893  * xgrid.store = Roo.data.Store
26894  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26895  * xgrid.store.reader = Roo.data.JsonReader 
26896  * 
26897  * 
26898  * @constructor
26899  * Creates a new GridField
26900  * @param {Object} config Configuration options
26901  */
26902 Roo.form.GridField = function(config){
26903     Roo.form.GridField.superclass.constructor.call(this, config);
26904      
26905 };
26906
26907 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26908     /**
26909      * @cfg {Number} width  - used to restrict width of grid..
26910      */
26911     width : 100,
26912     /**
26913      * @cfg {Number} height - used to restrict height of grid..
26914      */
26915     height : 50,
26916      /**
26917      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26918          * 
26919          *}
26920      */
26921     xgrid : false, 
26922     /**
26923      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26924      * {tag: "input", type: "checkbox", autocomplete: "off"})
26925      */
26926    // defaultAutoCreate : { tag: 'div' },
26927     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26928     /**
26929      * @cfg {String} addTitle Text to include for adding a title.
26930      */
26931     addTitle : false,
26932     //
26933     onResize : function(){
26934         Roo.form.Field.superclass.onResize.apply(this, arguments);
26935     },
26936
26937     initEvents : function(){
26938         // Roo.form.Checkbox.superclass.initEvents.call(this);
26939         // has no events...
26940        
26941     },
26942
26943
26944     getResizeEl : function(){
26945         return this.wrap;
26946     },
26947
26948     getPositionEl : function(){
26949         return this.wrap;
26950     },
26951
26952     // private
26953     onRender : function(ct, position){
26954         
26955         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26956         var style = this.style;
26957         delete this.style;
26958         
26959         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26960         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26961         this.viewEl = this.wrap.createChild({ tag: 'div' });
26962         if (style) {
26963             this.viewEl.applyStyles(style);
26964         }
26965         if (this.width) {
26966             this.viewEl.setWidth(this.width);
26967         }
26968         if (this.height) {
26969             this.viewEl.setHeight(this.height);
26970         }
26971         //if(this.inputValue !== undefined){
26972         //this.setValue(this.value);
26973         
26974         
26975         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26976         
26977         
26978         this.grid.render();
26979         this.grid.getDataSource().on('remove', this.refreshValue, this);
26980         this.grid.getDataSource().on('update', this.refreshValue, this);
26981         this.grid.on('afteredit', this.refreshValue, this);
26982  
26983     },
26984      
26985     
26986     /**
26987      * Sets the value of the item. 
26988      * @param {String} either an object  or a string..
26989      */
26990     setValue : function(v){
26991         //this.value = v;
26992         v = v || []; // empty set..
26993         // this does not seem smart - it really only affects memoryproxy grids..
26994         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26995             var ds = this.grid.getDataSource();
26996             // assumes a json reader..
26997             var data = {}
26998             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26999             ds.loadData( data);
27000         }
27001         // clear selection so it does not get stale.
27002         if (this.grid.sm) { 
27003             this.grid.sm.clearSelections();
27004         }
27005         
27006         Roo.form.GridField.superclass.setValue.call(this, v);
27007         this.refreshValue();
27008         // should load data in the grid really....
27009     },
27010     
27011     // private
27012     refreshValue: function() {
27013          var val = [];
27014         this.grid.getDataSource().each(function(r) {
27015             val.push(r.data);
27016         });
27017         this.el.dom.value = Roo.encode(val);
27018     }
27019     
27020      
27021     
27022     
27023 });/*
27024  * Based on:
27025  * Ext JS Library 1.1.1
27026  * Copyright(c) 2006-2007, Ext JS, LLC.
27027  *
27028  * Originally Released Under LGPL - original licence link has changed is not relivant.
27029  *
27030  * Fork - LGPL
27031  * <script type="text/javascript">
27032  */
27033 /**
27034  * @class Roo.form.DisplayField
27035  * @extends Roo.form.Field
27036  * A generic Field to display non-editable data.
27037  * @cfg {Boolean} closable (true|false) default false
27038  * @constructor
27039  * Creates a new Display Field item.
27040  * @param {Object} config Configuration options
27041  */
27042 Roo.form.DisplayField = function(config){
27043     Roo.form.DisplayField.superclass.constructor.call(this, config);
27044     
27045     this.addEvents({
27046         /**
27047          * @event close
27048          * Fires after the click the close btn
27049              * @param {Roo.form.DisplayField} this
27050              */
27051         close : true
27052     });
27053 };
27054
27055 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27056     inputType:      'hidden',
27057     allowBlank:     true,
27058     readOnly:         true,
27059     
27060  
27061     /**
27062      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27063      */
27064     focusClass : undefined,
27065     /**
27066      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27067      */
27068     fieldClass: 'x-form-field',
27069     
27070      /**
27071      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27072      */
27073     valueRenderer: undefined,
27074     
27075     width: 100,
27076     /**
27077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27078      * {tag: "input", type: "checkbox", autocomplete: "off"})
27079      */
27080      
27081  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27082  
27083     closable : false,
27084     
27085     onResize : function(){
27086         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27087         
27088     },
27089
27090     initEvents : function(){
27091         // Roo.form.Checkbox.superclass.initEvents.call(this);
27092         // has no events...
27093         
27094         if(this.closable){
27095             this.closeEl.on('click', this.onClose, this);
27096         }
27097        
27098     },
27099
27100
27101     getResizeEl : function(){
27102         return this.wrap;
27103     },
27104
27105     getPositionEl : function(){
27106         return this.wrap;
27107     },
27108
27109     // private
27110     onRender : function(ct, position){
27111         
27112         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27113         //if(this.inputValue !== undefined){
27114         this.wrap = this.el.wrap();
27115         
27116         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27117         
27118         if(this.closable){
27119             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27120         }
27121         
27122         if (this.bodyStyle) {
27123             this.viewEl.applyStyles(this.bodyStyle);
27124         }
27125         //this.viewEl.setStyle('padding', '2px');
27126         
27127         this.setValue(this.value);
27128         
27129     },
27130 /*
27131     // private
27132     initValue : Roo.emptyFn,
27133
27134   */
27135
27136         // private
27137     onClick : function(){
27138         
27139     },
27140
27141     /**
27142      * Sets the checked state of the checkbox.
27143      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27144      */
27145     setValue : function(v){
27146         this.value = v;
27147         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27148         // this might be called before we have a dom element..
27149         if (!this.viewEl) {
27150             return;
27151         }
27152         this.viewEl.dom.innerHTML = html;
27153         Roo.form.DisplayField.superclass.setValue.call(this, v);
27154
27155     },
27156     
27157     onClose : function(e)
27158     {
27159         e.preventDefault();
27160         
27161         this.fireEvent('close', this);
27162     }
27163 });/*
27164  * 
27165  * Licence- LGPL
27166  * 
27167  */
27168
27169 /**
27170  * @class Roo.form.DayPicker
27171  * @extends Roo.form.Field
27172  * A Day picker show [M] [T] [W] ....
27173  * @constructor
27174  * Creates a new Day Picker
27175  * @param {Object} config Configuration options
27176  */
27177 Roo.form.DayPicker= function(config){
27178     Roo.form.DayPicker.superclass.constructor.call(this, config);
27179      
27180 };
27181
27182 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27183     /**
27184      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27185      */
27186     focusClass : undefined,
27187     /**
27188      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27189      */
27190     fieldClass: "x-form-field",
27191    
27192     /**
27193      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27194      * {tag: "input", type: "checkbox", autocomplete: "off"})
27195      */
27196     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27197     
27198    
27199     actionMode : 'viewEl', 
27200     //
27201     // private
27202  
27203     inputType : 'hidden',
27204     
27205      
27206     inputElement: false, // real input element?
27207     basedOn: false, // ????
27208     
27209     isFormField: true, // not sure where this is needed!!!!
27210
27211     onResize : function(){
27212         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27213         if(!this.boxLabel){
27214             this.el.alignTo(this.wrap, 'c-c');
27215         }
27216     },
27217
27218     initEvents : function(){
27219         Roo.form.Checkbox.superclass.initEvents.call(this);
27220         this.el.on("click", this.onClick,  this);
27221         this.el.on("change", this.onClick,  this);
27222     },
27223
27224
27225     getResizeEl : function(){
27226         return this.wrap;
27227     },
27228
27229     getPositionEl : function(){
27230         return this.wrap;
27231     },
27232
27233     
27234     // private
27235     onRender : function(ct, position){
27236         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27237        
27238         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27239         
27240         var r1 = '<table><tr>';
27241         var r2 = '<tr class="x-form-daypick-icons">';
27242         for (var i=0; i < 7; i++) {
27243             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27244             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27245         }
27246         
27247         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27248         viewEl.select('img').on('click', this.onClick, this);
27249         this.viewEl = viewEl;   
27250         
27251         
27252         // this will not work on Chrome!!!
27253         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27254         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27255         
27256         
27257           
27258
27259     },
27260
27261     // private
27262     initValue : Roo.emptyFn,
27263
27264     /**
27265      * Returns the checked state of the checkbox.
27266      * @return {Boolean} True if checked, else false
27267      */
27268     getValue : function(){
27269         return this.el.dom.value;
27270         
27271     },
27272
27273         // private
27274     onClick : function(e){ 
27275         //this.setChecked(!this.checked);
27276         Roo.get(e.target).toggleClass('x-menu-item-checked');
27277         this.refreshValue();
27278         //if(this.el.dom.checked != this.checked){
27279         //    this.setValue(this.el.dom.checked);
27280        // }
27281     },
27282     
27283     // private
27284     refreshValue : function()
27285     {
27286         var val = '';
27287         this.viewEl.select('img',true).each(function(e,i,n)  {
27288             val += e.is(".x-menu-item-checked") ? String(n) : '';
27289         });
27290         this.setValue(val, true);
27291     },
27292
27293     /**
27294      * Sets the checked state of the checkbox.
27295      * On is always based on a string comparison between inputValue and the param.
27296      * @param {Boolean/String} value - the value to set 
27297      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27298      */
27299     setValue : function(v,suppressEvent){
27300         if (!this.el.dom) {
27301             return;
27302         }
27303         var old = this.el.dom.value ;
27304         this.el.dom.value = v;
27305         if (suppressEvent) {
27306             return ;
27307         }
27308          
27309         // update display..
27310         this.viewEl.select('img',true).each(function(e,i,n)  {
27311             
27312             var on = e.is(".x-menu-item-checked");
27313             var newv = v.indexOf(String(n)) > -1;
27314             if (on != newv) {
27315                 e.toggleClass('x-menu-item-checked');
27316             }
27317             
27318         });
27319         
27320         
27321         this.fireEvent('change', this, v, old);
27322         
27323         
27324     },
27325    
27326     // handle setting of hidden value by some other method!!?!?
27327     setFromHidden: function()
27328     {
27329         if(!this.el){
27330             return;
27331         }
27332         //console.log("SET FROM HIDDEN");
27333         //alert('setFrom hidden');
27334         this.setValue(this.el.dom.value);
27335     },
27336     
27337     onDestroy : function()
27338     {
27339         if(this.viewEl){
27340             Roo.get(this.viewEl).remove();
27341         }
27342          
27343         Roo.form.DayPicker.superclass.onDestroy.call(this);
27344     }
27345
27346 });/*
27347  * RooJS Library 1.1.1
27348  * Copyright(c) 2008-2011  Alan Knowles
27349  *
27350  * License - LGPL
27351  */
27352  
27353
27354 /**
27355  * @class Roo.form.ComboCheck
27356  * @extends Roo.form.ComboBox
27357  * A combobox for multiple select items.
27358  *
27359  * FIXME - could do with a reset button..
27360  * 
27361  * @constructor
27362  * Create a new ComboCheck
27363  * @param {Object} config Configuration options
27364  */
27365 Roo.form.ComboCheck = function(config){
27366     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27367     // should verify some data...
27368     // like
27369     // hiddenName = required..
27370     // displayField = required
27371     // valudField == required
27372     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27373     var _t = this;
27374     Roo.each(req, function(e) {
27375         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27376             throw "Roo.form.ComboCheck : missing value for: " + e;
27377         }
27378     });
27379     
27380     
27381 };
27382
27383 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27384      
27385      
27386     editable : false,
27387      
27388     selectedClass: 'x-menu-item-checked', 
27389     
27390     // private
27391     onRender : function(ct, position){
27392         var _t = this;
27393         
27394         
27395         
27396         if(!this.tpl){
27397             var cls = 'x-combo-list';
27398
27399             
27400             this.tpl =  new Roo.Template({
27401                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27402                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27403                    '<span>{' + this.displayField + '}</span>' +
27404                     '</div>' 
27405                 
27406             });
27407         }
27408  
27409         
27410         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27411         this.view.singleSelect = false;
27412         this.view.multiSelect = true;
27413         this.view.toggleSelect = true;
27414         this.pageTb.add(new Roo.Toolbar.Fill(), {
27415             
27416             text: 'Done',
27417             handler: function()
27418             {
27419                 _t.collapse();
27420             }
27421         });
27422     },
27423     
27424     onViewOver : function(e, t){
27425         // do nothing...
27426         return;
27427         
27428     },
27429     
27430     onViewClick : function(doFocus,index){
27431         return;
27432         
27433     },
27434     select: function () {
27435         //Roo.log("SELECT CALLED");
27436     },
27437      
27438     selectByValue : function(xv, scrollIntoView){
27439         var ar = this.getValueArray();
27440         var sels = [];
27441         
27442         Roo.each(ar, function(v) {
27443             if(v === undefined || v === null){
27444                 return;
27445             }
27446             var r = this.findRecord(this.valueField, v);
27447             if(r){
27448                 sels.push(this.store.indexOf(r))
27449                 
27450             }
27451         },this);
27452         this.view.select(sels);
27453         return false;
27454     },
27455     
27456     
27457     
27458     onSelect : function(record, index){
27459        // Roo.log("onselect Called");
27460        // this is only called by the clear button now..
27461         this.view.clearSelections();
27462         this.setValue('[]');
27463         if (this.value != this.valueBefore) {
27464             this.fireEvent('change', this, this.value, this.valueBefore);
27465             this.valueBefore = this.value;
27466         }
27467     },
27468     getValueArray : function()
27469     {
27470         var ar = [] ;
27471         
27472         try {
27473             //Roo.log(this.value);
27474             if (typeof(this.value) == 'undefined') {
27475                 return [];
27476             }
27477             var ar = Roo.decode(this.value);
27478             return  ar instanceof Array ? ar : []; //?? valid?
27479             
27480         } catch(e) {
27481             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27482             return [];
27483         }
27484          
27485     },
27486     expand : function ()
27487     {
27488         
27489         Roo.form.ComboCheck.superclass.expand.call(this);
27490         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27491         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27492         
27493
27494     },
27495     
27496     collapse : function(){
27497         Roo.form.ComboCheck.superclass.collapse.call(this);
27498         var sl = this.view.getSelectedIndexes();
27499         var st = this.store;
27500         var nv = [];
27501         var tv = [];
27502         var r;
27503         Roo.each(sl, function(i) {
27504             r = st.getAt(i);
27505             nv.push(r.get(this.valueField));
27506         },this);
27507         this.setValue(Roo.encode(nv));
27508         if (this.value != this.valueBefore) {
27509
27510             this.fireEvent('change', this, this.value, this.valueBefore);
27511             this.valueBefore = this.value;
27512         }
27513         
27514     },
27515     
27516     setValue : function(v){
27517         // Roo.log(v);
27518         this.value = v;
27519         
27520         var vals = this.getValueArray();
27521         var tv = [];
27522         Roo.each(vals, function(k) {
27523             var r = this.findRecord(this.valueField, k);
27524             if(r){
27525                 tv.push(r.data[this.displayField]);
27526             }else if(this.valueNotFoundText !== undefined){
27527                 tv.push( this.valueNotFoundText );
27528             }
27529         },this);
27530        // Roo.log(tv);
27531         
27532         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27533         this.hiddenField.value = v;
27534         this.value = v;
27535     }
27536     
27537 });/*
27538  * Based on:
27539  * Ext JS Library 1.1.1
27540  * Copyright(c) 2006-2007, Ext JS, LLC.
27541  *
27542  * Originally Released Under LGPL - original licence link has changed is not relivant.
27543  *
27544  * Fork - LGPL
27545  * <script type="text/javascript">
27546  */
27547  
27548 /**
27549  * @class Roo.form.Signature
27550  * @extends Roo.form.Field
27551  * Signature field.  
27552  * @constructor
27553  * 
27554  * @param {Object} config Configuration options
27555  */
27556
27557 Roo.form.Signature = function(config){
27558     Roo.form.Signature.superclass.constructor.call(this, config);
27559     
27560     this.addEvents({// not in used??
27561          /**
27562          * @event confirm
27563          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27564              * @param {Roo.form.Signature} combo This combo box
27565              */
27566         'confirm' : true,
27567         /**
27568          * @event reset
27569          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27570              * @param {Roo.form.ComboBox} combo This combo box
27571              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27572              */
27573         'reset' : true
27574     });
27575 };
27576
27577 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27578     /**
27579      * @cfg {Object} labels Label to use when rendering a form.
27580      * defaults to 
27581      * labels : { 
27582      *      clear : "Clear",
27583      *      confirm : "Confirm"
27584      *  }
27585      */
27586     labels : { 
27587         clear : "Clear",
27588         confirm : "Confirm"
27589     },
27590     /**
27591      * @cfg {Number} width The signature panel width (defaults to 300)
27592      */
27593     width: 300,
27594     /**
27595      * @cfg {Number} height The signature panel height (defaults to 100)
27596      */
27597     height : 100,
27598     /**
27599      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27600      */
27601     allowBlank : false,
27602     
27603     //private
27604     // {Object} signPanel The signature SVG panel element (defaults to {})
27605     signPanel : {},
27606     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27607     isMouseDown : false,
27608     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27609     isConfirmed : false,
27610     // {String} signatureTmp SVG mapping string (defaults to empty string)
27611     signatureTmp : '',
27612     
27613     
27614     defaultAutoCreate : { // modified by initCompnoent..
27615         tag: "input",
27616         type:"hidden"
27617     },
27618
27619     // private
27620     onRender : function(ct, position){
27621         
27622         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27623         
27624         this.wrap = this.el.wrap({
27625             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27626         });
27627         
27628         this.createToolbar(this);
27629         this.signPanel = this.wrap.createChild({
27630                 tag: 'div',
27631                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27632             }, this.el
27633         );
27634             
27635         this.svgID = Roo.id();
27636         this.svgEl = this.signPanel.createChild({
27637               xmlns : 'http://www.w3.org/2000/svg',
27638               tag : 'svg',
27639               id : this.svgID + "-svg",
27640               width: this.width,
27641               height: this.height,
27642               viewBox: '0 0 '+this.width+' '+this.height,
27643               cn : [
27644                 {
27645                     tag: "rect",
27646                     id: this.svgID + "-svg-r",
27647                     width: this.width,
27648                     height: this.height,
27649                     fill: "#ffa"
27650                 },
27651                 {
27652                     tag: "line",
27653                     id: this.svgID + "-svg-l",
27654                     x1: "0", // start
27655                     y1: (this.height*0.8), // start set the line in 80% of height
27656                     x2: this.width, // end
27657                     y2: (this.height*0.8), // end set the line in 80% of height
27658                     'stroke': "#666",
27659                     'stroke-width': "1",
27660                     'stroke-dasharray': "3",
27661                     'shape-rendering': "crispEdges",
27662                     'pointer-events': "none"
27663                 },
27664                 {
27665                     tag: "path",
27666                     id: this.svgID + "-svg-p",
27667                     'stroke': "navy",
27668                     'stroke-width': "3",
27669                     'fill': "none",
27670                     'pointer-events': 'none'
27671                 }
27672               ]
27673         });
27674         this.createSVG();
27675         this.svgBox = this.svgEl.dom.getScreenCTM();
27676     },
27677     createSVG : function(){ 
27678         var svg = this.signPanel;
27679         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27680         var t = this;
27681
27682         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27683         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27684         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27685         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27686         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27687         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27688         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27689         
27690     },
27691     isTouchEvent : function(e){
27692         return e.type.match(/^touch/);
27693     },
27694     getCoords : function (e) {
27695         var pt    = this.svgEl.dom.createSVGPoint();
27696         pt.x = e.clientX; 
27697         pt.y = e.clientY;
27698         if (this.isTouchEvent(e)) {
27699             pt.x =  e.targetTouches[0].clientX;
27700             pt.y = e.targetTouches[0].clientY;
27701         }
27702         var a = this.svgEl.dom.getScreenCTM();
27703         var b = a.inverse();
27704         var mx = pt.matrixTransform(b);
27705         return mx.x + ',' + mx.y;
27706     },
27707     //mouse event headler 
27708     down : function (e) {
27709         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27710         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27711         
27712         this.isMouseDown = true;
27713         
27714         e.preventDefault();
27715     },
27716     move : function (e) {
27717         if (this.isMouseDown) {
27718             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27719             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27720         }
27721         
27722         e.preventDefault();
27723     },
27724     up : function (e) {
27725         this.isMouseDown = false;
27726         var sp = this.signatureTmp.split(' ');
27727         
27728         if(sp.length > 1){
27729             if(!sp[sp.length-2].match(/^L/)){
27730                 sp.pop();
27731                 sp.pop();
27732                 sp.push("");
27733                 this.signatureTmp = sp.join(" ");
27734             }
27735         }
27736         if(this.getValue() != this.signatureTmp){
27737             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27738             this.isConfirmed = false;
27739         }
27740         e.preventDefault();
27741     },
27742     
27743     /**
27744      * Protected method that will not generally be called directly. It
27745      * is called when the editor creates its toolbar. Override this method if you need to
27746      * add custom toolbar buttons.
27747      * @param {HtmlEditor} editor
27748      */
27749     createToolbar : function(editor){
27750          function btn(id, toggle, handler){
27751             var xid = fid + '-'+ id ;
27752             return {
27753                 id : xid,
27754                 cmd : id,
27755                 cls : 'x-btn-icon x-edit-'+id,
27756                 enableToggle:toggle !== false,
27757                 scope: editor, // was editor...
27758                 handler:handler||editor.relayBtnCmd,
27759                 clickEvent:'mousedown',
27760                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27761                 tabIndex:-1
27762             };
27763         }
27764         
27765         
27766         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27767         this.tb = tb;
27768         this.tb.add(
27769            {
27770                 cls : ' x-signature-btn x-signature-'+id,
27771                 scope: editor, // was editor...
27772                 handler: this.reset,
27773                 clickEvent:'mousedown',
27774                 text: this.labels.clear
27775             },
27776             {
27777                  xtype : 'Fill',
27778                  xns: Roo.Toolbar
27779             }, 
27780             {
27781                 cls : '  x-signature-btn x-signature-'+id,
27782                 scope: editor, // was editor...
27783                 handler: this.confirmHandler,
27784                 clickEvent:'mousedown',
27785                 text: this.labels.confirm
27786             }
27787         );
27788     
27789     },
27790     //public
27791     /**
27792      * when user is clicked confirm then show this image.....
27793      * 
27794      * @return {String} Image Data URI
27795      */
27796     getImageDataURI : function(){
27797         var svg = this.svgEl.dom.parentNode.innerHTML;
27798         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27799         return src; 
27800     },
27801     /**
27802      * 
27803      * @return {Boolean} this.isConfirmed
27804      */
27805     getConfirmed : function(){
27806         return this.isConfirmed;
27807     },
27808     /**
27809      * 
27810      * @return {Number} this.width
27811      */
27812     getWidth : function(){
27813         return this.width;
27814     },
27815     /**
27816      * 
27817      * @return {Number} this.height
27818      */
27819     getHeight : function(){
27820         return this.height;
27821     },
27822     // private
27823     getSignature : function(){
27824         return this.signatureTmp;
27825     },
27826     // private
27827     reset : function(){
27828         this.signatureTmp = '';
27829         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27830         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27831         this.isConfirmed = false;
27832         Roo.form.Signature.superclass.reset.call(this);
27833     },
27834     setSignature : function(s){
27835         this.signatureTmp = s;
27836         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27837         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27838         this.setValue(s);
27839         this.isConfirmed = false;
27840         Roo.form.Signature.superclass.reset.call(this);
27841     }, 
27842     test : function(){
27843 //        Roo.log(this.signPanel.dom.contentWindow.up())
27844     },
27845     //private
27846     setConfirmed : function(){
27847         
27848         
27849         
27850 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27851     },
27852     // private
27853     confirmHandler : function(){
27854         if(!this.getSignature()){
27855             return;
27856         }
27857         
27858         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27859         this.setValue(this.getSignature());
27860         this.isConfirmed = true;
27861         
27862         this.fireEvent('confirm', this);
27863     },
27864     // private
27865     // Subclasses should provide the validation implementation by overriding this
27866     validateValue : function(value){
27867         if(this.allowBlank){
27868             return true;
27869         }
27870         
27871         if(this.isConfirmed){
27872             return true;
27873         }
27874         return false;
27875     }
27876 });/*
27877  * Based on:
27878  * Ext JS Library 1.1.1
27879  * Copyright(c) 2006-2007, Ext JS, LLC.
27880  *
27881  * Originally Released Under LGPL - original licence link has changed is not relivant.
27882  *
27883  * Fork - LGPL
27884  * <script type="text/javascript">
27885  */
27886  
27887
27888 /**
27889  * @class Roo.form.ComboBox
27890  * @extends Roo.form.TriggerField
27891  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27892  * @constructor
27893  * Create a new ComboBox.
27894  * @param {Object} config Configuration options
27895  */
27896 Roo.form.Select = function(config){
27897     Roo.form.Select.superclass.constructor.call(this, config);
27898      
27899 };
27900
27901 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27902     /**
27903      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27904      */
27905     /**
27906      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27907      * rendering into an Roo.Editor, defaults to false)
27908      */
27909     /**
27910      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27911      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27912      */
27913     /**
27914      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27915      */
27916     /**
27917      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27918      * the dropdown list (defaults to undefined, with no header element)
27919      */
27920
27921      /**
27922      * @cfg {String/Roo.Template} tpl The template to use to render the output
27923      */
27924      
27925     // private
27926     defaultAutoCreate : {tag: "select"  },
27927     /**
27928      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27929      */
27930     listWidth: undefined,
27931     /**
27932      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27933      * mode = 'remote' or 'text' if mode = 'local')
27934      */
27935     displayField: undefined,
27936     /**
27937      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27938      * mode = 'remote' or 'value' if mode = 'local'). 
27939      * Note: use of a valueField requires the user make a selection
27940      * in order for a value to be mapped.
27941      */
27942     valueField: undefined,
27943     
27944     
27945     /**
27946      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27947      * field's data value (defaults to the underlying DOM element's name)
27948      */
27949     hiddenName: undefined,
27950     /**
27951      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27952      */
27953     listClass: '',
27954     /**
27955      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27956      */
27957     selectedClass: 'x-combo-selected',
27958     /**
27959      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27960      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27961      * which displays a downward arrow icon).
27962      */
27963     triggerClass : 'x-form-arrow-trigger',
27964     /**
27965      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27966      */
27967     shadow:'sides',
27968     /**
27969      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27970      * anchor positions (defaults to 'tl-bl')
27971      */
27972     listAlign: 'tl-bl?',
27973     /**
27974      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27975      */
27976     maxHeight: 300,
27977     /**
27978      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27979      * query specified by the allQuery config option (defaults to 'query')
27980      */
27981     triggerAction: 'query',
27982     /**
27983      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27984      * (defaults to 4, does not apply if editable = false)
27985      */
27986     minChars : 4,
27987     /**
27988      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27989      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27990      */
27991     typeAhead: false,
27992     /**
27993      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27994      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27995      */
27996     queryDelay: 500,
27997     /**
27998      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27999      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28000      */
28001     pageSize: 0,
28002     /**
28003      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28004      * when editable = true (defaults to false)
28005      */
28006     selectOnFocus:false,
28007     /**
28008      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28009      */
28010     queryParam: 'query',
28011     /**
28012      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28013      * when mode = 'remote' (defaults to 'Loading...')
28014      */
28015     loadingText: 'Loading...',
28016     /**
28017      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28018      */
28019     resizable: false,
28020     /**
28021      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28022      */
28023     handleHeight : 8,
28024     /**
28025      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28026      * traditional select (defaults to true)
28027      */
28028     editable: true,
28029     /**
28030      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28031      */
28032     allQuery: '',
28033     /**
28034      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28035      */
28036     mode: 'remote',
28037     /**
28038      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28039      * listWidth has a higher value)
28040      */
28041     minListWidth : 70,
28042     /**
28043      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28044      * allow the user to set arbitrary text into the field (defaults to false)
28045      */
28046     forceSelection:false,
28047     /**
28048      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28049      * if typeAhead = true (defaults to 250)
28050      */
28051     typeAheadDelay : 250,
28052     /**
28053      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28054      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28055      */
28056     valueNotFoundText : undefined,
28057     
28058     /**
28059      * @cfg {String} defaultValue The value displayed after loading the store.
28060      */
28061     defaultValue: '',
28062     
28063     /**
28064      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28065      */
28066     blockFocus : false,
28067     
28068     /**
28069      * @cfg {Boolean} disableClear Disable showing of clear button.
28070      */
28071     disableClear : false,
28072     /**
28073      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28074      */
28075     alwaysQuery : false,
28076     
28077     //private
28078     addicon : false,
28079     editicon: false,
28080     
28081     // element that contains real text value.. (when hidden is used..)
28082      
28083     // private
28084     onRender : function(ct, position){
28085         Roo.form.Field.prototype.onRender.call(this, ct, position);
28086         
28087         if(this.store){
28088             this.store.on('beforeload', this.onBeforeLoad, this);
28089             this.store.on('load', this.onLoad, this);
28090             this.store.on('loadexception', this.onLoadException, this);
28091             this.store.load({});
28092         }
28093         
28094         
28095         
28096     },
28097
28098     // private
28099     initEvents : function(){
28100         //Roo.form.ComboBox.superclass.initEvents.call(this);
28101  
28102     },
28103
28104     onDestroy : function(){
28105        
28106         if(this.store){
28107             this.store.un('beforeload', this.onBeforeLoad, this);
28108             this.store.un('load', this.onLoad, this);
28109             this.store.un('loadexception', this.onLoadException, this);
28110         }
28111         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28112     },
28113
28114     // private
28115     fireKey : function(e){
28116         if(e.isNavKeyPress() && !this.list.isVisible()){
28117             this.fireEvent("specialkey", this, e);
28118         }
28119     },
28120
28121     // private
28122     onResize: function(w, h){
28123         
28124         return; 
28125     
28126         
28127     },
28128
28129     /**
28130      * Allow or prevent the user from directly editing the field text.  If false is passed,
28131      * the user will only be able to select from the items defined in the dropdown list.  This method
28132      * is the runtime equivalent of setting the 'editable' config option at config time.
28133      * @param {Boolean} value True to allow the user to directly edit the field text
28134      */
28135     setEditable : function(value){
28136          
28137     },
28138
28139     // private
28140     onBeforeLoad : function(){
28141         
28142         Roo.log("Select before load");
28143         return;
28144     
28145         this.innerList.update(this.loadingText ?
28146                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28147         //this.restrictHeight();
28148         this.selectedIndex = -1;
28149     },
28150
28151     // private
28152     onLoad : function(){
28153
28154     
28155         var dom = this.el.dom;
28156         dom.innerHTML = '';
28157          var od = dom.ownerDocument;
28158          
28159         if (this.emptyText) {
28160             var op = od.createElement('option');
28161             op.setAttribute('value', '');
28162             op.innerHTML = String.format('{0}', this.emptyText);
28163             dom.appendChild(op);
28164         }
28165         if(this.store.getCount() > 0){
28166            
28167             var vf = this.valueField;
28168             var df = this.displayField;
28169             this.store.data.each(function(r) {
28170                 // which colmsn to use... testing - cdoe / title..
28171                 var op = od.createElement('option');
28172                 op.setAttribute('value', r.data[vf]);
28173                 op.innerHTML = String.format('{0}', r.data[df]);
28174                 dom.appendChild(op);
28175             });
28176             if (typeof(this.defaultValue != 'undefined')) {
28177                 this.setValue(this.defaultValue);
28178             }
28179             
28180              
28181         }else{
28182             //this.onEmptyResults();
28183         }
28184         //this.el.focus();
28185     },
28186     // private
28187     onLoadException : function()
28188     {
28189         dom.innerHTML = '';
28190             
28191         Roo.log("Select on load exception");
28192         return;
28193     
28194         this.collapse();
28195         Roo.log(this.store.reader.jsonData);
28196         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28197             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28198         }
28199         
28200         
28201     },
28202     // private
28203     onTypeAhead : function(){
28204          
28205     },
28206
28207     // private
28208     onSelect : function(record, index){
28209         Roo.log('on select?');
28210         return;
28211         if(this.fireEvent('beforeselect', this, record, index) !== false){
28212             this.setFromData(index > -1 ? record.data : false);
28213             this.collapse();
28214             this.fireEvent('select', this, record, index);
28215         }
28216     },
28217
28218     /**
28219      * Returns the currently selected field value or empty string if no value is set.
28220      * @return {String} value The selected value
28221      */
28222     getValue : function(){
28223         var dom = this.el.dom;
28224         this.value = dom.options[dom.selectedIndex].value;
28225         return this.value;
28226         
28227     },
28228
28229     /**
28230      * Clears any text/value currently set in the field
28231      */
28232     clearValue : function(){
28233         this.value = '';
28234         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28235         
28236     },
28237
28238     /**
28239      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28240      * will be displayed in the field.  If the value does not match the data value of an existing item,
28241      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28242      * Otherwise the field will be blank (although the value will still be set).
28243      * @param {String} value The value to match
28244      */
28245     setValue : function(v){
28246         var d = this.el.dom;
28247         for (var i =0; i < d.options.length;i++) {
28248             if (v == d.options[i].value) {
28249                 d.selectedIndex = i;
28250                 this.value = v;
28251                 return;
28252             }
28253         }
28254         this.clearValue();
28255     },
28256     /**
28257      * @property {Object} the last set data for the element
28258      */
28259     
28260     lastData : false,
28261     /**
28262      * Sets the value of the field based on a object which is related to the record format for the store.
28263      * @param {Object} value the value to set as. or false on reset?
28264      */
28265     setFromData : function(o){
28266         Roo.log('setfrom data?');
28267          
28268         
28269         
28270     },
28271     // private
28272     reset : function(){
28273         this.clearValue();
28274     },
28275     // private
28276     findRecord : function(prop, value){
28277         
28278         return false;
28279     
28280         var record;
28281         if(this.store.getCount() > 0){
28282             this.store.each(function(r){
28283                 if(r.data[prop] == value){
28284                     record = r;
28285                     return false;
28286                 }
28287                 return true;
28288             });
28289         }
28290         return record;
28291     },
28292     
28293     getName: function()
28294     {
28295         // returns hidden if it's set..
28296         if (!this.rendered) {return ''};
28297         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28298         
28299     },
28300      
28301
28302     
28303
28304     // private
28305     onEmptyResults : function(){
28306         Roo.log('empty results');
28307         //this.collapse();
28308     },
28309
28310     /**
28311      * Returns true if the dropdown list is expanded, else false.
28312      */
28313     isExpanded : function(){
28314         return false;
28315     },
28316
28317     /**
28318      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28319      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28320      * @param {String} value The data value of the item to select
28321      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28322      * selected item if it is not currently in view (defaults to true)
28323      * @return {Boolean} True if the value matched an item in the list, else false
28324      */
28325     selectByValue : function(v, scrollIntoView){
28326         Roo.log('select By Value');
28327         return false;
28328     
28329         if(v !== undefined && v !== null){
28330             var r = this.findRecord(this.valueField || this.displayField, v);
28331             if(r){
28332                 this.select(this.store.indexOf(r), scrollIntoView);
28333                 return true;
28334             }
28335         }
28336         return false;
28337     },
28338
28339     /**
28340      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28341      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28342      * @param {Number} index The zero-based index of the list item to select
28343      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28344      * selected item if it is not currently in view (defaults to true)
28345      */
28346     select : function(index, scrollIntoView){
28347         Roo.log('select ');
28348         return  ;
28349         
28350         this.selectedIndex = index;
28351         this.view.select(index);
28352         if(scrollIntoView !== false){
28353             var el = this.view.getNode(index);
28354             if(el){
28355                 this.innerList.scrollChildIntoView(el, false);
28356             }
28357         }
28358     },
28359
28360       
28361
28362     // private
28363     validateBlur : function(){
28364         
28365         return;
28366         
28367     },
28368
28369     // private
28370     initQuery : function(){
28371         this.doQuery(this.getRawValue());
28372     },
28373
28374     // private
28375     doForce : function(){
28376         if(this.el.dom.value.length > 0){
28377             this.el.dom.value =
28378                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28379              
28380         }
28381     },
28382
28383     /**
28384      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28385      * query allowing the query action to be canceled if needed.
28386      * @param {String} query The SQL query to execute
28387      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28388      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28389      * saved in the current store (defaults to false)
28390      */
28391     doQuery : function(q, forceAll){
28392         
28393         Roo.log('doQuery?');
28394         if(q === undefined || q === null){
28395             q = '';
28396         }
28397         var qe = {
28398             query: q,
28399             forceAll: forceAll,
28400             combo: this,
28401             cancel:false
28402         };
28403         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28404             return false;
28405         }
28406         q = qe.query;
28407         forceAll = qe.forceAll;
28408         if(forceAll === true || (q.length >= this.minChars)){
28409             if(this.lastQuery != q || this.alwaysQuery){
28410                 this.lastQuery = q;
28411                 if(this.mode == 'local'){
28412                     this.selectedIndex = -1;
28413                     if(forceAll){
28414                         this.store.clearFilter();
28415                     }else{
28416                         this.store.filter(this.displayField, q);
28417                     }
28418                     this.onLoad();
28419                 }else{
28420                     this.store.baseParams[this.queryParam] = q;
28421                     this.store.load({
28422                         params: this.getParams(q)
28423                     });
28424                     this.expand();
28425                 }
28426             }else{
28427                 this.selectedIndex = -1;
28428                 this.onLoad();   
28429             }
28430         }
28431     },
28432
28433     // private
28434     getParams : function(q){
28435         var p = {};
28436         //p[this.queryParam] = q;
28437         if(this.pageSize){
28438             p.start = 0;
28439             p.limit = this.pageSize;
28440         }
28441         return p;
28442     },
28443
28444     /**
28445      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28446      */
28447     collapse : function(){
28448         
28449     },
28450
28451     // private
28452     collapseIf : function(e){
28453         
28454     },
28455
28456     /**
28457      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28458      */
28459     expand : function(){
28460         
28461     } ,
28462
28463     // private
28464      
28465
28466     /** 
28467     * @cfg {Boolean} grow 
28468     * @hide 
28469     */
28470     /** 
28471     * @cfg {Number} growMin 
28472     * @hide 
28473     */
28474     /** 
28475     * @cfg {Number} growMax 
28476     * @hide 
28477     */
28478     /**
28479      * @hide
28480      * @method autoSize
28481      */
28482     
28483     setWidth : function()
28484     {
28485         
28486     },
28487     getResizeEl : function(){
28488         return this.el;
28489     }
28490 });//<script type="text/javasscript">
28491  
28492
28493 /**
28494  * @class Roo.DDView
28495  * A DnD enabled version of Roo.View.
28496  * @param {Element/String} container The Element in which to create the View.
28497  * @param {String} tpl The template string used to create the markup for each element of the View
28498  * @param {Object} config The configuration properties. These include all the config options of
28499  * {@link Roo.View} plus some specific to this class.<br>
28500  * <p>
28501  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28502  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28503  * <p>
28504  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28505 .x-view-drag-insert-above {
28506         border-top:1px dotted #3366cc;
28507 }
28508 .x-view-drag-insert-below {
28509         border-bottom:1px dotted #3366cc;
28510 }
28511 </code></pre>
28512  * 
28513  */
28514  
28515 Roo.DDView = function(container, tpl, config) {
28516     Roo.DDView.superclass.constructor.apply(this, arguments);
28517     this.getEl().setStyle("outline", "0px none");
28518     this.getEl().unselectable();
28519     if (this.dragGroup) {
28520         this.setDraggable(this.dragGroup.split(","));
28521     }
28522     if (this.dropGroup) {
28523         this.setDroppable(this.dropGroup.split(","));
28524     }
28525     if (this.deletable) {
28526         this.setDeletable();
28527     }
28528     this.isDirtyFlag = false;
28529         this.addEvents({
28530                 "drop" : true
28531         });
28532 };
28533
28534 Roo.extend(Roo.DDView, Roo.View, {
28535 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28536 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28537 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28538 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28539
28540         isFormField: true,
28541
28542         reset: Roo.emptyFn,
28543         
28544         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28545
28546         validate: function() {
28547                 return true;
28548         },
28549         
28550         destroy: function() {
28551                 this.purgeListeners();
28552                 this.getEl.removeAllListeners();
28553                 this.getEl().remove();
28554                 if (this.dragZone) {
28555                         if (this.dragZone.destroy) {
28556                                 this.dragZone.destroy();
28557                         }
28558                 }
28559                 if (this.dropZone) {
28560                         if (this.dropZone.destroy) {
28561                                 this.dropZone.destroy();
28562                         }
28563                 }
28564         },
28565
28566 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28567         getName: function() {
28568                 return this.name;
28569         },
28570
28571 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28572         setValue: function(v) {
28573                 if (!this.store) {
28574                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28575                 }
28576                 var data = {};
28577                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28578                 this.store.proxy = new Roo.data.MemoryProxy(data);
28579                 this.store.load();
28580         },
28581
28582 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28583         getValue: function() {
28584                 var result = '(';
28585                 this.store.each(function(rec) {
28586                         result += rec.id + ',';
28587                 });
28588                 return result.substr(0, result.length - 1) + ')';
28589         },
28590         
28591         getIds: function() {
28592                 var i = 0, result = new Array(this.store.getCount());
28593                 this.store.each(function(rec) {
28594                         result[i++] = rec.id;
28595                 });
28596                 return result;
28597         },
28598         
28599         isDirty: function() {
28600                 return this.isDirtyFlag;
28601         },
28602
28603 /**
28604  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28605  *      whole Element becomes the target, and this causes the drop gesture to append.
28606  */
28607     getTargetFromEvent : function(e) {
28608                 var target = e.getTarget();
28609                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28610                 target = target.parentNode;
28611                 }
28612                 if (!target) {
28613                         target = this.el.dom.lastChild || this.el.dom;
28614                 }
28615                 return target;
28616     },
28617
28618 /**
28619  *      Create the drag data which consists of an object which has the property "ddel" as
28620  *      the drag proxy element. 
28621  */
28622     getDragData : function(e) {
28623         var target = this.findItemFromChild(e.getTarget());
28624                 if(target) {
28625                         this.handleSelection(e);
28626                         var selNodes = this.getSelectedNodes();
28627             var dragData = {
28628                 source: this,
28629                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28630                 nodes: selNodes,
28631                 records: []
28632                         };
28633                         var selectedIndices = this.getSelectedIndexes();
28634                         for (var i = 0; i < selectedIndices.length; i++) {
28635                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28636                         }
28637                         if (selNodes.length == 1) {
28638                                 dragData.ddel = target.cloneNode(true); // the div element
28639                         } else {
28640                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28641                                 div.className = 'multi-proxy';
28642                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28643                                         div.appendChild(selNodes[i].cloneNode(true));
28644                                 }
28645                                 dragData.ddel = div;
28646                         }
28647             //console.log(dragData)
28648             //console.log(dragData.ddel.innerHTML)
28649                         return dragData;
28650                 }
28651         //console.log('nodragData')
28652                 return false;
28653     },
28654     
28655 /**     Specify to which ddGroup items in this DDView may be dragged. */
28656     setDraggable: function(ddGroup) {
28657         if (ddGroup instanceof Array) {
28658                 Roo.each(ddGroup, this.setDraggable, this);
28659                 return;
28660         }
28661         if (this.dragZone) {
28662                 this.dragZone.addToGroup(ddGroup);
28663         } else {
28664                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28665                                 containerScroll: true,
28666                                 ddGroup: ddGroup 
28667
28668                         });
28669 //                      Draggability implies selection. DragZone's mousedown selects the element.
28670                         if (!this.multiSelect) { this.singleSelect = true; }
28671
28672 //                      Wire the DragZone's handlers up to methods in *this*
28673                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28674                 }
28675     },
28676
28677 /**     Specify from which ddGroup this DDView accepts drops. */
28678     setDroppable: function(ddGroup) {
28679         if (ddGroup instanceof Array) {
28680                 Roo.each(ddGroup, this.setDroppable, this);
28681                 return;
28682         }
28683         if (this.dropZone) {
28684                 this.dropZone.addToGroup(ddGroup);
28685         } else {
28686                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28687                                 containerScroll: true,
28688                                 ddGroup: ddGroup
28689                         });
28690
28691 //                      Wire the DropZone's handlers up to methods in *this*
28692                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28693                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28694                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28695                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28696                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28697                 }
28698     },
28699
28700 /**     Decide whether to drop above or below a View node. */
28701     getDropPoint : function(e, n, dd){
28702         if (n == this.el.dom) { return "above"; }
28703                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28704                 var c = t + (b - t) / 2;
28705                 var y = Roo.lib.Event.getPageY(e);
28706                 if(y <= c) {
28707                         return "above";
28708                 }else{
28709                         return "below";
28710                 }
28711     },
28712
28713     onNodeEnter : function(n, dd, e, data){
28714                 return false;
28715     },
28716     
28717     onNodeOver : function(n, dd, e, data){
28718                 var pt = this.getDropPoint(e, n, dd);
28719                 // set the insert point style on the target node
28720                 var dragElClass = this.dropNotAllowed;
28721                 if (pt) {
28722                         var targetElClass;
28723                         if (pt == "above"){
28724                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28725                                 targetElClass = "x-view-drag-insert-above";
28726                         } else {
28727                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28728                                 targetElClass = "x-view-drag-insert-below";
28729                         }
28730                         if (this.lastInsertClass != targetElClass){
28731                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28732                                 this.lastInsertClass = targetElClass;
28733                         }
28734                 }
28735                 return dragElClass;
28736         },
28737
28738     onNodeOut : function(n, dd, e, data){
28739                 this.removeDropIndicators(n);
28740     },
28741
28742     onNodeDrop : function(n, dd, e, data){
28743         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28744                 return false;
28745         }
28746         var pt = this.getDropPoint(e, n, dd);
28747                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28748                 if (pt == "below") { insertAt++; }
28749                 for (var i = 0; i < data.records.length; i++) {
28750                         var r = data.records[i];
28751                         var dup = this.store.getById(r.id);
28752                         if (dup && (dd != this.dragZone)) {
28753                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28754                         } else {
28755                                 if (data.copy) {
28756                                         this.store.insert(insertAt++, r.copy());
28757                                 } else {
28758                                         data.source.isDirtyFlag = true;
28759                                         r.store.remove(r);
28760                                         this.store.insert(insertAt++, r);
28761                                 }
28762                                 this.isDirtyFlag = true;
28763                         }
28764                 }
28765                 this.dragZone.cachedTarget = null;
28766                 return true;
28767     },
28768
28769     removeDropIndicators : function(n){
28770                 if(n){
28771                         Roo.fly(n).removeClass([
28772                                 "x-view-drag-insert-above",
28773                                 "x-view-drag-insert-below"]);
28774                         this.lastInsertClass = "_noclass";
28775                 }
28776     },
28777
28778 /**
28779  *      Utility method. Add a delete option to the DDView's context menu.
28780  *      @param {String} imageUrl The URL of the "delete" icon image.
28781  */
28782         setDeletable: function(imageUrl) {
28783                 if (!this.singleSelect && !this.multiSelect) {
28784                         this.singleSelect = true;
28785                 }
28786                 var c = this.getContextMenu();
28787                 this.contextMenu.on("itemclick", function(item) {
28788                         switch (item.id) {
28789                                 case "delete":
28790                                         this.remove(this.getSelectedIndexes());
28791                                         break;
28792                         }
28793                 }, this);
28794                 this.contextMenu.add({
28795                         icon: imageUrl,
28796                         id: "delete",
28797                         text: 'Delete'
28798                 });
28799         },
28800         
28801 /**     Return the context menu for this DDView. */
28802         getContextMenu: function() {
28803                 if (!this.contextMenu) {
28804 //                      Create the View's context menu
28805                         this.contextMenu = new Roo.menu.Menu({
28806                                 id: this.id + "-contextmenu"
28807                         });
28808                         this.el.on("contextmenu", this.showContextMenu, this);
28809                 }
28810                 return this.contextMenu;
28811         },
28812         
28813         disableContextMenu: function() {
28814                 if (this.contextMenu) {
28815                         this.el.un("contextmenu", this.showContextMenu, this);
28816                 }
28817         },
28818
28819         showContextMenu: function(e, item) {
28820         item = this.findItemFromChild(e.getTarget());
28821                 if (item) {
28822                         e.stopEvent();
28823                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28824                         this.contextMenu.showAt(e.getXY());
28825             }
28826     },
28827
28828 /**
28829  *      Remove {@link Roo.data.Record}s at the specified indices.
28830  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28831  */
28832     remove: function(selectedIndices) {
28833                 selectedIndices = [].concat(selectedIndices);
28834                 for (var i = 0; i < selectedIndices.length; i++) {
28835                         var rec = this.store.getAt(selectedIndices[i]);
28836                         this.store.remove(rec);
28837                 }
28838     },
28839
28840 /**
28841  *      Double click fires the event, but also, if this is draggable, and there is only one other
28842  *      related DropZone, it transfers the selected node.
28843  */
28844     onDblClick : function(e){
28845         var item = this.findItemFromChild(e.getTarget());
28846         if(item){
28847             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28848                 return false;
28849             }
28850             if (this.dragGroup) {
28851                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28852                     while (targets.indexOf(this.dropZone) > -1) {
28853                             targets.remove(this.dropZone);
28854                                 }
28855                     if (targets.length == 1) {
28856                                         this.dragZone.cachedTarget = null;
28857                         var el = Roo.get(targets[0].getEl());
28858                         var box = el.getBox(true);
28859                         targets[0].onNodeDrop(el.dom, {
28860                                 target: el.dom,
28861                                 xy: [box.x, box.y + box.height - 1]
28862                         }, null, this.getDragData(e));
28863                     }
28864                 }
28865         }
28866     },
28867     
28868     handleSelection: function(e) {
28869                 this.dragZone.cachedTarget = null;
28870         var item = this.findItemFromChild(e.getTarget());
28871         if (!item) {
28872                 this.clearSelections(true);
28873                 return;
28874         }
28875                 if (item && (this.multiSelect || this.singleSelect)){
28876                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28877                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28878                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28879                                 this.unselect(item);
28880                         } else {
28881                                 this.select(item, this.multiSelect && e.ctrlKey);
28882                                 this.lastSelection = item;
28883                         }
28884                 }
28885     },
28886
28887     onItemClick : function(item, index, e){
28888                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28889                         return false;
28890                 }
28891                 return true;
28892     },
28893
28894     unselect : function(nodeInfo, suppressEvent){
28895                 var node = this.getNode(nodeInfo);
28896                 if(node && this.isSelected(node)){
28897                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28898                                 Roo.fly(node).removeClass(this.selectedClass);
28899                                 this.selections.remove(node);
28900                                 if(!suppressEvent){
28901                                         this.fireEvent("selectionchange", this, this.selections);
28902                                 }
28903                         }
28904                 }
28905     }
28906 });
28907 /*
28908  * Based on:
28909  * Ext JS Library 1.1.1
28910  * Copyright(c) 2006-2007, Ext JS, LLC.
28911  *
28912  * Originally Released Under LGPL - original licence link has changed is not relivant.
28913  *
28914  * Fork - LGPL
28915  * <script type="text/javascript">
28916  */
28917  
28918 /**
28919  * @class Roo.LayoutManager
28920  * @extends Roo.util.Observable
28921  * Base class for layout managers.
28922  */
28923 Roo.LayoutManager = function(container, config){
28924     Roo.LayoutManager.superclass.constructor.call(this);
28925     this.el = Roo.get(container);
28926     // ie scrollbar fix
28927     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28928         document.body.scroll = "no";
28929     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28930         this.el.position('relative');
28931     }
28932     this.id = this.el.id;
28933     this.el.addClass("x-layout-container");
28934     /** false to disable window resize monitoring @type Boolean */
28935     this.monitorWindowResize = true;
28936     this.regions = {};
28937     this.addEvents({
28938         /**
28939          * @event layout
28940          * Fires when a layout is performed. 
28941          * @param {Roo.LayoutManager} this
28942          */
28943         "layout" : true,
28944         /**
28945          * @event regionresized
28946          * Fires when the user resizes a region. 
28947          * @param {Roo.LayoutRegion} region The resized region
28948          * @param {Number} newSize The new size (width for east/west, height for north/south)
28949          */
28950         "regionresized" : true,
28951         /**
28952          * @event regioncollapsed
28953          * Fires when a region is collapsed. 
28954          * @param {Roo.LayoutRegion} region The collapsed region
28955          */
28956         "regioncollapsed" : true,
28957         /**
28958          * @event regionexpanded
28959          * Fires when a region is expanded.  
28960          * @param {Roo.LayoutRegion} region The expanded region
28961          */
28962         "regionexpanded" : true
28963     });
28964     this.updating = false;
28965     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28966 };
28967
28968 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28969     /**
28970      * Returns true if this layout is currently being updated
28971      * @return {Boolean}
28972      */
28973     isUpdating : function(){
28974         return this.updating; 
28975     },
28976     
28977     /**
28978      * Suspend the LayoutManager from doing auto-layouts while
28979      * making multiple add or remove calls
28980      */
28981     beginUpdate : function(){
28982         this.updating = true;    
28983     },
28984     
28985     /**
28986      * Restore auto-layouts and optionally disable the manager from performing a layout
28987      * @param {Boolean} noLayout true to disable a layout update 
28988      */
28989     endUpdate : function(noLayout){
28990         this.updating = false;
28991         if(!noLayout){
28992             this.layout();
28993         }    
28994     },
28995     
28996     layout: function(){
28997         
28998     },
28999     
29000     onRegionResized : function(region, newSize){
29001         this.fireEvent("regionresized", region, newSize);
29002         this.layout();
29003     },
29004     
29005     onRegionCollapsed : function(region){
29006         this.fireEvent("regioncollapsed", region);
29007     },
29008     
29009     onRegionExpanded : function(region){
29010         this.fireEvent("regionexpanded", region);
29011     },
29012         
29013     /**
29014      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29015      * performs box-model adjustments.
29016      * @return {Object} The size as an object {width: (the width), height: (the height)}
29017      */
29018     getViewSize : function(){
29019         var size;
29020         if(this.el.dom != document.body){
29021             size = this.el.getSize();
29022         }else{
29023             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29024         }
29025         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29026         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29027         return size;
29028     },
29029     
29030     /**
29031      * Returns the Element this layout is bound to.
29032      * @return {Roo.Element}
29033      */
29034     getEl : function(){
29035         return this.el;
29036     },
29037     
29038     /**
29039      * Returns the specified region.
29040      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29041      * @return {Roo.LayoutRegion}
29042      */
29043     getRegion : function(target){
29044         return this.regions[target.toLowerCase()];
29045     },
29046     
29047     onWindowResize : function(){
29048         if(this.monitorWindowResize){
29049             this.layout();
29050         }
29051     }
29052 });/*
29053  * Based on:
29054  * Ext JS Library 1.1.1
29055  * Copyright(c) 2006-2007, Ext JS, LLC.
29056  *
29057  * Originally Released Under LGPL - original licence link has changed is not relivant.
29058  *
29059  * Fork - LGPL
29060  * <script type="text/javascript">
29061  */
29062 /**
29063  * @class Roo.BorderLayout
29064  * @extends Roo.LayoutManager
29065  * @children Roo.ContentPanel
29066  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29067  * please see: <br><br>
29068  * <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>
29069  * <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>
29070  * Example:
29071  <pre><code>
29072  var layout = new Roo.BorderLayout(document.body, {
29073     north: {
29074         initialSize: 25,
29075         titlebar: false
29076     },
29077     west: {
29078         split:true,
29079         initialSize: 200,
29080         minSize: 175,
29081         maxSize: 400,
29082         titlebar: true,
29083         collapsible: true
29084     },
29085     east: {
29086         split:true,
29087         initialSize: 202,
29088         minSize: 175,
29089         maxSize: 400,
29090         titlebar: true,
29091         collapsible: true
29092     },
29093     south: {
29094         split:true,
29095         initialSize: 100,
29096         minSize: 100,
29097         maxSize: 200,
29098         titlebar: true,
29099         collapsible: true
29100     },
29101     center: {
29102         titlebar: true,
29103         autoScroll:true,
29104         resizeTabs: true,
29105         minTabWidth: 50,
29106         preferredTabWidth: 150
29107     }
29108 });
29109
29110 // shorthand
29111 var CP = Roo.ContentPanel;
29112
29113 layout.beginUpdate();
29114 layout.add("north", new CP("north", "North"));
29115 layout.add("south", new CP("south", {title: "South", closable: true}));
29116 layout.add("west", new CP("west", {title: "West"}));
29117 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29118 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29119 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29120 layout.getRegion("center").showPanel("center1");
29121 layout.endUpdate();
29122 </code></pre>
29123
29124 <b>The container the layout is rendered into can be either the body element or any other element.
29125 If it is not the body element, the container needs to either be an absolute positioned element,
29126 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29127 the container size if it is not the body element.</b>
29128
29129 * @constructor
29130 * Create a new BorderLayout
29131 * @param {String/HTMLElement/Element} container The container this layout is bound to
29132 * @param {Object} config Configuration options
29133  */
29134 Roo.BorderLayout = function(container, config){
29135     config = config || {};
29136     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29137     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29138     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29139         var target = this.factory.validRegions[i];
29140         if(config[target]){
29141             this.addRegion(target, config[target]);
29142         }
29143     }
29144 };
29145
29146 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29147         
29148         /**
29149          * @cfg {Roo.LayoutRegion} east
29150          */
29151         /**
29152          * @cfg {Roo.LayoutRegion} west
29153          */
29154         /**
29155          * @cfg {Roo.LayoutRegion} north
29156          */
29157         /**
29158          * @cfg {Roo.LayoutRegion} south
29159          */
29160         /**
29161          * @cfg {Roo.LayoutRegion} center
29162          */
29163     /**
29164      * Creates and adds a new region if it doesn't already exist.
29165      * @param {String} target The target region key (north, south, east, west or center).
29166      * @param {Object} config The regions config object
29167      * @return {BorderLayoutRegion} The new region
29168      */
29169     addRegion : function(target, config){
29170         if(!this.regions[target]){
29171             var r = this.factory.create(target, this, config);
29172             this.bindRegion(target, r);
29173         }
29174         return this.regions[target];
29175     },
29176
29177     // private (kinda)
29178     bindRegion : function(name, r){
29179         this.regions[name] = r;
29180         r.on("visibilitychange", this.layout, this);
29181         r.on("paneladded", this.layout, this);
29182         r.on("panelremoved", this.layout, this);
29183         r.on("invalidated", this.layout, this);
29184         r.on("resized", this.onRegionResized, this);
29185         r.on("collapsed", this.onRegionCollapsed, this);
29186         r.on("expanded", this.onRegionExpanded, this);
29187     },
29188
29189     /**
29190      * Performs a layout update.
29191      */
29192     layout : function(){
29193         if(this.updating) {
29194             return;
29195         }
29196         var size = this.getViewSize();
29197         var w = size.width;
29198         var h = size.height;
29199         var centerW = w;
29200         var centerH = h;
29201         var centerY = 0;
29202         var centerX = 0;
29203         //var x = 0, y = 0;
29204
29205         var rs = this.regions;
29206         var north = rs["north"];
29207         var south = rs["south"]; 
29208         var west = rs["west"];
29209         var east = rs["east"];
29210         var center = rs["center"];
29211         //if(this.hideOnLayout){ // not supported anymore
29212             //c.el.setStyle("display", "none");
29213         //}
29214         if(north && north.isVisible()){
29215             var b = north.getBox();
29216             var m = north.getMargins();
29217             b.width = w - (m.left+m.right);
29218             b.x = m.left;
29219             b.y = m.top;
29220             centerY = b.height + b.y + m.bottom;
29221             centerH -= centerY;
29222             north.updateBox(this.safeBox(b));
29223         }
29224         if(south && south.isVisible()){
29225             var b = south.getBox();
29226             var m = south.getMargins();
29227             b.width = w - (m.left+m.right);
29228             b.x = m.left;
29229             var totalHeight = (b.height + m.top + m.bottom);
29230             b.y = h - totalHeight + m.top;
29231             centerH -= totalHeight;
29232             south.updateBox(this.safeBox(b));
29233         }
29234         if(west && west.isVisible()){
29235             var b = west.getBox();
29236             var m = west.getMargins();
29237             b.height = centerH - (m.top+m.bottom);
29238             b.x = m.left;
29239             b.y = centerY + m.top;
29240             var totalWidth = (b.width + m.left + m.right);
29241             centerX += totalWidth;
29242             centerW -= totalWidth;
29243             west.updateBox(this.safeBox(b));
29244         }
29245         if(east && east.isVisible()){
29246             var b = east.getBox();
29247             var m = east.getMargins();
29248             b.height = centerH - (m.top+m.bottom);
29249             var totalWidth = (b.width + m.left + m.right);
29250             b.x = w - totalWidth + m.left;
29251             b.y = centerY + m.top;
29252             centerW -= totalWidth;
29253             east.updateBox(this.safeBox(b));
29254         }
29255         if(center){
29256             var m = center.getMargins();
29257             var centerBox = {
29258                 x: centerX + m.left,
29259                 y: centerY + m.top,
29260                 width: centerW - (m.left+m.right),
29261                 height: centerH - (m.top+m.bottom)
29262             };
29263             //if(this.hideOnLayout){
29264                 //center.el.setStyle("display", "block");
29265             //}
29266             center.updateBox(this.safeBox(centerBox));
29267         }
29268         this.el.repaint();
29269         this.fireEvent("layout", this);
29270     },
29271
29272     // private
29273     safeBox : function(box){
29274         box.width = Math.max(0, box.width);
29275         box.height = Math.max(0, box.height);
29276         return box;
29277     },
29278
29279     /**
29280      * Adds a ContentPanel (or subclass) to this layout.
29281      * @param {String} target The target region key (north, south, east, west or center).
29282      * @param {Roo.ContentPanel} panel The panel to add
29283      * @return {Roo.ContentPanel} The added panel
29284      */
29285     add : function(target, panel){
29286          
29287         target = target.toLowerCase();
29288         return this.regions[target].add(panel);
29289     },
29290
29291     /**
29292      * Remove a ContentPanel (or subclass) to this layout.
29293      * @param {String} target The target region key (north, south, east, west or center).
29294      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29295      * @return {Roo.ContentPanel} The removed panel
29296      */
29297     remove : function(target, panel){
29298         target = target.toLowerCase();
29299         return this.regions[target].remove(panel);
29300     },
29301
29302     /**
29303      * Searches all regions for a panel with the specified id
29304      * @param {String} panelId
29305      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29306      */
29307     findPanel : function(panelId){
29308         var rs = this.regions;
29309         for(var target in rs){
29310             if(typeof rs[target] != "function"){
29311                 var p = rs[target].getPanel(panelId);
29312                 if(p){
29313                     return p;
29314                 }
29315             }
29316         }
29317         return null;
29318     },
29319
29320     /**
29321      * Searches all regions for a panel with the specified id and activates (shows) it.
29322      * @param {String/ContentPanel} panelId The panels id or the panel itself
29323      * @return {Roo.ContentPanel} The shown panel or null
29324      */
29325     showPanel : function(panelId) {
29326       var rs = this.regions;
29327       for(var target in rs){
29328          var r = rs[target];
29329          if(typeof r != "function"){
29330             if(r.hasPanel(panelId)){
29331                return r.showPanel(panelId);
29332             }
29333          }
29334       }
29335       return null;
29336    },
29337
29338    /**
29339      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29340      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29341      */
29342     restoreState : function(provider){
29343         if(!provider){
29344             provider = Roo.state.Manager;
29345         }
29346         var sm = new Roo.LayoutStateManager();
29347         sm.init(this, provider);
29348     },
29349
29350     /**
29351      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29352      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29353      * a valid ContentPanel config object.  Example:
29354      * <pre><code>
29355 // Create the main layout
29356 var layout = new Roo.BorderLayout('main-ct', {
29357     west: {
29358         split:true,
29359         minSize: 175,
29360         titlebar: true
29361     },
29362     center: {
29363         title:'Components'
29364     }
29365 }, 'main-ct');
29366
29367 // Create and add multiple ContentPanels at once via configs
29368 layout.batchAdd({
29369    west: {
29370        id: 'source-files',
29371        autoCreate:true,
29372        title:'Ext Source Files',
29373        autoScroll:true,
29374        fitToFrame:true
29375    },
29376    center : {
29377        el: cview,
29378        autoScroll:true,
29379        fitToFrame:true,
29380        toolbar: tb,
29381        resizeEl:'cbody'
29382    }
29383 });
29384 </code></pre>
29385      * @param {Object} regions An object containing ContentPanel configs by region name
29386      */
29387     batchAdd : function(regions){
29388         this.beginUpdate();
29389         for(var rname in regions){
29390             var lr = this.regions[rname];
29391             if(lr){
29392                 this.addTypedPanels(lr, regions[rname]);
29393             }
29394         }
29395         this.endUpdate();
29396     },
29397
29398     // private
29399     addTypedPanels : function(lr, ps){
29400         if(typeof ps == 'string'){
29401             lr.add(new Roo.ContentPanel(ps));
29402         }
29403         else if(ps instanceof Array){
29404             for(var i =0, len = ps.length; i < len; i++){
29405                 this.addTypedPanels(lr, ps[i]);
29406             }
29407         }
29408         else if(!ps.events){ // raw config?
29409             var el = ps.el;
29410             delete ps.el; // prevent conflict
29411             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29412         }
29413         else {  // panel object assumed!
29414             lr.add(ps);
29415         }
29416     },
29417     /**
29418      * Adds a xtype elements to the layout.
29419      * <pre><code>
29420
29421 layout.addxtype({
29422        xtype : 'ContentPanel',
29423        region: 'west',
29424        items: [ .... ]
29425    }
29426 );
29427
29428 layout.addxtype({
29429         xtype : 'NestedLayoutPanel',
29430         region: 'west',
29431         layout: {
29432            center: { },
29433            west: { }   
29434         },
29435         items : [ ... list of content panels or nested layout panels.. ]
29436    }
29437 );
29438 </code></pre>
29439      * @param {Object} cfg Xtype definition of item to add.
29440      */
29441     addxtype : function(cfg)
29442     {
29443         // basically accepts a pannel...
29444         // can accept a layout region..!?!?
29445         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29446         
29447         if (!cfg.xtype.match(/Panel$/)) {
29448             return false;
29449         }
29450         var ret = false;
29451         
29452         if (typeof(cfg.region) == 'undefined') {
29453             Roo.log("Failed to add Panel, region was not set");
29454             Roo.log(cfg);
29455             return false;
29456         }
29457         var region = cfg.region;
29458         delete cfg.region;
29459         
29460           
29461         var xitems = [];
29462         if (cfg.items) {
29463             xitems = cfg.items;
29464             delete cfg.items;
29465         }
29466         var nb = false;
29467         
29468         switch(cfg.xtype) 
29469         {
29470             case 'ContentPanel':  // ContentPanel (el, cfg)
29471             case 'ScrollPanel':  // ContentPanel (el, cfg)
29472             case 'ViewPanel': 
29473                 if(cfg.autoCreate) {
29474                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29475                 } else {
29476                     var el = this.el.createChild();
29477                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29478                 }
29479                 
29480                 this.add(region, ret);
29481                 break;
29482             
29483             
29484             case 'TreePanel': // our new panel!
29485                 cfg.el = this.el.createChild();
29486                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29487                 this.add(region, ret);
29488                 break;
29489             
29490             case 'NestedLayoutPanel': 
29491                 // create a new Layout (which is  a Border Layout...
29492                 var el = this.el.createChild();
29493                 var clayout = cfg.layout;
29494                 delete cfg.layout;
29495                 clayout.items   = clayout.items  || [];
29496                 // replace this exitems with the clayout ones..
29497                 xitems = clayout.items;
29498                  
29499                 
29500                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29501                     cfg.background = false;
29502                 }
29503                 var layout = new Roo.BorderLayout(el, clayout);
29504                 
29505                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29506                 //console.log('adding nested layout panel '  + cfg.toSource());
29507                 this.add(region, ret);
29508                 nb = {}; /// find first...
29509                 break;
29510                 
29511             case 'GridPanel': 
29512             
29513                 // needs grid and region
29514                 
29515                 //var el = this.getRegion(region).el.createChild();
29516                 var el = this.el.createChild();
29517                 // create the grid first...
29518                 
29519                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29520                 delete cfg.grid;
29521                 if (region == 'center' && this.active ) {
29522                     cfg.background = false;
29523                 }
29524                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29525                 
29526                 this.add(region, ret);
29527                 if (cfg.background) {
29528                     ret.on('activate', function(gp) {
29529                         if (!gp.grid.rendered) {
29530                             gp.grid.render();
29531                         }
29532                     });
29533                 } else {
29534                     grid.render();
29535                 }
29536                 break;
29537            
29538            
29539            
29540                 
29541                 
29542                 
29543             default:
29544                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29545                     
29546                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29547                     this.add(region, ret);
29548                 } else {
29549                 
29550                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29551                     return null;
29552                 }
29553                 
29554              // GridPanel (grid, cfg)
29555             
29556         }
29557         this.beginUpdate();
29558         // add children..
29559         var region = '';
29560         var abn = {};
29561         Roo.each(xitems, function(i)  {
29562             region = nb && i.region ? i.region : false;
29563             
29564             var add = ret.addxtype(i);
29565            
29566             if (region) {
29567                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29568                 if (!i.background) {
29569                     abn[region] = nb[region] ;
29570                 }
29571             }
29572             
29573         });
29574         this.endUpdate();
29575
29576         // make the last non-background panel active..
29577         //if (nb) { Roo.log(abn); }
29578         if (nb) {
29579             
29580             for(var r in abn) {
29581                 region = this.getRegion(r);
29582                 if (region) {
29583                     // tried using nb[r], but it does not work..
29584                      
29585                     region.showPanel(abn[r]);
29586                    
29587                 }
29588             }
29589         }
29590         return ret;
29591         
29592     }
29593 });
29594
29595 /**
29596  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29597  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29598  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29599  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29600  * <pre><code>
29601 // shorthand
29602 var CP = Roo.ContentPanel;
29603
29604 var layout = Roo.BorderLayout.create({
29605     north: {
29606         initialSize: 25,
29607         titlebar: false,
29608         panels: [new CP("north", "North")]
29609     },
29610     west: {
29611         split:true,
29612         initialSize: 200,
29613         minSize: 175,
29614         maxSize: 400,
29615         titlebar: true,
29616         collapsible: true,
29617         panels: [new CP("west", {title: "West"})]
29618     },
29619     east: {
29620         split:true,
29621         initialSize: 202,
29622         minSize: 175,
29623         maxSize: 400,
29624         titlebar: true,
29625         collapsible: true,
29626         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29627     },
29628     south: {
29629         split:true,
29630         initialSize: 100,
29631         minSize: 100,
29632         maxSize: 200,
29633         titlebar: true,
29634         collapsible: true,
29635         panels: [new CP("south", {title: "South", closable: true})]
29636     },
29637     center: {
29638         titlebar: true,
29639         autoScroll:true,
29640         resizeTabs: true,
29641         minTabWidth: 50,
29642         preferredTabWidth: 150,
29643         panels: [
29644             new CP("center1", {title: "Close Me", closable: true}),
29645             new CP("center2", {title: "Center Panel", closable: false})
29646         ]
29647     }
29648 }, document.body);
29649
29650 layout.getRegion("center").showPanel("center1");
29651 </code></pre>
29652  * @param config
29653  * @param targetEl
29654  */
29655 Roo.BorderLayout.create = function(config, targetEl){
29656     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29657     layout.beginUpdate();
29658     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29659     for(var j = 0, jlen = regions.length; j < jlen; j++){
29660         var lr = regions[j];
29661         if(layout.regions[lr] && config[lr].panels){
29662             var r = layout.regions[lr];
29663             var ps = config[lr].panels;
29664             layout.addTypedPanels(r, ps);
29665         }
29666     }
29667     layout.endUpdate();
29668     return layout;
29669 };
29670
29671 // private
29672 Roo.BorderLayout.RegionFactory = {
29673     // private
29674     validRegions : ["north","south","east","west","center"],
29675
29676     // private
29677     create : function(target, mgr, config){
29678         target = target.toLowerCase();
29679         if(config.lightweight || config.basic){
29680             return new Roo.BasicLayoutRegion(mgr, config, target);
29681         }
29682         switch(target){
29683             case "north":
29684                 return new Roo.NorthLayoutRegion(mgr, config);
29685             case "south":
29686                 return new Roo.SouthLayoutRegion(mgr, config);
29687             case "east":
29688                 return new Roo.EastLayoutRegion(mgr, config);
29689             case "west":
29690                 return new Roo.WestLayoutRegion(mgr, config);
29691             case "center":
29692                 return new Roo.CenterLayoutRegion(mgr, config);
29693         }
29694         throw 'Layout region "'+target+'" not supported.';
29695     }
29696 };/*
29697  * Based on:
29698  * Ext JS Library 1.1.1
29699  * Copyright(c) 2006-2007, Ext JS, LLC.
29700  *
29701  * Originally Released Under LGPL - original licence link has changed is not relivant.
29702  *
29703  * Fork - LGPL
29704  * <script type="text/javascript">
29705  */
29706  
29707 /**
29708  * @class Roo.BasicLayoutRegion
29709  * @extends Roo.util.Observable
29710  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29711  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29712  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29713  */
29714 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29715     this.mgr = mgr;
29716     this.position  = pos;
29717     this.events = {
29718         /**
29719          * @scope Roo.BasicLayoutRegion
29720          */
29721         
29722         /**
29723          * @event beforeremove
29724          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29725          * @param {Roo.LayoutRegion} this
29726          * @param {Roo.ContentPanel} panel The panel
29727          * @param {Object} e The cancel event object
29728          */
29729         "beforeremove" : true,
29730         /**
29731          * @event invalidated
29732          * Fires when the layout for this region is changed.
29733          * @param {Roo.LayoutRegion} this
29734          */
29735         "invalidated" : true,
29736         /**
29737          * @event visibilitychange
29738          * Fires when this region is shown or hidden 
29739          * @param {Roo.LayoutRegion} this
29740          * @param {Boolean} visibility true or false
29741          */
29742         "visibilitychange" : true,
29743         /**
29744          * @event paneladded
29745          * Fires when a panel is added. 
29746          * @param {Roo.LayoutRegion} this
29747          * @param {Roo.ContentPanel} panel The panel
29748          */
29749         "paneladded" : true,
29750         /**
29751          * @event panelremoved
29752          * Fires when a panel is removed. 
29753          * @param {Roo.LayoutRegion} this
29754          * @param {Roo.ContentPanel} panel The panel
29755          */
29756         "panelremoved" : true,
29757         /**
29758          * @event beforecollapse
29759          * Fires when this region before collapse.
29760          * @param {Roo.LayoutRegion} this
29761          */
29762         "beforecollapse" : true,
29763         /**
29764          * @event collapsed
29765          * Fires when this region is collapsed.
29766          * @param {Roo.LayoutRegion} this
29767          */
29768         "collapsed" : true,
29769         /**
29770          * @event expanded
29771          * Fires when this region is expanded.
29772          * @param {Roo.LayoutRegion} this
29773          */
29774         "expanded" : true,
29775         /**
29776          * @event slideshow
29777          * Fires when this region is slid into view.
29778          * @param {Roo.LayoutRegion} this
29779          */
29780         "slideshow" : true,
29781         /**
29782          * @event slidehide
29783          * Fires when this region slides out of view. 
29784          * @param {Roo.LayoutRegion} this
29785          */
29786         "slidehide" : true,
29787         /**
29788          * @event panelactivated
29789          * Fires when a panel is activated. 
29790          * @param {Roo.LayoutRegion} this
29791          * @param {Roo.ContentPanel} panel The activated panel
29792          */
29793         "panelactivated" : true,
29794         /**
29795          * @event resized
29796          * Fires when the user resizes this region. 
29797          * @param {Roo.LayoutRegion} this
29798          * @param {Number} newSize The new size (width for east/west, height for north/south)
29799          */
29800         "resized" : true
29801     };
29802     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29803     this.panels = new Roo.util.MixedCollection();
29804     this.panels.getKey = this.getPanelId.createDelegate(this);
29805     this.box = null;
29806     this.activePanel = null;
29807     // ensure listeners are added...
29808     
29809     if (config.listeners || config.events) {
29810         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29811             listeners : config.listeners || {},
29812             events : config.events || {}
29813         });
29814     }
29815     
29816     if(skipConfig !== true){
29817         this.applyConfig(config);
29818     }
29819 };
29820
29821 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29822     getPanelId : function(p){
29823         return p.getId();
29824     },
29825     
29826     applyConfig : function(config){
29827         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29828         this.config = config;
29829         
29830     },
29831     
29832     /**
29833      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29834      * the width, for horizontal (north, south) the height.
29835      * @param {Number} newSize The new width or height
29836      */
29837     resizeTo : function(newSize){
29838         var el = this.el ? this.el :
29839                  (this.activePanel ? this.activePanel.getEl() : null);
29840         if(el){
29841             switch(this.position){
29842                 case "east":
29843                 case "west":
29844                     el.setWidth(newSize);
29845                     this.fireEvent("resized", this, newSize);
29846                 break;
29847                 case "north":
29848                 case "south":
29849                     el.setHeight(newSize);
29850                     this.fireEvent("resized", this, newSize);
29851                 break;                
29852             }
29853         }
29854     },
29855     
29856     getBox : function(){
29857         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29858     },
29859     
29860     getMargins : function(){
29861         return this.margins;
29862     },
29863     
29864     updateBox : function(box){
29865         this.box = box;
29866         var el = this.activePanel.getEl();
29867         el.dom.style.left = box.x + "px";
29868         el.dom.style.top = box.y + "px";
29869         this.activePanel.setSize(box.width, box.height);
29870     },
29871     
29872     /**
29873      * Returns the container element for this region.
29874      * @return {Roo.Element}
29875      */
29876     getEl : function(){
29877         return this.activePanel;
29878     },
29879     
29880     /**
29881      * Returns true if this region is currently visible.
29882      * @return {Boolean}
29883      */
29884     isVisible : function(){
29885         return this.activePanel ? true : false;
29886     },
29887     
29888     setActivePanel : function(panel){
29889         panel = this.getPanel(panel);
29890         if(this.activePanel && this.activePanel != panel){
29891             this.activePanel.setActiveState(false);
29892             this.activePanel.getEl().setLeftTop(-10000,-10000);
29893         }
29894         this.activePanel = panel;
29895         panel.setActiveState(true);
29896         if(this.box){
29897             panel.setSize(this.box.width, this.box.height);
29898         }
29899         this.fireEvent("panelactivated", this, panel);
29900         this.fireEvent("invalidated");
29901     },
29902     
29903     /**
29904      * Show the specified panel.
29905      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29906      * @return {Roo.ContentPanel} The shown panel or null
29907      */
29908     showPanel : function(panel){
29909         if(panel = this.getPanel(panel)){
29910             this.setActivePanel(panel);
29911         }
29912         return panel;
29913     },
29914     
29915     /**
29916      * Get the active panel for this region.
29917      * @return {Roo.ContentPanel} The active panel or null
29918      */
29919     getActivePanel : function(){
29920         return this.activePanel;
29921     },
29922     
29923     /**
29924      * Add the passed ContentPanel(s)
29925      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29926      * @return {Roo.ContentPanel} The panel added (if only one was added)
29927      */
29928     add : function(panel){
29929         if(arguments.length > 1){
29930             for(var i = 0, len = arguments.length; i < len; i++) {
29931                 this.add(arguments[i]);
29932             }
29933             return null;
29934         }
29935         if(this.hasPanel(panel)){
29936             this.showPanel(panel);
29937             return panel;
29938         }
29939         var el = panel.getEl();
29940         if(el.dom.parentNode != this.mgr.el.dom){
29941             this.mgr.el.dom.appendChild(el.dom);
29942         }
29943         if(panel.setRegion){
29944             panel.setRegion(this);
29945         }
29946         this.panels.add(panel);
29947         el.setStyle("position", "absolute");
29948         if(!panel.background){
29949             this.setActivePanel(panel);
29950             if(this.config.initialSize && this.panels.getCount()==1){
29951                 this.resizeTo(this.config.initialSize);
29952             }
29953         }
29954         this.fireEvent("paneladded", this, panel);
29955         return panel;
29956     },
29957     
29958     /**
29959      * Returns true if the panel is in this region.
29960      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29961      * @return {Boolean}
29962      */
29963     hasPanel : function(panel){
29964         if(typeof panel == "object"){ // must be panel obj
29965             panel = panel.getId();
29966         }
29967         return this.getPanel(panel) ? true : false;
29968     },
29969     
29970     /**
29971      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29972      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29973      * @param {Boolean} preservePanel Overrides the config preservePanel option
29974      * @return {Roo.ContentPanel} The panel that was removed
29975      */
29976     remove : function(panel, preservePanel){
29977         panel = this.getPanel(panel);
29978         if(!panel){
29979             return null;
29980         }
29981         var e = {};
29982         this.fireEvent("beforeremove", this, panel, e);
29983         if(e.cancel === true){
29984             return null;
29985         }
29986         var panelId = panel.getId();
29987         this.panels.removeKey(panelId);
29988         return panel;
29989     },
29990     
29991     /**
29992      * Returns the panel specified or null if it's not in this region.
29993      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29994      * @return {Roo.ContentPanel}
29995      */
29996     getPanel : function(id){
29997         if(typeof id == "object"){ // must be panel obj
29998             return id;
29999         }
30000         return this.panels.get(id);
30001     },
30002     
30003     /**
30004      * Returns this regions position (north/south/east/west/center).
30005      * @return {String} 
30006      */
30007     getPosition: function(){
30008         return this.position;    
30009     }
30010 });/*
30011  * Based on:
30012  * Ext JS Library 1.1.1
30013  * Copyright(c) 2006-2007, Ext JS, LLC.
30014  *
30015  * Originally Released Under LGPL - original licence link has changed is not relivant.
30016  *
30017  * Fork - LGPL
30018  * <script type="text/javascript">
30019  */
30020  
30021 /**
30022  * @class Roo.LayoutRegion
30023  * @extends Roo.BasicLayoutRegion
30024  * This class represents a region in a layout manager.
30025  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30026  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30027  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30028  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30029  * @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})
30030  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30031  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30032  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30033  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30034  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30035  * @cfg {String}    title           The title for the region (overrides panel titles)
30036  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30037  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30038  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30039  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30040  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30041  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30042  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30043  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30044  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30045  * @cfg {Boolean}   showPin         True to show a pin button
30046  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30047  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30048  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30049  * @cfg {Number}    width           For East/West panels
30050  * @cfg {Number}    height          For North/South panels
30051  * @cfg {Boolean}   split           To show the splitter
30052  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30053  */
30054 Roo.LayoutRegion = function(mgr, config, pos){
30055     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30056     var dh = Roo.DomHelper;
30057     /** This region's container element 
30058     * @type Roo.Element */
30059     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30060     /** This region's title element 
30061     * @type Roo.Element */
30062
30063     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30064         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30065         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30066     ]}, true);
30067     this.titleEl.enableDisplayMode();
30068     /** This region's title text element 
30069     * @type HTMLElement */
30070     this.titleTextEl = this.titleEl.dom.firstChild;
30071     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30072     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30073     this.closeBtn.enableDisplayMode();
30074     this.closeBtn.on("click", this.closeClicked, this);
30075     this.closeBtn.hide();
30076
30077     this.createBody(config);
30078     this.visible = true;
30079     this.collapsed = false;
30080
30081     if(config.hideWhenEmpty){
30082         this.hide();
30083         this.on("paneladded", this.validateVisibility, this);
30084         this.on("panelremoved", this.validateVisibility, this);
30085     }
30086     this.applyConfig(config);
30087 };
30088
30089 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30090
30091     createBody : function(){
30092         /** This region's body element 
30093         * @type Roo.Element */
30094         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30095     },
30096
30097     applyConfig : function(c){
30098         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30099             var dh = Roo.DomHelper;
30100             if(c.titlebar !== false){
30101                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30102                 this.collapseBtn.on("click", this.collapse, this);
30103                 this.collapseBtn.enableDisplayMode();
30104
30105                 if(c.showPin === true || this.showPin){
30106                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30107                     this.stickBtn.enableDisplayMode();
30108                     this.stickBtn.on("click", this.expand, this);
30109                     this.stickBtn.hide();
30110                 }
30111             }
30112             /** This region's collapsed element
30113             * @type Roo.Element */
30114             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30115                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30116             ]}, true);
30117             if(c.floatable !== false){
30118                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30119                this.collapsedEl.on("click", this.collapseClick, this);
30120             }
30121
30122             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30123                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30124                    id: "message", unselectable: "on", style:{"float":"left"}});
30125                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30126              }
30127             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30128             this.expandBtn.on("click", this.expand, this);
30129         }
30130         if(this.collapseBtn){
30131             this.collapseBtn.setVisible(c.collapsible == true);
30132         }
30133         this.cmargins = c.cmargins || this.cmargins ||
30134                          (this.position == "west" || this.position == "east" ?
30135                              {top: 0, left: 2, right:2, bottom: 0} :
30136                              {top: 2, left: 0, right:0, bottom: 2});
30137         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30138         this.bottomTabs = c.tabPosition != "top";
30139         this.autoScroll = c.autoScroll || false;
30140         if(this.autoScroll){
30141             this.bodyEl.setStyle("overflow", "auto");
30142         }else{
30143             this.bodyEl.setStyle("overflow", "hidden");
30144         }
30145         //if(c.titlebar !== false){
30146             if((!c.titlebar && !c.title) || c.titlebar === false){
30147                 this.titleEl.hide();
30148             }else{
30149                 this.titleEl.show();
30150                 if(c.title){
30151                     this.titleTextEl.innerHTML = c.title;
30152                 }
30153             }
30154         //}
30155         this.duration = c.duration || .30;
30156         this.slideDuration = c.slideDuration || .45;
30157         this.config = c;
30158         if(c.collapsed){
30159             this.collapse(true);
30160         }
30161         if(c.hidden){
30162             this.hide();
30163         }
30164     },
30165     /**
30166      * Returns true if this region is currently visible.
30167      * @return {Boolean}
30168      */
30169     isVisible : function(){
30170         return this.visible;
30171     },
30172
30173     /**
30174      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30175      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30176      */
30177     setCollapsedTitle : function(title){
30178         title = title || "&#160;";
30179         if(this.collapsedTitleTextEl){
30180             this.collapsedTitleTextEl.innerHTML = title;
30181         }
30182     },
30183
30184     getBox : function(){
30185         var b;
30186         if(!this.collapsed){
30187             b = this.el.getBox(false, true);
30188         }else{
30189             b = this.collapsedEl.getBox(false, true);
30190         }
30191         return b;
30192     },
30193
30194     getMargins : function(){
30195         return this.collapsed ? this.cmargins : this.margins;
30196     },
30197
30198     highlight : function(){
30199         this.el.addClass("x-layout-panel-dragover");
30200     },
30201
30202     unhighlight : function(){
30203         this.el.removeClass("x-layout-panel-dragover");
30204     },
30205
30206     updateBox : function(box){
30207         this.box = box;
30208         if(!this.collapsed){
30209             this.el.dom.style.left = box.x + "px";
30210             this.el.dom.style.top = box.y + "px";
30211             this.updateBody(box.width, box.height);
30212         }else{
30213             this.collapsedEl.dom.style.left = box.x + "px";
30214             this.collapsedEl.dom.style.top = box.y + "px";
30215             this.collapsedEl.setSize(box.width, box.height);
30216         }
30217         if(this.tabs){
30218             this.tabs.autoSizeTabs();
30219         }
30220     },
30221
30222     updateBody : function(w, h){
30223         if(w !== null){
30224             this.el.setWidth(w);
30225             w -= this.el.getBorderWidth("rl");
30226             if(this.config.adjustments){
30227                 w += this.config.adjustments[0];
30228             }
30229         }
30230         if(h !== null){
30231             this.el.setHeight(h);
30232             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30233             h -= this.el.getBorderWidth("tb");
30234             if(this.config.adjustments){
30235                 h += this.config.adjustments[1];
30236             }
30237             this.bodyEl.setHeight(h);
30238             if(this.tabs){
30239                 h = this.tabs.syncHeight(h);
30240             }
30241         }
30242         if(this.panelSize){
30243             w = w !== null ? w : this.panelSize.width;
30244             h = h !== null ? h : this.panelSize.height;
30245         }
30246         if(this.activePanel){
30247             var el = this.activePanel.getEl();
30248             w = w !== null ? w : el.getWidth();
30249             h = h !== null ? h : el.getHeight();
30250             this.panelSize = {width: w, height: h};
30251             this.activePanel.setSize(w, h);
30252         }
30253         if(Roo.isIE && this.tabs){
30254             this.tabs.el.repaint();
30255         }
30256     },
30257
30258     /**
30259      * Returns the container element for this region.
30260      * @return {Roo.Element}
30261      */
30262     getEl : function(){
30263         return this.el;
30264     },
30265
30266     /**
30267      * Hides this region.
30268      */
30269     hide : function(){
30270         if(!this.collapsed){
30271             this.el.dom.style.left = "-2000px";
30272             this.el.hide();
30273         }else{
30274             this.collapsedEl.dom.style.left = "-2000px";
30275             this.collapsedEl.hide();
30276         }
30277         this.visible = false;
30278         this.fireEvent("visibilitychange", this, false);
30279     },
30280
30281     /**
30282      * Shows this region if it was previously hidden.
30283      */
30284     show : function(){
30285         if(!this.collapsed){
30286             this.el.show();
30287         }else{
30288             this.collapsedEl.show();
30289         }
30290         this.visible = true;
30291         this.fireEvent("visibilitychange", this, true);
30292     },
30293
30294     closeClicked : function(){
30295         if(this.activePanel){
30296             this.remove(this.activePanel);
30297         }
30298     },
30299
30300     collapseClick : function(e){
30301         if(this.isSlid){
30302            e.stopPropagation();
30303            this.slideIn();
30304         }else{
30305            e.stopPropagation();
30306            this.slideOut();
30307         }
30308     },
30309
30310     /**
30311      * Collapses this region.
30312      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30313      */
30314     collapse : function(skipAnim, skipCheck){
30315         if(this.collapsed) {
30316             return;
30317         }
30318         
30319         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30320             
30321             this.collapsed = true;
30322             if(this.split){
30323                 this.split.el.hide();
30324             }
30325             if(this.config.animate && skipAnim !== true){
30326                 this.fireEvent("invalidated", this);
30327                 this.animateCollapse();
30328             }else{
30329                 this.el.setLocation(-20000,-20000);
30330                 this.el.hide();
30331                 this.collapsedEl.show();
30332                 this.fireEvent("collapsed", this);
30333                 this.fireEvent("invalidated", this);
30334             }
30335         }
30336         
30337     },
30338
30339     animateCollapse : function(){
30340         // overridden
30341     },
30342
30343     /**
30344      * Expands this region if it was previously collapsed.
30345      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30346      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30347      */
30348     expand : function(e, skipAnim){
30349         if(e) {
30350             e.stopPropagation();
30351         }
30352         if(!this.collapsed || this.el.hasActiveFx()) {
30353             return;
30354         }
30355         if(this.isSlid){
30356             this.afterSlideIn();
30357             skipAnim = true;
30358         }
30359         this.collapsed = false;
30360         if(this.config.animate && skipAnim !== true){
30361             this.animateExpand();
30362         }else{
30363             this.el.show();
30364             if(this.split){
30365                 this.split.el.show();
30366             }
30367             this.collapsedEl.setLocation(-2000,-2000);
30368             this.collapsedEl.hide();
30369             this.fireEvent("invalidated", this);
30370             this.fireEvent("expanded", this);
30371         }
30372     },
30373
30374     animateExpand : function(){
30375         // overridden
30376     },
30377
30378     initTabs : function()
30379     {
30380         this.bodyEl.setStyle("overflow", "hidden");
30381         var ts = new Roo.TabPanel(
30382                 this.bodyEl.dom,
30383                 {
30384                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30385                     disableTooltips: this.config.disableTabTips,
30386                     toolbar : this.config.toolbar
30387                 }
30388         );
30389         if(this.config.hideTabs){
30390             ts.stripWrap.setDisplayed(false);
30391         }
30392         this.tabs = ts;
30393         ts.resizeTabs = this.config.resizeTabs === true;
30394         ts.minTabWidth = this.config.minTabWidth || 40;
30395         ts.maxTabWidth = this.config.maxTabWidth || 250;
30396         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30397         ts.monitorResize = false;
30398         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30399         ts.bodyEl.addClass('x-layout-tabs-body');
30400         this.panels.each(this.initPanelAsTab, this);
30401     },
30402
30403     initPanelAsTab : function(panel){
30404         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30405                     this.config.closeOnTab && panel.isClosable());
30406         if(panel.tabTip !== undefined){
30407             ti.setTooltip(panel.tabTip);
30408         }
30409         ti.on("activate", function(){
30410               this.setActivePanel(panel);
30411         }, this);
30412         if(this.config.closeOnTab){
30413             ti.on("beforeclose", function(t, e){
30414                 e.cancel = true;
30415                 this.remove(panel);
30416             }, this);
30417         }
30418         return ti;
30419     },
30420
30421     updatePanelTitle : function(panel, title){
30422         if(this.activePanel == panel){
30423             this.updateTitle(title);
30424         }
30425         if(this.tabs){
30426             var ti = this.tabs.getTab(panel.getEl().id);
30427             ti.setText(title);
30428             if(panel.tabTip !== undefined){
30429                 ti.setTooltip(panel.tabTip);
30430             }
30431         }
30432     },
30433
30434     updateTitle : function(title){
30435         if(this.titleTextEl && !this.config.title){
30436             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30437         }
30438     },
30439
30440     setActivePanel : function(panel){
30441         panel = this.getPanel(panel);
30442         if(this.activePanel && this.activePanel != panel){
30443             this.activePanel.setActiveState(false);
30444         }
30445         this.activePanel = panel;
30446         panel.setActiveState(true);
30447         if(this.panelSize){
30448             panel.setSize(this.panelSize.width, this.panelSize.height);
30449         }
30450         if(this.closeBtn){
30451             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30452         }
30453         this.updateTitle(panel.getTitle());
30454         if(this.tabs){
30455             this.fireEvent("invalidated", this);
30456         }
30457         this.fireEvent("panelactivated", this, panel);
30458     },
30459
30460     /**
30461      * Shows the specified panel.
30462      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30463      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30464      */
30465     showPanel : function(panel)
30466     {
30467         panel = this.getPanel(panel);
30468         if(panel){
30469             if(this.tabs){
30470                 var tab = this.tabs.getTab(panel.getEl().id);
30471                 if(tab.isHidden()){
30472                     this.tabs.unhideTab(tab.id);
30473                 }
30474                 tab.activate();
30475             }else{
30476                 this.setActivePanel(panel);
30477             }
30478         }
30479         return panel;
30480     },
30481
30482     /**
30483      * Get the active panel for this region.
30484      * @return {Roo.ContentPanel} The active panel or null
30485      */
30486     getActivePanel : function(){
30487         return this.activePanel;
30488     },
30489
30490     validateVisibility : function(){
30491         if(this.panels.getCount() < 1){
30492             this.updateTitle("&#160;");
30493             this.closeBtn.hide();
30494             this.hide();
30495         }else{
30496             if(!this.isVisible()){
30497                 this.show();
30498             }
30499         }
30500     },
30501
30502     /**
30503      * Adds the passed ContentPanel(s) to this region.
30504      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30505      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30506      */
30507     add : function(panel){
30508         if(arguments.length > 1){
30509             for(var i = 0, len = arguments.length; i < len; i++) {
30510                 this.add(arguments[i]);
30511             }
30512             return null;
30513         }
30514         if(this.hasPanel(panel)){
30515             this.showPanel(panel);
30516             return panel;
30517         }
30518         panel.setRegion(this);
30519         this.panels.add(panel);
30520         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30521             this.bodyEl.dom.appendChild(panel.getEl().dom);
30522             if(panel.background !== true){
30523                 this.setActivePanel(panel);
30524             }
30525             this.fireEvent("paneladded", this, panel);
30526             return panel;
30527         }
30528         if(!this.tabs){
30529             this.initTabs();
30530         }else{
30531             this.initPanelAsTab(panel);
30532         }
30533         if(panel.background !== true){
30534             this.tabs.activate(panel.getEl().id);
30535         }
30536         this.fireEvent("paneladded", this, panel);
30537         return panel;
30538     },
30539
30540     /**
30541      * Hides the tab for the specified panel.
30542      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30543      */
30544     hidePanel : function(panel){
30545         if(this.tabs && (panel = this.getPanel(panel))){
30546             this.tabs.hideTab(panel.getEl().id);
30547         }
30548     },
30549
30550     /**
30551      * Unhides the tab for a previously hidden panel.
30552      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30553      */
30554     unhidePanel : function(panel){
30555         if(this.tabs && (panel = this.getPanel(panel))){
30556             this.tabs.unhideTab(panel.getEl().id);
30557         }
30558     },
30559
30560     clearPanels : function(){
30561         while(this.panels.getCount() > 0){
30562              this.remove(this.panels.first());
30563         }
30564     },
30565
30566     /**
30567      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30568      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30569      * @param {Boolean} preservePanel Overrides the config preservePanel option
30570      * @return {Roo.ContentPanel} The panel that was removed
30571      */
30572     remove : function(panel, preservePanel){
30573         panel = this.getPanel(panel);
30574         if(!panel){
30575             return null;
30576         }
30577         var e = {};
30578         this.fireEvent("beforeremove", this, panel, e);
30579         if(e.cancel === true){
30580             return null;
30581         }
30582         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30583         var panelId = panel.getId();
30584         this.panels.removeKey(panelId);
30585         if(preservePanel){
30586             document.body.appendChild(panel.getEl().dom);
30587         }
30588         if(this.tabs){
30589             this.tabs.removeTab(panel.getEl().id);
30590         }else if (!preservePanel){
30591             this.bodyEl.dom.removeChild(panel.getEl().dom);
30592         }
30593         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30594             var p = this.panels.first();
30595             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30596             tempEl.appendChild(p.getEl().dom);
30597             this.bodyEl.update("");
30598             this.bodyEl.dom.appendChild(p.getEl().dom);
30599             tempEl = null;
30600             this.updateTitle(p.getTitle());
30601             this.tabs = null;
30602             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30603             this.setActivePanel(p);
30604         }
30605         panel.setRegion(null);
30606         if(this.activePanel == panel){
30607             this.activePanel = null;
30608         }
30609         if(this.config.autoDestroy !== false && preservePanel !== true){
30610             try{panel.destroy();}catch(e){}
30611         }
30612         this.fireEvent("panelremoved", this, panel);
30613         return panel;
30614     },
30615
30616     /**
30617      * Returns the TabPanel component used by this region
30618      * @return {Roo.TabPanel}
30619      */
30620     getTabs : function(){
30621         return this.tabs;
30622     },
30623
30624     createTool : function(parentEl, className){
30625         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30626             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30627         btn.addClassOnOver("x-layout-tools-button-over");
30628         return btn;
30629     }
30630 });/*
30631  * Based on:
30632  * Ext JS Library 1.1.1
30633  * Copyright(c) 2006-2007, Ext JS, LLC.
30634  *
30635  * Originally Released Under LGPL - original licence link has changed is not relivant.
30636  *
30637  * Fork - LGPL
30638  * <script type="text/javascript">
30639  */
30640  
30641
30642
30643 /**
30644  * @class Roo.SplitLayoutRegion
30645  * @extends Roo.LayoutRegion
30646  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30647  */
30648 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30649     this.cursor = cursor;
30650     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30651 };
30652
30653 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30654     splitTip : "Drag to resize.",
30655     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30656     useSplitTips : false,
30657
30658     applyConfig : function(config){
30659         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30660         if(config.split){
30661             if(!this.split){
30662                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30663                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30664                 /** The SplitBar for this region 
30665                 * @type Roo.SplitBar */
30666                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30667                 this.split.on("moved", this.onSplitMove, this);
30668                 this.split.useShim = config.useShim === true;
30669                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30670                 if(this.useSplitTips){
30671                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30672                 }
30673                 if(config.collapsible){
30674                     this.split.el.on("dblclick", this.collapse,  this);
30675                 }
30676             }
30677             if(typeof config.minSize != "undefined"){
30678                 this.split.minSize = config.minSize;
30679             }
30680             if(typeof config.maxSize != "undefined"){
30681                 this.split.maxSize = config.maxSize;
30682             }
30683             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30684                 this.hideSplitter();
30685             }
30686         }
30687     },
30688
30689     getHMaxSize : function(){
30690          var cmax = this.config.maxSize || 10000;
30691          var center = this.mgr.getRegion("center");
30692          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30693     },
30694
30695     getVMaxSize : function(){
30696          var cmax = this.config.maxSize || 10000;
30697          var center = this.mgr.getRegion("center");
30698          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30699     },
30700
30701     onSplitMove : function(split, newSize){
30702         this.fireEvent("resized", this, newSize);
30703     },
30704     
30705     /** 
30706      * Returns the {@link Roo.SplitBar} for this region.
30707      * @return {Roo.SplitBar}
30708      */
30709     getSplitBar : function(){
30710         return this.split;
30711     },
30712     
30713     hide : function(){
30714         this.hideSplitter();
30715         Roo.SplitLayoutRegion.superclass.hide.call(this);
30716     },
30717
30718     hideSplitter : function(){
30719         if(this.split){
30720             this.split.el.setLocation(-2000,-2000);
30721             this.split.el.hide();
30722         }
30723     },
30724
30725     show : function(){
30726         if(this.split){
30727             this.split.el.show();
30728         }
30729         Roo.SplitLayoutRegion.superclass.show.call(this);
30730     },
30731     
30732     beforeSlide: function(){
30733         if(Roo.isGecko){// firefox overflow auto bug workaround
30734             this.bodyEl.clip();
30735             if(this.tabs) {
30736                 this.tabs.bodyEl.clip();
30737             }
30738             if(this.activePanel){
30739                 this.activePanel.getEl().clip();
30740                 
30741                 if(this.activePanel.beforeSlide){
30742                     this.activePanel.beforeSlide();
30743                 }
30744             }
30745         }
30746     },
30747     
30748     afterSlide : function(){
30749         if(Roo.isGecko){// firefox overflow auto bug workaround
30750             this.bodyEl.unclip();
30751             if(this.tabs) {
30752                 this.tabs.bodyEl.unclip();
30753             }
30754             if(this.activePanel){
30755                 this.activePanel.getEl().unclip();
30756                 if(this.activePanel.afterSlide){
30757                     this.activePanel.afterSlide();
30758                 }
30759             }
30760         }
30761     },
30762
30763     initAutoHide : function(){
30764         if(this.autoHide !== false){
30765             if(!this.autoHideHd){
30766                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30767                 this.autoHideHd = {
30768                     "mouseout": function(e){
30769                         if(!e.within(this.el, true)){
30770                             st.delay(500);
30771                         }
30772                     },
30773                     "mouseover" : function(e){
30774                         st.cancel();
30775                     },
30776                     scope : this
30777                 };
30778             }
30779             this.el.on(this.autoHideHd);
30780         }
30781     },
30782
30783     clearAutoHide : function(){
30784         if(this.autoHide !== false){
30785             this.el.un("mouseout", this.autoHideHd.mouseout);
30786             this.el.un("mouseover", this.autoHideHd.mouseover);
30787         }
30788     },
30789
30790     clearMonitor : function(){
30791         Roo.get(document).un("click", this.slideInIf, this);
30792     },
30793
30794     // these names are backwards but not changed for compat
30795     slideOut : function(){
30796         if(this.isSlid || this.el.hasActiveFx()){
30797             return;
30798         }
30799         this.isSlid = true;
30800         if(this.collapseBtn){
30801             this.collapseBtn.hide();
30802         }
30803         this.closeBtnState = this.closeBtn.getStyle('display');
30804         this.closeBtn.hide();
30805         if(this.stickBtn){
30806             this.stickBtn.show();
30807         }
30808         this.el.show();
30809         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30810         this.beforeSlide();
30811         this.el.setStyle("z-index", 10001);
30812         this.el.slideIn(this.getSlideAnchor(), {
30813             callback: function(){
30814                 this.afterSlide();
30815                 this.initAutoHide();
30816                 Roo.get(document).on("click", this.slideInIf, this);
30817                 this.fireEvent("slideshow", this);
30818             },
30819             scope: this,
30820             block: true
30821         });
30822     },
30823
30824     afterSlideIn : function(){
30825         this.clearAutoHide();
30826         this.isSlid = false;
30827         this.clearMonitor();
30828         this.el.setStyle("z-index", "");
30829         if(this.collapseBtn){
30830             this.collapseBtn.show();
30831         }
30832         this.closeBtn.setStyle('display', this.closeBtnState);
30833         if(this.stickBtn){
30834             this.stickBtn.hide();
30835         }
30836         this.fireEvent("slidehide", this);
30837     },
30838
30839     slideIn : function(cb){
30840         if(!this.isSlid || this.el.hasActiveFx()){
30841             Roo.callback(cb);
30842             return;
30843         }
30844         this.isSlid = false;
30845         this.beforeSlide();
30846         this.el.slideOut(this.getSlideAnchor(), {
30847             callback: function(){
30848                 this.el.setLeftTop(-10000, -10000);
30849                 this.afterSlide();
30850                 this.afterSlideIn();
30851                 Roo.callback(cb);
30852             },
30853             scope: this,
30854             block: true
30855         });
30856     },
30857     
30858     slideInIf : function(e){
30859         if(!e.within(this.el)){
30860             this.slideIn();
30861         }
30862     },
30863
30864     animateCollapse : function(){
30865         this.beforeSlide();
30866         this.el.setStyle("z-index", 20000);
30867         var anchor = this.getSlideAnchor();
30868         this.el.slideOut(anchor, {
30869             callback : function(){
30870                 this.el.setStyle("z-index", "");
30871                 this.collapsedEl.slideIn(anchor, {duration:.3});
30872                 this.afterSlide();
30873                 this.el.setLocation(-10000,-10000);
30874                 this.el.hide();
30875                 this.fireEvent("collapsed", this);
30876             },
30877             scope: this,
30878             block: true
30879         });
30880     },
30881
30882     animateExpand : function(){
30883         this.beforeSlide();
30884         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30885         this.el.setStyle("z-index", 20000);
30886         this.collapsedEl.hide({
30887             duration:.1
30888         });
30889         this.el.slideIn(this.getSlideAnchor(), {
30890             callback : function(){
30891                 this.el.setStyle("z-index", "");
30892                 this.afterSlide();
30893                 if(this.split){
30894                     this.split.el.show();
30895                 }
30896                 this.fireEvent("invalidated", this);
30897                 this.fireEvent("expanded", this);
30898             },
30899             scope: this,
30900             block: true
30901         });
30902     },
30903
30904     anchors : {
30905         "west" : "left",
30906         "east" : "right",
30907         "north" : "top",
30908         "south" : "bottom"
30909     },
30910
30911     sanchors : {
30912         "west" : "l",
30913         "east" : "r",
30914         "north" : "t",
30915         "south" : "b"
30916     },
30917
30918     canchors : {
30919         "west" : "tl-tr",
30920         "east" : "tr-tl",
30921         "north" : "tl-bl",
30922         "south" : "bl-tl"
30923     },
30924
30925     getAnchor : function(){
30926         return this.anchors[this.position];
30927     },
30928
30929     getCollapseAnchor : function(){
30930         return this.canchors[this.position];
30931     },
30932
30933     getSlideAnchor : function(){
30934         return this.sanchors[this.position];
30935     },
30936
30937     getAlignAdj : function(){
30938         var cm = this.cmargins;
30939         switch(this.position){
30940             case "west":
30941                 return [0, 0];
30942             break;
30943             case "east":
30944                 return [0, 0];
30945             break;
30946             case "north":
30947                 return [0, 0];
30948             break;
30949             case "south":
30950                 return [0, 0];
30951             break;
30952         }
30953     },
30954
30955     getExpandAdj : function(){
30956         var c = this.collapsedEl, cm = this.cmargins;
30957         switch(this.position){
30958             case "west":
30959                 return [-(cm.right+c.getWidth()+cm.left), 0];
30960             break;
30961             case "east":
30962                 return [cm.right+c.getWidth()+cm.left, 0];
30963             break;
30964             case "north":
30965                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30966             break;
30967             case "south":
30968                 return [0, cm.top+cm.bottom+c.getHeight()];
30969             break;
30970         }
30971     }
30972 });/*
30973  * Based on:
30974  * Ext JS Library 1.1.1
30975  * Copyright(c) 2006-2007, Ext JS, LLC.
30976  *
30977  * Originally Released Under LGPL - original licence link has changed is not relivant.
30978  *
30979  * Fork - LGPL
30980  * <script type="text/javascript">
30981  */
30982 /*
30983  * These classes are private internal classes
30984  */
30985 Roo.CenterLayoutRegion = function(mgr, config){
30986     Roo.LayoutRegion.call(this, mgr, config, "center");
30987     this.visible = true;
30988     this.minWidth = config.minWidth || 20;
30989     this.minHeight = config.minHeight || 20;
30990 };
30991
30992 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30993     hide : function(){
30994         // center panel can't be hidden
30995     },
30996     
30997     show : function(){
30998         // center panel can't be hidden
30999     },
31000     
31001     getMinWidth: function(){
31002         return this.minWidth;
31003     },
31004     
31005     getMinHeight: function(){
31006         return this.minHeight;
31007     }
31008 });
31009
31010
31011 Roo.NorthLayoutRegion = function(mgr, config){
31012     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31013     if(this.split){
31014         this.split.placement = Roo.SplitBar.TOP;
31015         this.split.orientation = Roo.SplitBar.VERTICAL;
31016         this.split.el.addClass("x-layout-split-v");
31017     }
31018     var size = config.initialSize || config.height;
31019     if(typeof size != "undefined"){
31020         this.el.setHeight(size);
31021     }
31022 };
31023 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31024     orientation: Roo.SplitBar.VERTICAL,
31025     getBox : function(){
31026         if(this.collapsed){
31027             return this.collapsedEl.getBox();
31028         }
31029         var box = this.el.getBox();
31030         if(this.split){
31031             box.height += this.split.el.getHeight();
31032         }
31033         return box;
31034     },
31035     
31036     updateBox : function(box){
31037         if(this.split && !this.collapsed){
31038             box.height -= this.split.el.getHeight();
31039             this.split.el.setLeft(box.x);
31040             this.split.el.setTop(box.y+box.height);
31041             this.split.el.setWidth(box.width);
31042         }
31043         if(this.collapsed){
31044             this.updateBody(box.width, null);
31045         }
31046         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31047     }
31048 });
31049
31050 Roo.SouthLayoutRegion = function(mgr, config){
31051     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31052     if(this.split){
31053         this.split.placement = Roo.SplitBar.BOTTOM;
31054         this.split.orientation = Roo.SplitBar.VERTICAL;
31055         this.split.el.addClass("x-layout-split-v");
31056     }
31057     var size = config.initialSize || config.height;
31058     if(typeof size != "undefined"){
31059         this.el.setHeight(size);
31060     }
31061 };
31062 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31063     orientation: Roo.SplitBar.VERTICAL,
31064     getBox : function(){
31065         if(this.collapsed){
31066             return this.collapsedEl.getBox();
31067         }
31068         var box = this.el.getBox();
31069         if(this.split){
31070             var sh = this.split.el.getHeight();
31071             box.height += sh;
31072             box.y -= sh;
31073         }
31074         return box;
31075     },
31076     
31077     updateBox : function(box){
31078         if(this.split && !this.collapsed){
31079             var sh = this.split.el.getHeight();
31080             box.height -= sh;
31081             box.y += sh;
31082             this.split.el.setLeft(box.x);
31083             this.split.el.setTop(box.y-sh);
31084             this.split.el.setWidth(box.width);
31085         }
31086         if(this.collapsed){
31087             this.updateBody(box.width, null);
31088         }
31089         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31090     }
31091 });
31092
31093 Roo.EastLayoutRegion = function(mgr, config){
31094     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31095     if(this.split){
31096         this.split.placement = Roo.SplitBar.RIGHT;
31097         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31098         this.split.el.addClass("x-layout-split-h");
31099     }
31100     var size = config.initialSize || config.width;
31101     if(typeof size != "undefined"){
31102         this.el.setWidth(size);
31103     }
31104 };
31105 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31106     orientation: Roo.SplitBar.HORIZONTAL,
31107     getBox : function(){
31108         if(this.collapsed){
31109             return this.collapsedEl.getBox();
31110         }
31111         var box = this.el.getBox();
31112         if(this.split){
31113             var sw = this.split.el.getWidth();
31114             box.width += sw;
31115             box.x -= sw;
31116         }
31117         return box;
31118     },
31119
31120     updateBox : function(box){
31121         if(this.split && !this.collapsed){
31122             var sw = this.split.el.getWidth();
31123             box.width -= sw;
31124             this.split.el.setLeft(box.x);
31125             this.split.el.setTop(box.y);
31126             this.split.el.setHeight(box.height);
31127             box.x += sw;
31128         }
31129         if(this.collapsed){
31130             this.updateBody(null, box.height);
31131         }
31132         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31133     }
31134 });
31135
31136 Roo.WestLayoutRegion = function(mgr, config){
31137     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31138     if(this.split){
31139         this.split.placement = Roo.SplitBar.LEFT;
31140         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31141         this.split.el.addClass("x-layout-split-h");
31142     }
31143     var size = config.initialSize || config.width;
31144     if(typeof size != "undefined"){
31145         this.el.setWidth(size);
31146     }
31147 };
31148 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31149     orientation: Roo.SplitBar.HORIZONTAL,
31150     getBox : function(){
31151         if(this.collapsed){
31152             return this.collapsedEl.getBox();
31153         }
31154         var box = this.el.getBox();
31155         if(this.split){
31156             box.width += this.split.el.getWidth();
31157         }
31158         return box;
31159     },
31160     
31161     updateBox : function(box){
31162         if(this.split && !this.collapsed){
31163             var sw = this.split.el.getWidth();
31164             box.width -= sw;
31165             this.split.el.setLeft(box.x+box.width);
31166             this.split.el.setTop(box.y);
31167             this.split.el.setHeight(box.height);
31168         }
31169         if(this.collapsed){
31170             this.updateBody(null, box.height);
31171         }
31172         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31173     }
31174 });
31175 /*
31176  * Based on:
31177  * Ext JS Library 1.1.1
31178  * Copyright(c) 2006-2007, Ext JS, LLC.
31179  *
31180  * Originally Released Under LGPL - original licence link has changed is not relivant.
31181  *
31182  * Fork - LGPL
31183  * <script type="text/javascript">
31184  */
31185  
31186  
31187 /*
31188  * Private internal class for reading and applying state
31189  */
31190 Roo.LayoutStateManager = function(layout){
31191      // default empty state
31192      this.state = {
31193         north: {},
31194         south: {},
31195         east: {},
31196         west: {}       
31197     };
31198 };
31199
31200 Roo.LayoutStateManager.prototype = {
31201     init : function(layout, provider){
31202         this.provider = provider;
31203         var state = provider.get(layout.id+"-layout-state");
31204         if(state){
31205             var wasUpdating = layout.isUpdating();
31206             if(!wasUpdating){
31207                 layout.beginUpdate();
31208             }
31209             for(var key in state){
31210                 if(typeof state[key] != "function"){
31211                     var rstate = state[key];
31212                     var r = layout.getRegion(key);
31213                     if(r && rstate){
31214                         if(rstate.size){
31215                             r.resizeTo(rstate.size);
31216                         }
31217                         if(rstate.collapsed == true){
31218                             r.collapse(true);
31219                         }else{
31220                             r.expand(null, true);
31221                         }
31222                     }
31223                 }
31224             }
31225             if(!wasUpdating){
31226                 layout.endUpdate();
31227             }
31228             this.state = state; 
31229         }
31230         this.layout = layout;
31231         layout.on("regionresized", this.onRegionResized, this);
31232         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31233         layout.on("regionexpanded", this.onRegionExpanded, this);
31234     },
31235     
31236     storeState : function(){
31237         this.provider.set(this.layout.id+"-layout-state", this.state);
31238     },
31239     
31240     onRegionResized : function(region, newSize){
31241         this.state[region.getPosition()].size = newSize;
31242         this.storeState();
31243     },
31244     
31245     onRegionCollapsed : function(region){
31246         this.state[region.getPosition()].collapsed = true;
31247         this.storeState();
31248     },
31249     
31250     onRegionExpanded : function(region){
31251         this.state[region.getPosition()].collapsed = false;
31252         this.storeState();
31253     }
31254 };/*
31255  * Based on:
31256  * Ext JS Library 1.1.1
31257  * Copyright(c) 2006-2007, Ext JS, LLC.
31258  *
31259  * Originally Released Under LGPL - original licence link has changed is not relivant.
31260  *
31261  * Fork - LGPL
31262  * <script type="text/javascript">
31263  */
31264 /**
31265  * @class Roo.ContentPanel
31266  * @extends Roo.util.Observable
31267  * @children Roo.form.Form Roo.JsonView Roo.View
31268  * A basic ContentPanel element.
31269  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31270  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31271  * @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
31272  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31273  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31274  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31275  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31276  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31277  * @cfg {String} title          The title for this panel
31278  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31279  * @cfg {String} url            Calls {@link #setUrl} with this value
31280  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31281  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31282  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31283  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31284  * @cfg {String}    style  Extra style to add to the content panel 
31285
31286  * @constructor
31287  * Create a new ContentPanel.
31288  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31289  * @param {String/Object} config A string to set only the title or a config object
31290  * @param {String} content (optional) Set the HTML content for this panel
31291  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31292  */
31293 Roo.ContentPanel = function(el, config, content){
31294     
31295      
31296     /*
31297     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31298         config = el;
31299         el = Roo.id();
31300     }
31301     if (config && config.parentLayout) { 
31302         el = config.parentLayout.el.createChild(); 
31303     }
31304     */
31305     if(el.autoCreate){ // xtype is available if this is called from factory
31306         config = el;
31307         el = Roo.id();
31308     }
31309     this.el = Roo.get(el);
31310     if(!this.el && config && config.autoCreate){
31311         if(typeof config.autoCreate == "object"){
31312             if(!config.autoCreate.id){
31313                 config.autoCreate.id = config.id||el;
31314             }
31315             this.el = Roo.DomHelper.append(document.body,
31316                         config.autoCreate, true);
31317         }else{
31318             this.el = Roo.DomHelper.append(document.body,
31319                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31320         }
31321     }
31322     
31323     
31324     this.closable = false;
31325     this.loaded = false;
31326     this.active = false;
31327     if(typeof config == "string"){
31328         this.title = config;
31329     }else{
31330         Roo.apply(this, config);
31331     }
31332     
31333     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31334         this.wrapEl = this.el.wrap();
31335         this.toolbar.container = this.el.insertSibling(false, 'before');
31336         this.toolbar = new Roo.Toolbar(this.toolbar);
31337     }
31338     
31339     // xtype created footer. - not sure if will work as we normally have to render first..
31340     if (this.footer && !this.footer.el && this.footer.xtype) {
31341         if (!this.wrapEl) {
31342             this.wrapEl = this.el.wrap();
31343         }
31344     
31345         this.footer.container = this.wrapEl.createChild();
31346          
31347         this.footer = Roo.factory(this.footer, Roo);
31348         
31349     }
31350     
31351     if(this.resizeEl){
31352         this.resizeEl = Roo.get(this.resizeEl, true);
31353     }else{
31354         this.resizeEl = this.el;
31355     }
31356     // handle view.xtype
31357     
31358  
31359     
31360     
31361     this.addEvents({
31362         /**
31363          * @event activate
31364          * Fires when this panel is activated. 
31365          * @param {Roo.ContentPanel} this
31366          */
31367         "activate" : true,
31368         /**
31369          * @event deactivate
31370          * Fires when this panel is activated. 
31371          * @param {Roo.ContentPanel} this
31372          */
31373         "deactivate" : true,
31374
31375         /**
31376          * @event resize
31377          * Fires when this panel is resized if fitToFrame is true.
31378          * @param {Roo.ContentPanel} this
31379          * @param {Number} width The width after any component adjustments
31380          * @param {Number} height The height after any component adjustments
31381          */
31382         "resize" : true,
31383         
31384          /**
31385          * @event render
31386          * Fires when this tab is created
31387          * @param {Roo.ContentPanel} this
31388          */
31389         "render" : true
31390          
31391         
31392     });
31393     
31394
31395     
31396     
31397     if(this.autoScroll){
31398         this.resizeEl.setStyle("overflow", "auto");
31399     } else {
31400         // fix randome scrolling
31401         this.el.on('scroll', function() {
31402             Roo.log('fix random scolling');
31403             this.scrollTo('top',0); 
31404         });
31405     }
31406     content = content || this.content;
31407     if(content){
31408         this.setContent(content);
31409     }
31410     if(config && config.url){
31411         this.setUrl(this.url, this.params, this.loadOnce);
31412     }
31413     
31414     
31415     
31416     Roo.ContentPanel.superclass.constructor.call(this);
31417     
31418     if (this.view && typeof(this.view.xtype) != 'undefined') {
31419         this.view.el = this.el.appendChild(document.createElement("div"));
31420         this.view = Roo.factory(this.view); 
31421         this.view.render  &&  this.view.render(false, '');  
31422     }
31423     
31424     
31425     this.fireEvent('render', this);
31426 };
31427
31428 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31429     tabTip:'',
31430     setRegion : function(region){
31431         this.region = region;
31432         if(region){
31433            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31434         }else{
31435            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31436         } 
31437     },
31438     
31439     /**
31440      * Returns the toolbar for this Panel if one was configured. 
31441      * @return {Roo.Toolbar} 
31442      */
31443     getToolbar : function(){
31444         return this.toolbar;
31445     },
31446     
31447     setActiveState : function(active){
31448         this.active = active;
31449         if(!active){
31450             this.fireEvent("deactivate", this);
31451         }else{
31452             this.fireEvent("activate", this);
31453         }
31454     },
31455     /**
31456      * Updates this panel's element
31457      * @param {String} content The new content
31458      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31459     */
31460     setContent : function(content, loadScripts){
31461         this.el.update(content, loadScripts);
31462     },
31463
31464     ignoreResize : function(w, h){
31465         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31466             return true;
31467         }else{
31468             this.lastSize = {width: w, height: h};
31469             return false;
31470         }
31471     },
31472     /**
31473      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31474      * @return {Roo.UpdateManager} The UpdateManager
31475      */
31476     getUpdateManager : function(){
31477         return this.el.getUpdateManager();
31478     },
31479      /**
31480      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31481      * @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:
31482 <pre><code>
31483 panel.load({
31484     url: "your-url.php",
31485     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31486     callback: yourFunction,
31487     scope: yourObject, //(optional scope)
31488     discardUrl: false,
31489     nocache: false,
31490     text: "Loading...",
31491     timeout: 30,
31492     scripts: false
31493 });
31494 </code></pre>
31495      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31496      * 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.
31497      * @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}
31498      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31499      * @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.
31500      * @return {Roo.ContentPanel} this
31501      */
31502     load : function(){
31503         var um = this.el.getUpdateManager();
31504         um.update.apply(um, arguments);
31505         return this;
31506     },
31507
31508
31509     /**
31510      * 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.
31511      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31512      * @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)
31513      * @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)
31514      * @return {Roo.UpdateManager} The UpdateManager
31515      */
31516     setUrl : function(url, params, loadOnce){
31517         if(this.refreshDelegate){
31518             this.removeListener("activate", this.refreshDelegate);
31519         }
31520         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31521         this.on("activate", this.refreshDelegate);
31522         return this.el.getUpdateManager();
31523     },
31524     
31525     _handleRefresh : function(url, params, loadOnce){
31526         if(!loadOnce || !this.loaded){
31527             var updater = this.el.getUpdateManager();
31528             updater.update(url, params, this._setLoaded.createDelegate(this));
31529         }
31530     },
31531     
31532     _setLoaded : function(){
31533         this.loaded = true;
31534     }, 
31535     
31536     /**
31537      * Returns this panel's id
31538      * @return {String} 
31539      */
31540     getId : function(){
31541         return this.el.id;
31542     },
31543     
31544     /** 
31545      * Returns this panel's element - used by regiosn to add.
31546      * @return {Roo.Element} 
31547      */
31548     getEl : function(){
31549         return this.wrapEl || this.el;
31550     },
31551     
31552     adjustForComponents : function(width, height)
31553     {
31554         //Roo.log('adjustForComponents ');
31555         if(this.resizeEl != this.el){
31556             width -= this.el.getFrameWidth('lr');
31557             height -= this.el.getFrameWidth('tb');
31558         }
31559         if(this.toolbar){
31560             var te = this.toolbar.getEl();
31561             height -= te.getHeight();
31562             te.setWidth(width);
31563         }
31564         if(this.footer){
31565             var te = this.footer.getEl();
31566             //Roo.log("footer:" + te.getHeight());
31567             
31568             height -= te.getHeight();
31569             te.setWidth(width);
31570         }
31571         
31572         
31573         if(this.adjustments){
31574             width += this.adjustments[0];
31575             height += this.adjustments[1];
31576         }
31577         return {"width": width, "height": height};
31578     },
31579     
31580     setSize : function(width, height){
31581         if(this.fitToFrame && !this.ignoreResize(width, height)){
31582             if(this.fitContainer && this.resizeEl != this.el){
31583                 this.el.setSize(width, height);
31584             }
31585             var size = this.adjustForComponents(width, height);
31586             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31587             this.fireEvent('resize', this, size.width, size.height);
31588         }
31589     },
31590     
31591     /**
31592      * Returns this panel's title
31593      * @return {String} 
31594      */
31595     getTitle : function(){
31596         return this.title;
31597     },
31598     
31599     /**
31600      * Set this panel's title
31601      * @param {String} title
31602      */
31603     setTitle : function(title){
31604         this.title = title;
31605         if(this.region){
31606             this.region.updatePanelTitle(this, title);
31607         }
31608     },
31609     
31610     /**
31611      * Returns true is this panel was configured to be closable
31612      * @return {Boolean} 
31613      */
31614     isClosable : function(){
31615         return this.closable;
31616     },
31617     
31618     beforeSlide : function(){
31619         this.el.clip();
31620         this.resizeEl.clip();
31621     },
31622     
31623     afterSlide : function(){
31624         this.el.unclip();
31625         this.resizeEl.unclip();
31626     },
31627     
31628     /**
31629      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31630      *   Will fail silently if the {@link #setUrl} method has not been called.
31631      *   This does not activate the panel, just updates its content.
31632      */
31633     refresh : function(){
31634         if(this.refreshDelegate){
31635            this.loaded = false;
31636            this.refreshDelegate();
31637         }
31638     },
31639     
31640     /**
31641      * Destroys this panel
31642      */
31643     destroy : function(){
31644         this.el.removeAllListeners();
31645         var tempEl = document.createElement("span");
31646         tempEl.appendChild(this.el.dom);
31647         tempEl.innerHTML = "";
31648         this.el.remove();
31649         this.el = null;
31650     },
31651     
31652     /**
31653      * form - if the content panel contains a form - this is a reference to it.
31654      * @type {Roo.form.Form}
31655      */
31656     form : false,
31657     /**
31658      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31659      *    This contains a reference to it.
31660      * @type {Roo.View}
31661      */
31662     view : false,
31663     
31664       /**
31665      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31666      * <pre><code>
31667
31668 layout.addxtype({
31669        xtype : 'Form',
31670        items: [ .... ]
31671    }
31672 );
31673
31674 </code></pre>
31675      * @param {Object} cfg Xtype definition of item to add.
31676      */
31677     
31678     addxtype : function(cfg) {
31679         // add form..
31680         if (cfg.xtype.match(/^Form$/)) {
31681             
31682             var el;
31683             //if (this.footer) {
31684             //    el = this.footer.container.insertSibling(false, 'before');
31685             //} else {
31686                 el = this.el.createChild();
31687             //}
31688
31689             this.form = new  Roo.form.Form(cfg);
31690             
31691             
31692             if ( this.form.allItems.length) {
31693                 this.form.render(el.dom);
31694             }
31695             return this.form;
31696         }
31697         // should only have one of theses..
31698         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31699             // views.. should not be just added - used named prop 'view''
31700             
31701             cfg.el = this.el.appendChild(document.createElement("div"));
31702             // factory?
31703             
31704             var ret = new Roo.factory(cfg);
31705              
31706              ret.render && ret.render(false, ''); // render blank..
31707             this.view = ret;
31708             return ret;
31709         }
31710         return false;
31711     }
31712 });
31713
31714 /**
31715  * @class Roo.GridPanel
31716  * @extends Roo.ContentPanel
31717  * @constructor
31718  * Create a new GridPanel.
31719  * @param {Roo.grid.Grid} grid The grid for this panel
31720  * @param {String/Object} config A string to set only the panel's title, or a config object
31721  */
31722 Roo.GridPanel = function(grid, config){
31723     
31724   
31725     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31726         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31727         
31728     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31729     
31730     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31731     
31732     if(this.toolbar){
31733         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31734     }
31735     // xtype created footer. - not sure if will work as we normally have to render first..
31736     if (this.footer && !this.footer.el && this.footer.xtype) {
31737         
31738         this.footer.container = this.grid.getView().getFooterPanel(true);
31739         this.footer.dataSource = this.grid.dataSource;
31740         this.footer = Roo.factory(this.footer, Roo);
31741         
31742     }
31743     
31744     grid.monitorWindowResize = false; // turn off autosizing
31745     grid.autoHeight = false;
31746     grid.autoWidth = false;
31747     this.grid = grid;
31748     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31749 };
31750
31751 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31752     getId : function(){
31753         return this.grid.id;
31754     },
31755     
31756     /**
31757      * Returns the grid for this panel
31758      * @return {Roo.grid.Grid} 
31759      */
31760     getGrid : function(){
31761         return this.grid;    
31762     },
31763     
31764     setSize : function(width, height){
31765         if(!this.ignoreResize(width, height)){
31766             var grid = this.grid;
31767             var size = this.adjustForComponents(width, height);
31768             grid.getGridEl().setSize(size.width, size.height);
31769             grid.autoSize();
31770         }
31771     },
31772     
31773     beforeSlide : function(){
31774         this.grid.getView().scroller.clip();
31775     },
31776     
31777     afterSlide : function(){
31778         this.grid.getView().scroller.unclip();
31779     },
31780     
31781     destroy : function(){
31782         this.grid.destroy();
31783         delete this.grid;
31784         Roo.GridPanel.superclass.destroy.call(this); 
31785     }
31786 });
31787
31788
31789 /**
31790  * @class Roo.NestedLayoutPanel
31791  * @extends Roo.ContentPanel
31792  * @constructor
31793  * Create a new NestedLayoutPanel.
31794  * 
31795  * 
31796  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31797  * @param {String/Object} config A string to set only the title or a config object
31798  */
31799 Roo.NestedLayoutPanel = function(layout, config)
31800 {
31801     // construct with only one argument..
31802     /* FIXME - implement nicer consturctors
31803     if (layout.layout) {
31804         config = layout;
31805         layout = config.layout;
31806         delete config.layout;
31807     }
31808     if (layout.xtype && !layout.getEl) {
31809         // then layout needs constructing..
31810         layout = Roo.factory(layout, Roo);
31811     }
31812     */
31813     
31814     
31815     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31816     
31817     layout.monitorWindowResize = false; // turn off autosizing
31818     this.layout = layout;
31819     this.layout.getEl().addClass("x-layout-nested-layout");
31820     
31821     
31822     
31823     
31824 };
31825
31826 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31827
31828     setSize : function(width, height){
31829         if(!this.ignoreResize(width, height)){
31830             var size = this.adjustForComponents(width, height);
31831             var el = this.layout.getEl();
31832             el.setSize(size.width, size.height);
31833             var touch = el.dom.offsetWidth;
31834             this.layout.layout();
31835             // ie requires a double layout on the first pass
31836             if(Roo.isIE && !this.initialized){
31837                 this.initialized = true;
31838                 this.layout.layout();
31839             }
31840         }
31841     },
31842     
31843     // activate all subpanels if not currently active..
31844     
31845     setActiveState : function(active){
31846         this.active = active;
31847         if(!active){
31848             this.fireEvent("deactivate", this);
31849             return;
31850         }
31851         
31852         this.fireEvent("activate", this);
31853         // not sure if this should happen before or after..
31854         if (!this.layout) {
31855             return; // should not happen..
31856         }
31857         var reg = false;
31858         for (var r in this.layout.regions) {
31859             reg = this.layout.getRegion(r);
31860             if (reg.getActivePanel()) {
31861                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31862                 reg.setActivePanel(reg.getActivePanel());
31863                 continue;
31864             }
31865             if (!reg.panels.length) {
31866                 continue;
31867             }
31868             reg.showPanel(reg.getPanel(0));
31869         }
31870         
31871         
31872         
31873         
31874     },
31875     
31876     /**
31877      * Returns the nested BorderLayout for this panel
31878      * @return {Roo.BorderLayout} 
31879      */
31880     getLayout : function(){
31881         return this.layout;
31882     },
31883     
31884      /**
31885      * Adds a xtype elements to the layout of the nested panel
31886      * <pre><code>
31887
31888 panel.addxtype({
31889        xtype : 'ContentPanel',
31890        region: 'west',
31891        items: [ .... ]
31892    }
31893 );
31894
31895 panel.addxtype({
31896         xtype : 'NestedLayoutPanel',
31897         region: 'west',
31898         layout: {
31899            center: { },
31900            west: { }   
31901         },
31902         items : [ ... list of content panels or nested layout panels.. ]
31903    }
31904 );
31905 </code></pre>
31906      * @param {Object} cfg Xtype definition of item to add.
31907      */
31908     addxtype : function(cfg) {
31909         return this.layout.addxtype(cfg);
31910     
31911     }
31912 });
31913
31914 Roo.ScrollPanel = function(el, config, content){
31915     config = config || {};
31916     config.fitToFrame = true;
31917     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31918     
31919     this.el.dom.style.overflow = "hidden";
31920     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31921     this.el.removeClass("x-layout-inactive-content");
31922     this.el.on("mousewheel", this.onWheel, this);
31923
31924     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31925     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31926     up.unselectable(); down.unselectable();
31927     up.on("click", this.scrollUp, this);
31928     down.on("click", this.scrollDown, this);
31929     up.addClassOnOver("x-scroller-btn-over");
31930     down.addClassOnOver("x-scroller-btn-over");
31931     up.addClassOnClick("x-scroller-btn-click");
31932     down.addClassOnClick("x-scroller-btn-click");
31933     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31934
31935     this.resizeEl = this.el;
31936     this.el = wrap; this.up = up; this.down = down;
31937 };
31938
31939 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31940     increment : 100,
31941     wheelIncrement : 5,
31942     scrollUp : function(){
31943         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31944     },
31945
31946     scrollDown : function(){
31947         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31948     },
31949
31950     afterScroll : function(){
31951         var el = this.resizeEl;
31952         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31953         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31954         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31955     },
31956
31957     setSize : function(){
31958         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31959         this.afterScroll();
31960     },
31961
31962     onWheel : function(e){
31963         var d = e.getWheelDelta();
31964         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31965         this.afterScroll();
31966         e.stopEvent();
31967     },
31968
31969     setContent : function(content, loadScripts){
31970         this.resizeEl.update(content, loadScripts);
31971     }
31972
31973 });
31974
31975
31976
31977 /**
31978  * @class Roo.TreePanel
31979  * @extends Roo.ContentPanel
31980  * Treepanel component
31981  * 
31982  * @constructor
31983  * Create a new TreePanel. - defaults to fit/scoll contents.
31984  * @param {String/Object} config A string to set only the panel's title, or a config object
31985  */
31986 Roo.TreePanel = function(config){
31987     var el = config.el;
31988     var tree = config.tree;
31989     delete config.tree; 
31990     delete config.el; // hopefull!
31991     
31992     // wrapper for IE7 strict & safari scroll issue
31993     
31994     var treeEl = el.createChild();
31995     config.resizeEl = treeEl;
31996     
31997     
31998     
31999     Roo.TreePanel.superclass.constructor.call(this, el, config);
32000  
32001  
32002     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32003     //console.log(tree);
32004     this.on('activate', function()
32005     {
32006         if (this.tree.rendered) {
32007             return;
32008         }
32009         //console.log('render tree');
32010         this.tree.render();
32011     });
32012     // this should not be needed.. - it's actually the 'el' that resizes?
32013     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32014     
32015     //this.on('resize',  function (cp, w, h) {
32016     //        this.tree.innerCt.setWidth(w);
32017     //        this.tree.innerCt.setHeight(h);
32018     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32019     //});
32020
32021         
32022     
32023 };
32024
32025 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32026     fitToFrame : true,
32027     autoScroll : true,
32028     /*
32029      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32030      */
32031     tree : false
32032
32033 });
32034
32035
32036
32037
32038
32039
32040
32041
32042
32043
32044
32045 /*
32046  * Based on:
32047  * Ext JS Library 1.1.1
32048  * Copyright(c) 2006-2007, Ext JS, LLC.
32049  *
32050  * Originally Released Under LGPL - original licence link has changed is not relivant.
32051  *
32052  * Fork - LGPL
32053  * <script type="text/javascript">
32054  */
32055  
32056
32057 /**
32058  * @class Roo.ReaderLayout
32059  * @extends Roo.BorderLayout
32060  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32061  * center region containing two nested regions (a top one for a list view and one for item preview below),
32062  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32063  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32064  * expedites the setup of the overall layout and regions for this common application style.
32065  * Example:
32066  <pre><code>
32067 var reader = new Roo.ReaderLayout();
32068 var CP = Roo.ContentPanel;  // shortcut for adding
32069
32070 reader.beginUpdate();
32071 reader.add("north", new CP("north", "North"));
32072 reader.add("west", new CP("west", {title: "West"}));
32073 reader.add("east", new CP("east", {title: "East"}));
32074
32075 reader.regions.listView.add(new CP("listView", "List"));
32076 reader.regions.preview.add(new CP("preview", "Preview"));
32077 reader.endUpdate();
32078 </code></pre>
32079 * @constructor
32080 * Create a new ReaderLayout
32081 * @param {Object} config Configuration options
32082 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32083 * document.body if omitted)
32084 */
32085 Roo.ReaderLayout = function(config, renderTo){
32086     var c = config || {size:{}};
32087     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32088         north: c.north !== false ? Roo.apply({
32089             split:false,
32090             initialSize: 32,
32091             titlebar: false
32092         }, c.north) : false,
32093         west: c.west !== false ? Roo.apply({
32094             split:true,
32095             initialSize: 200,
32096             minSize: 175,
32097             maxSize: 400,
32098             titlebar: true,
32099             collapsible: true,
32100             animate: true,
32101             margins:{left:5,right:0,bottom:5,top:5},
32102             cmargins:{left:5,right:5,bottom:5,top:5}
32103         }, c.west) : false,
32104         east: c.east !== false ? Roo.apply({
32105             split:true,
32106             initialSize: 200,
32107             minSize: 175,
32108             maxSize: 400,
32109             titlebar: true,
32110             collapsible: true,
32111             animate: true,
32112             margins:{left:0,right:5,bottom:5,top:5},
32113             cmargins:{left:5,right:5,bottom:5,top:5}
32114         }, c.east) : false,
32115         center: Roo.apply({
32116             tabPosition: 'top',
32117             autoScroll:false,
32118             closeOnTab: true,
32119             titlebar:false,
32120             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32121         }, c.center)
32122     });
32123
32124     this.el.addClass('x-reader');
32125
32126     this.beginUpdate();
32127
32128     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32129         south: c.preview !== false ? Roo.apply({
32130             split:true,
32131             initialSize: 200,
32132             minSize: 100,
32133             autoScroll:true,
32134             collapsible:true,
32135             titlebar: true,
32136             cmargins:{top:5,left:0, right:0, bottom:0}
32137         }, c.preview) : false,
32138         center: Roo.apply({
32139             autoScroll:false,
32140             titlebar:false,
32141             minHeight:200
32142         }, c.listView)
32143     });
32144     this.add('center', new Roo.NestedLayoutPanel(inner,
32145             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32146
32147     this.endUpdate();
32148
32149     this.regions.preview = inner.getRegion('south');
32150     this.regions.listView = inner.getRegion('center');
32151 };
32152
32153 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32154  * Based on:
32155  * Ext JS Library 1.1.1
32156  * Copyright(c) 2006-2007, Ext JS, LLC.
32157  *
32158  * Originally Released Under LGPL - original licence link has changed is not relivant.
32159  *
32160  * Fork - LGPL
32161  * <script type="text/javascript">
32162  */
32163  
32164 /**
32165  * @class Roo.grid.Grid
32166  * @extends Roo.util.Observable
32167  * This class represents the primary interface of a component based grid control.
32168  * <br><br>Usage:<pre><code>
32169  var grid = new Roo.grid.Grid("my-container-id", {
32170      ds: myDataStore,
32171      cm: myColModel,
32172      selModel: mySelectionModel,
32173      autoSizeColumns: true,
32174      monitorWindowResize: false,
32175      trackMouseOver: true
32176  });
32177  // set any options
32178  grid.render();
32179  * </code></pre>
32180  * <b>Common Problems:</b><br/>
32181  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32182  * element will correct this<br/>
32183  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32184  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32185  * are unpredictable.<br/>
32186  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32187  * grid to calculate dimensions/offsets.<br/>
32188   * @constructor
32189  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32190  * The container MUST have some type of size defined for the grid to fill. The container will be
32191  * automatically set to position relative if it isn't already.
32192  * @param {Object} config A config object that sets properties on this grid.
32193  */
32194 Roo.grid.Grid = function(container, config){
32195         // initialize the container
32196         this.container = Roo.get(container);
32197         this.container.update("");
32198         this.container.setStyle("overflow", "hidden");
32199     this.container.addClass('x-grid-container');
32200
32201     this.id = this.container.id;
32202
32203     Roo.apply(this, config);
32204     // check and correct shorthanded configs
32205     if(this.ds){
32206         this.dataSource = this.ds;
32207         delete this.ds;
32208     }
32209     if(this.cm){
32210         this.colModel = this.cm;
32211         delete this.cm;
32212     }
32213     if(this.sm){
32214         this.selModel = this.sm;
32215         delete this.sm;
32216     }
32217
32218     if (this.selModel) {
32219         this.selModel = Roo.factory(this.selModel, Roo.grid);
32220         this.sm = this.selModel;
32221         this.sm.xmodule = this.xmodule || false;
32222     }
32223     if (typeof(this.colModel.config) == 'undefined') {
32224         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32225         this.cm = this.colModel;
32226         this.cm.xmodule = this.xmodule || false;
32227     }
32228     if (this.dataSource) {
32229         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32230         this.ds = this.dataSource;
32231         this.ds.xmodule = this.xmodule || false;
32232          
32233     }
32234     
32235     
32236     
32237     if(this.width){
32238         this.container.setWidth(this.width);
32239     }
32240
32241     if(this.height){
32242         this.container.setHeight(this.height);
32243     }
32244     /** @private */
32245         this.addEvents({
32246         // raw events
32247         /**
32248          * @event click
32249          * The raw click event for the entire grid.
32250          * @param {Roo.EventObject} e
32251          */
32252         "click" : true,
32253         /**
32254          * @event dblclick
32255          * The raw dblclick event for the entire grid.
32256          * @param {Roo.EventObject} e
32257          */
32258         "dblclick" : true,
32259         /**
32260          * @event contextmenu
32261          * The raw contextmenu event for the entire grid.
32262          * @param {Roo.EventObject} e
32263          */
32264         "contextmenu" : true,
32265         /**
32266          * @event mousedown
32267          * The raw mousedown event for the entire grid.
32268          * @param {Roo.EventObject} e
32269          */
32270         "mousedown" : true,
32271         /**
32272          * @event mouseup
32273          * The raw mouseup event for the entire grid.
32274          * @param {Roo.EventObject} e
32275          */
32276         "mouseup" : true,
32277         /**
32278          * @event mouseover
32279          * The raw mouseover event for the entire grid.
32280          * @param {Roo.EventObject} e
32281          */
32282         "mouseover" : true,
32283         /**
32284          * @event mouseout
32285          * The raw mouseout event for the entire grid.
32286          * @param {Roo.EventObject} e
32287          */
32288         "mouseout" : true,
32289         /**
32290          * @event keypress
32291          * The raw keypress event for the entire grid.
32292          * @param {Roo.EventObject} e
32293          */
32294         "keypress" : true,
32295         /**
32296          * @event keydown
32297          * The raw keydown event for the entire grid.
32298          * @param {Roo.EventObject} e
32299          */
32300         "keydown" : true,
32301
32302         // custom events
32303
32304         /**
32305          * @event cellclick
32306          * Fires when a cell is clicked
32307          * @param {Grid} this
32308          * @param {Number} rowIndex
32309          * @param {Number} columnIndex
32310          * @param {Roo.EventObject} e
32311          */
32312         "cellclick" : true,
32313         /**
32314          * @event celldblclick
32315          * Fires when a cell is double clicked
32316          * @param {Grid} this
32317          * @param {Number} rowIndex
32318          * @param {Number} columnIndex
32319          * @param {Roo.EventObject} e
32320          */
32321         "celldblclick" : true,
32322         /**
32323          * @event rowclick
32324          * Fires when a row is clicked
32325          * @param {Grid} this
32326          * @param {Number} rowIndex
32327          * @param {Roo.EventObject} e
32328          */
32329         "rowclick" : true,
32330         /**
32331          * @event rowdblclick
32332          * Fires when a row is double clicked
32333          * @param {Grid} this
32334          * @param {Number} rowIndex
32335          * @param {Roo.EventObject} e
32336          */
32337         "rowdblclick" : true,
32338         /**
32339          * @event headerclick
32340          * Fires when a header is clicked
32341          * @param {Grid} this
32342          * @param {Number} columnIndex
32343          * @param {Roo.EventObject} e
32344          */
32345         "headerclick" : true,
32346         /**
32347          * @event headerdblclick
32348          * Fires when a header cell is double clicked
32349          * @param {Grid} this
32350          * @param {Number} columnIndex
32351          * @param {Roo.EventObject} e
32352          */
32353         "headerdblclick" : true,
32354         /**
32355          * @event rowcontextmenu
32356          * Fires when a row is right clicked
32357          * @param {Grid} this
32358          * @param {Number} rowIndex
32359          * @param {Roo.EventObject} e
32360          */
32361         "rowcontextmenu" : true,
32362         /**
32363          * @event cellcontextmenu
32364          * Fires when a cell is right clicked
32365          * @param {Grid} this
32366          * @param {Number} rowIndex
32367          * @param {Number} cellIndex
32368          * @param {Roo.EventObject} e
32369          */
32370          "cellcontextmenu" : true,
32371         /**
32372          * @event headercontextmenu
32373          * Fires when a header is right clicked
32374          * @param {Grid} this
32375          * @param {Number} columnIndex
32376          * @param {Roo.EventObject} e
32377          */
32378         "headercontextmenu" : true,
32379         /**
32380          * @event bodyscroll
32381          * Fires when the body element is scrolled
32382          * @param {Number} scrollLeft
32383          * @param {Number} scrollTop
32384          */
32385         "bodyscroll" : true,
32386         /**
32387          * @event columnresize
32388          * Fires when the user resizes a column
32389          * @param {Number} columnIndex
32390          * @param {Number} newSize
32391          */
32392         "columnresize" : true,
32393         /**
32394          * @event columnmove
32395          * Fires when the user moves a column
32396          * @param {Number} oldIndex
32397          * @param {Number} newIndex
32398          */
32399         "columnmove" : true,
32400         /**
32401          * @event startdrag
32402          * Fires when row(s) start being dragged
32403          * @param {Grid} this
32404          * @param {Roo.GridDD} dd The drag drop object
32405          * @param {event} e The raw browser event
32406          */
32407         "startdrag" : true,
32408         /**
32409          * @event enddrag
32410          * Fires when a drag operation is complete
32411          * @param {Grid} this
32412          * @param {Roo.GridDD} dd The drag drop object
32413          * @param {event} e The raw browser event
32414          */
32415         "enddrag" : true,
32416         /**
32417          * @event dragdrop
32418          * Fires when dragged row(s) are dropped on a valid DD target
32419          * @param {Grid} this
32420          * @param {Roo.GridDD} dd The drag drop object
32421          * @param {String} targetId The target drag drop object
32422          * @param {event} e The raw browser event
32423          */
32424         "dragdrop" : true,
32425         /**
32426          * @event dragover
32427          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32428          * @param {Grid} this
32429          * @param {Roo.GridDD} dd The drag drop object
32430          * @param {String} targetId The target drag drop object
32431          * @param {event} e The raw browser event
32432          */
32433         "dragover" : true,
32434         /**
32435          * @event dragenter
32436          *  Fires when the dragged row(s) first cross another DD target while being dragged
32437          * @param {Grid} this
32438          * @param {Roo.GridDD} dd The drag drop object
32439          * @param {String} targetId The target drag drop object
32440          * @param {event} e The raw browser event
32441          */
32442         "dragenter" : true,
32443         /**
32444          * @event dragout
32445          * Fires when the dragged row(s) leave another DD target while being dragged
32446          * @param {Grid} this
32447          * @param {Roo.GridDD} dd The drag drop object
32448          * @param {String} targetId The target drag drop object
32449          * @param {event} e The raw browser event
32450          */
32451         "dragout" : true,
32452         /**
32453          * @event rowclass
32454          * Fires when a row is rendered, so you can change add a style to it.
32455          * @param {GridView} gridview   The grid view
32456          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32457          */
32458         'rowclass' : true,
32459
32460         /**
32461          * @event render
32462          * Fires when the grid is rendered
32463          * @param {Grid} grid
32464          */
32465         'render' : true
32466     });
32467
32468     Roo.grid.Grid.superclass.constructor.call(this);
32469 };
32470 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32471     
32472     /**
32473      * @cfg {String} ddGroup - drag drop group.
32474      */
32475       /**
32476      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32477      */
32478
32479     /**
32480      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32481      */
32482     minColumnWidth : 25,
32483
32484     /**
32485      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32486      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32487      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32488      */
32489     autoSizeColumns : false,
32490
32491     /**
32492      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32493      */
32494     autoSizeHeaders : true,
32495
32496     /**
32497      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32498      */
32499     monitorWindowResize : true,
32500
32501     /**
32502      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32503      * rows measured to get a columns size. Default is 0 (all rows).
32504      */
32505     maxRowsToMeasure : 0,
32506
32507     /**
32508      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32509      */
32510     trackMouseOver : true,
32511
32512     /**
32513     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32514     */
32515       /**
32516     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32517     */
32518     
32519     /**
32520     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32521     */
32522     enableDragDrop : false,
32523     
32524     /**
32525     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32526     */
32527     enableColumnMove : true,
32528     
32529     /**
32530     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32531     */
32532     enableColumnHide : true,
32533     
32534     /**
32535     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32536     */
32537     enableRowHeightSync : false,
32538     
32539     /**
32540     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32541     */
32542     stripeRows : true,
32543     
32544     /**
32545     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32546     */
32547     autoHeight : false,
32548
32549     /**
32550      * @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.
32551      */
32552     autoExpandColumn : false,
32553
32554     /**
32555     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32556     * Default is 50.
32557     */
32558     autoExpandMin : 50,
32559
32560     /**
32561     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32562     */
32563     autoExpandMax : 1000,
32564
32565     /**
32566     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32567     */
32568     view : null,
32569
32570     /**
32571     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32572     */
32573     loadMask : false,
32574     /**
32575     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32576     */
32577     dropTarget: false,
32578     
32579    
32580     
32581     // private
32582     rendered : false,
32583
32584     /**
32585     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32586     * of a fixed width. Default is false.
32587     */
32588     /**
32589     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32590     */
32591     
32592     
32593     /**
32594     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32595     * %0 is replaced with the number of selected rows.
32596     */
32597     ddText : "{0} selected row{1}",
32598     
32599     
32600     /**
32601      * Called once after all setup has been completed and the grid is ready to be rendered.
32602      * @return {Roo.grid.Grid} this
32603      */
32604     render : function()
32605     {
32606         var c = this.container;
32607         // try to detect autoHeight/width mode
32608         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32609             this.autoHeight = true;
32610         }
32611         var view = this.getView();
32612         view.init(this);
32613
32614         c.on("click", this.onClick, this);
32615         c.on("dblclick", this.onDblClick, this);
32616         c.on("contextmenu", this.onContextMenu, this);
32617         c.on("keydown", this.onKeyDown, this);
32618         if (Roo.isTouch) {
32619             c.on("touchstart", this.onTouchStart, this);
32620         }
32621
32622         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32623
32624         this.getSelectionModel().init(this);
32625
32626         view.render();
32627
32628         if(this.loadMask){
32629             this.loadMask = new Roo.LoadMask(this.container,
32630                     Roo.apply({store:this.dataSource}, this.loadMask));
32631         }
32632         
32633         
32634         if (this.toolbar && this.toolbar.xtype) {
32635             this.toolbar.container = this.getView().getHeaderPanel(true);
32636             this.toolbar = new Roo.Toolbar(this.toolbar);
32637         }
32638         if (this.footer && this.footer.xtype) {
32639             this.footer.dataSource = this.getDataSource();
32640             this.footer.container = this.getView().getFooterPanel(true);
32641             this.footer = Roo.factory(this.footer, Roo);
32642         }
32643         if (this.dropTarget && this.dropTarget.xtype) {
32644             delete this.dropTarget.xtype;
32645             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32646         }
32647         
32648         
32649         this.rendered = true;
32650         this.fireEvent('render', this);
32651         return this;
32652     },
32653
32654     /**
32655      * Reconfigures the grid to use a different Store and Column Model.
32656      * The View will be bound to the new objects and refreshed.
32657      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32658      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32659      */
32660     reconfigure : function(dataSource, colModel){
32661         if(this.loadMask){
32662             this.loadMask.destroy();
32663             this.loadMask = new Roo.LoadMask(this.container,
32664                     Roo.apply({store:dataSource}, this.loadMask));
32665         }
32666         this.view.bind(dataSource, colModel);
32667         this.dataSource = dataSource;
32668         this.colModel = colModel;
32669         this.view.refresh(true);
32670     },
32671     /**
32672      * addColumns
32673      * Add's a column, default at the end..
32674      
32675      * @param {int} position to add (default end)
32676      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32677      */
32678     addColumns : function(pos, ar)
32679     {
32680         
32681         for (var i =0;i< ar.length;i++) {
32682             var cfg = ar[i];
32683             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32684             this.cm.lookup[cfg.id] = cfg;
32685         }
32686         
32687         
32688         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32689             pos = this.cm.config.length; //this.cm.config.push(cfg);
32690         } 
32691         pos = Math.max(0,pos);
32692         ar.unshift(0);
32693         ar.unshift(pos);
32694         this.cm.config.splice.apply(this.cm.config, ar);
32695         
32696         
32697         
32698         this.view.generateRules(this.cm);
32699         this.view.refresh(true);
32700         
32701     },
32702     
32703     
32704     
32705     
32706     // private
32707     onKeyDown : function(e){
32708         this.fireEvent("keydown", e);
32709     },
32710
32711     /**
32712      * Destroy this grid.
32713      * @param {Boolean} removeEl True to remove the element
32714      */
32715     destroy : function(removeEl, keepListeners){
32716         if(this.loadMask){
32717             this.loadMask.destroy();
32718         }
32719         var c = this.container;
32720         c.removeAllListeners();
32721         this.view.destroy();
32722         this.colModel.purgeListeners();
32723         if(!keepListeners){
32724             this.purgeListeners();
32725         }
32726         c.update("");
32727         if(removeEl === true){
32728             c.remove();
32729         }
32730     },
32731
32732     // private
32733     processEvent : function(name, e){
32734         // does this fire select???
32735         //Roo.log('grid:processEvent '  + name);
32736         
32737         if (name != 'touchstart' ) {
32738             this.fireEvent(name, e);    
32739         }
32740         
32741         var t = e.getTarget();
32742         var v = this.view;
32743         var header = v.findHeaderIndex(t);
32744         if(header !== false){
32745             var ename = name == 'touchstart' ? 'click' : name;
32746              
32747             this.fireEvent("header" + ename, this, header, e);
32748         }else{
32749             var row = v.findRowIndex(t);
32750             var cell = v.findCellIndex(t);
32751             if (name == 'touchstart') {
32752                 // first touch is always a click.
32753                 // hopefull this happens after selection is updated.?
32754                 name = false;
32755                 
32756                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32757                     var cs = this.selModel.getSelectedCell();
32758                     if (row == cs[0] && cell == cs[1]){
32759                         name = 'dblclick';
32760                     }
32761                 }
32762                 if (typeof(this.selModel.getSelections) != 'undefined') {
32763                     var cs = this.selModel.getSelections();
32764                     var ds = this.dataSource;
32765                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32766                         name = 'dblclick';
32767                     }
32768                 }
32769                 if (!name) {
32770                     return;
32771                 }
32772             }
32773             
32774             
32775             if(row !== false){
32776                 this.fireEvent("row" + name, this, row, e);
32777                 if(cell !== false){
32778                     this.fireEvent("cell" + name, this, row, cell, e);
32779                 }
32780             }
32781         }
32782     },
32783
32784     // private
32785     onClick : function(e){
32786         this.processEvent("click", e);
32787     },
32788    // private
32789     onTouchStart : function(e){
32790         this.processEvent("touchstart", e);
32791     },
32792
32793     // private
32794     onContextMenu : function(e, t){
32795         this.processEvent("contextmenu", e);
32796     },
32797
32798     // private
32799     onDblClick : function(e){
32800         this.processEvent("dblclick", e);
32801     },
32802
32803     // private
32804     walkCells : function(row, col, step, fn, scope){
32805         var cm = this.colModel, clen = cm.getColumnCount();
32806         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32807         if(step < 0){
32808             if(col < 0){
32809                 row--;
32810                 first = false;
32811             }
32812             while(row >= 0){
32813                 if(!first){
32814                     col = clen-1;
32815                 }
32816                 first = false;
32817                 while(col >= 0){
32818                     if(fn.call(scope || this, row, col, cm) === true){
32819                         return [row, col];
32820                     }
32821                     col--;
32822                 }
32823                 row--;
32824             }
32825         } else {
32826             if(col >= clen){
32827                 row++;
32828                 first = false;
32829             }
32830             while(row < rlen){
32831                 if(!first){
32832                     col = 0;
32833                 }
32834                 first = false;
32835                 while(col < clen){
32836                     if(fn.call(scope || this, row, col, cm) === true){
32837                         return [row, col];
32838                     }
32839                     col++;
32840                 }
32841                 row++;
32842             }
32843         }
32844         return null;
32845     },
32846
32847     // private
32848     getSelections : function(){
32849         return this.selModel.getSelections();
32850     },
32851
32852     /**
32853      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32854      * but if manual update is required this method will initiate it.
32855      */
32856     autoSize : function(){
32857         if(this.rendered){
32858             this.view.layout();
32859             if(this.view.adjustForScroll){
32860                 this.view.adjustForScroll();
32861             }
32862         }
32863     },
32864
32865     /**
32866      * Returns the grid's underlying element.
32867      * @return {Element} The element
32868      */
32869     getGridEl : function(){
32870         return this.container;
32871     },
32872
32873     // private for compatibility, overridden by editor grid
32874     stopEditing : function(){},
32875
32876     /**
32877      * Returns the grid's SelectionModel.
32878      * @return {SelectionModel}
32879      */
32880     getSelectionModel : function(){
32881         if(!this.selModel){
32882             this.selModel = new Roo.grid.RowSelectionModel();
32883         }
32884         return this.selModel;
32885     },
32886
32887     /**
32888      * Returns the grid's DataSource.
32889      * @return {DataSource}
32890      */
32891     getDataSource : function(){
32892         return this.dataSource;
32893     },
32894
32895     /**
32896      * Returns the grid's ColumnModel.
32897      * @return {ColumnModel}
32898      */
32899     getColumnModel : function(){
32900         return this.colModel;
32901     },
32902
32903     /**
32904      * Returns the grid's GridView object.
32905      * @return {GridView}
32906      */
32907     getView : function(){
32908         if(!this.view){
32909             this.view = new Roo.grid.GridView(this.viewConfig);
32910             this.relayEvents(this.view, [
32911                 "beforerowremoved", "beforerowsinserted",
32912                 "beforerefresh", "rowremoved",
32913                 "rowsinserted", "rowupdated" ,"refresh"
32914             ]);
32915         }
32916         return this.view;
32917     },
32918     /**
32919      * Called to get grid's drag proxy text, by default returns this.ddText.
32920      * Override this to put something different in the dragged text.
32921      * @return {String}
32922      */
32923     getDragDropText : function(){
32924         var count = this.selModel.getCount();
32925         return String.format(this.ddText, count, count == 1 ? '' : 's');
32926     }
32927 });
32928 /*
32929  * Based on:
32930  * Ext JS Library 1.1.1
32931  * Copyright(c) 2006-2007, Ext JS, LLC.
32932  *
32933  * Originally Released Under LGPL - original licence link has changed is not relivant.
32934  *
32935  * Fork - LGPL
32936  * <script type="text/javascript">
32937  */
32938  
32939 Roo.grid.AbstractGridView = function(){
32940         this.grid = null;
32941         
32942         this.events = {
32943             "beforerowremoved" : true,
32944             "beforerowsinserted" : true,
32945             "beforerefresh" : true,
32946             "rowremoved" : true,
32947             "rowsinserted" : true,
32948             "rowupdated" : true,
32949             "refresh" : true
32950         };
32951     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32952 };
32953
32954 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32955     rowClass : "x-grid-row",
32956     cellClass : "x-grid-cell",
32957     tdClass : "x-grid-td",
32958     hdClass : "x-grid-hd",
32959     splitClass : "x-grid-hd-split",
32960     
32961     init: function(grid){
32962         this.grid = grid;
32963                 var cid = this.grid.getGridEl().id;
32964         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32965         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32966         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32967         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32968         },
32969         
32970     getColumnRenderers : function(){
32971         var renderers = [];
32972         var cm = this.grid.colModel;
32973         var colCount = cm.getColumnCount();
32974         for(var i = 0; i < colCount; i++){
32975             renderers[i] = cm.getRenderer(i);
32976         }
32977         return renderers;
32978     },
32979     
32980     getColumnIds : function(){
32981         var ids = [];
32982         var cm = this.grid.colModel;
32983         var colCount = cm.getColumnCount();
32984         for(var i = 0; i < colCount; i++){
32985             ids[i] = cm.getColumnId(i);
32986         }
32987         return ids;
32988     },
32989     
32990     getDataIndexes : function(){
32991         if(!this.indexMap){
32992             this.indexMap = this.buildIndexMap();
32993         }
32994         return this.indexMap.colToData;
32995     },
32996     
32997     getColumnIndexByDataIndex : function(dataIndex){
32998         if(!this.indexMap){
32999             this.indexMap = this.buildIndexMap();
33000         }
33001         return this.indexMap.dataToCol[dataIndex];
33002     },
33003     
33004     /**
33005      * Set a css style for a column dynamically. 
33006      * @param {Number} colIndex The index of the column
33007      * @param {String} name The css property name
33008      * @param {String} value The css value
33009      */
33010     setCSSStyle : function(colIndex, name, value){
33011         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33012         Roo.util.CSS.updateRule(selector, name, value);
33013     },
33014     
33015     generateRules : function(cm){
33016         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33017         Roo.util.CSS.removeStyleSheet(rulesId);
33018         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33019             var cid = cm.getColumnId(i);
33020             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33021                          this.tdSelector, cid, " {\n}\n",
33022                          this.hdSelector, cid, " {\n}\n",
33023                          this.splitSelector, cid, " {\n}\n");
33024         }
33025         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33026     }
33027 });/*
33028  * Based on:
33029  * Ext JS Library 1.1.1
33030  * Copyright(c) 2006-2007, Ext JS, LLC.
33031  *
33032  * Originally Released Under LGPL - original licence link has changed is not relivant.
33033  *
33034  * Fork - LGPL
33035  * <script type="text/javascript">
33036  */
33037
33038 // private
33039 // This is a support class used internally by the Grid components
33040 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33041     this.grid = grid;
33042     this.view = grid.getView();
33043     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33044     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33045     if(hd2){
33046         this.setHandleElId(Roo.id(hd));
33047         this.setOuterHandleElId(Roo.id(hd2));
33048     }
33049     this.scroll = false;
33050 };
33051 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33052     maxDragWidth: 120,
33053     getDragData : function(e){
33054         var t = Roo.lib.Event.getTarget(e);
33055         var h = this.view.findHeaderCell(t);
33056         if(h){
33057             return {ddel: h.firstChild, header:h};
33058         }
33059         return false;
33060     },
33061
33062     onInitDrag : function(e){
33063         this.view.headersDisabled = true;
33064         var clone = this.dragData.ddel.cloneNode(true);
33065         clone.id = Roo.id();
33066         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33067         this.proxy.update(clone);
33068         return true;
33069     },
33070
33071     afterValidDrop : function(){
33072         var v = this.view;
33073         setTimeout(function(){
33074             v.headersDisabled = false;
33075         }, 50);
33076     },
33077
33078     afterInvalidDrop : function(){
33079         var v = this.view;
33080         setTimeout(function(){
33081             v.headersDisabled = false;
33082         }, 50);
33083     }
33084 });
33085 /*
33086  * Based on:
33087  * Ext JS Library 1.1.1
33088  * Copyright(c) 2006-2007, Ext JS, LLC.
33089  *
33090  * Originally Released Under LGPL - original licence link has changed is not relivant.
33091  *
33092  * Fork - LGPL
33093  * <script type="text/javascript">
33094  */
33095 // private
33096 // This is a support class used internally by the Grid components
33097 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33098     this.grid = grid;
33099     this.view = grid.getView();
33100     // split the proxies so they don't interfere with mouse events
33101     this.proxyTop = Roo.DomHelper.append(document.body, {
33102         cls:"col-move-top", html:"&#160;"
33103     }, true);
33104     this.proxyBottom = Roo.DomHelper.append(document.body, {
33105         cls:"col-move-bottom", html:"&#160;"
33106     }, true);
33107     this.proxyTop.hide = this.proxyBottom.hide = function(){
33108         this.setLeftTop(-100,-100);
33109         this.setStyle("visibility", "hidden");
33110     };
33111     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33112     // temporarily disabled
33113     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33114     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33115 };
33116 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33117     proxyOffsets : [-4, -9],
33118     fly: Roo.Element.fly,
33119
33120     getTargetFromEvent : function(e){
33121         var t = Roo.lib.Event.getTarget(e);
33122         var cindex = this.view.findCellIndex(t);
33123         if(cindex !== false){
33124             return this.view.getHeaderCell(cindex);
33125         }
33126         return null;
33127     },
33128
33129     nextVisible : function(h){
33130         var v = this.view, cm = this.grid.colModel;
33131         h = h.nextSibling;
33132         while(h){
33133             if(!cm.isHidden(v.getCellIndex(h))){
33134                 return h;
33135             }
33136             h = h.nextSibling;
33137         }
33138         return null;
33139     },
33140
33141     prevVisible : function(h){
33142         var v = this.view, cm = this.grid.colModel;
33143         h = h.prevSibling;
33144         while(h){
33145             if(!cm.isHidden(v.getCellIndex(h))){
33146                 return h;
33147             }
33148             h = h.prevSibling;
33149         }
33150         return null;
33151     },
33152
33153     positionIndicator : function(h, n, e){
33154         var x = Roo.lib.Event.getPageX(e);
33155         var r = Roo.lib.Dom.getRegion(n.firstChild);
33156         var px, pt, py = r.top + this.proxyOffsets[1];
33157         if((r.right - x) <= (r.right-r.left)/2){
33158             px = r.right+this.view.borderWidth;
33159             pt = "after";
33160         }else{
33161             px = r.left;
33162             pt = "before";
33163         }
33164         var oldIndex = this.view.getCellIndex(h);
33165         var newIndex = this.view.getCellIndex(n);
33166
33167         if(this.grid.colModel.isFixed(newIndex)){
33168             return false;
33169         }
33170
33171         var locked = this.grid.colModel.isLocked(newIndex);
33172
33173         if(pt == "after"){
33174             newIndex++;
33175         }
33176         if(oldIndex < newIndex){
33177             newIndex--;
33178         }
33179         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33180             return false;
33181         }
33182         px +=  this.proxyOffsets[0];
33183         this.proxyTop.setLeftTop(px, py);
33184         this.proxyTop.show();
33185         if(!this.bottomOffset){
33186             this.bottomOffset = this.view.mainHd.getHeight();
33187         }
33188         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33189         this.proxyBottom.show();
33190         return pt;
33191     },
33192
33193     onNodeEnter : function(n, dd, e, data){
33194         if(data.header != n){
33195             this.positionIndicator(data.header, n, e);
33196         }
33197     },
33198
33199     onNodeOver : function(n, dd, e, data){
33200         var result = false;
33201         if(data.header != n){
33202             result = this.positionIndicator(data.header, n, e);
33203         }
33204         if(!result){
33205             this.proxyTop.hide();
33206             this.proxyBottom.hide();
33207         }
33208         return result ? this.dropAllowed : this.dropNotAllowed;
33209     },
33210
33211     onNodeOut : function(n, dd, e, data){
33212         this.proxyTop.hide();
33213         this.proxyBottom.hide();
33214     },
33215
33216     onNodeDrop : function(n, dd, e, data){
33217         var h = data.header;
33218         if(h != n){
33219             var cm = this.grid.colModel;
33220             var x = Roo.lib.Event.getPageX(e);
33221             var r = Roo.lib.Dom.getRegion(n.firstChild);
33222             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33223             var oldIndex = this.view.getCellIndex(h);
33224             var newIndex = this.view.getCellIndex(n);
33225             var locked = cm.isLocked(newIndex);
33226             if(pt == "after"){
33227                 newIndex++;
33228             }
33229             if(oldIndex < newIndex){
33230                 newIndex--;
33231             }
33232             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33233                 return false;
33234             }
33235             cm.setLocked(oldIndex, locked, true);
33236             cm.moveColumn(oldIndex, newIndex);
33237             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33238             return true;
33239         }
33240         return false;
33241     }
33242 });
33243 /*
33244  * Based on:
33245  * Ext JS Library 1.1.1
33246  * Copyright(c) 2006-2007, Ext JS, LLC.
33247  *
33248  * Originally Released Under LGPL - original licence link has changed is not relivant.
33249  *
33250  * Fork - LGPL
33251  * <script type="text/javascript">
33252  */
33253   
33254 /**
33255  * @class Roo.grid.GridView
33256  * @extends Roo.util.Observable
33257  *
33258  * @constructor
33259  * @param {Object} config
33260  */
33261 Roo.grid.GridView = function(config){
33262     Roo.grid.GridView.superclass.constructor.call(this);
33263     this.el = null;
33264
33265     Roo.apply(this, config);
33266 };
33267
33268 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33269
33270     unselectable :  'unselectable="on"',
33271     unselectableCls :  'x-unselectable',
33272     
33273     
33274     rowClass : "x-grid-row",
33275
33276     cellClass : "x-grid-col",
33277
33278     tdClass : "x-grid-td",
33279
33280     hdClass : "x-grid-hd",
33281
33282     splitClass : "x-grid-split",
33283
33284     sortClasses : ["sort-asc", "sort-desc"],
33285
33286     enableMoveAnim : false,
33287
33288     hlColor: "C3DAF9",
33289
33290     dh : Roo.DomHelper,
33291
33292     fly : Roo.Element.fly,
33293
33294     css : Roo.util.CSS,
33295
33296     borderWidth: 1,
33297
33298     splitOffset: 3,
33299
33300     scrollIncrement : 22,
33301
33302     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33303
33304     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33305
33306     bind : function(ds, cm){
33307         if(this.ds){
33308             this.ds.un("load", this.onLoad, this);
33309             this.ds.un("datachanged", this.onDataChange, this);
33310             this.ds.un("add", this.onAdd, this);
33311             this.ds.un("remove", this.onRemove, this);
33312             this.ds.un("update", this.onUpdate, this);
33313             this.ds.un("clear", this.onClear, this);
33314         }
33315         if(ds){
33316             ds.on("load", this.onLoad, this);
33317             ds.on("datachanged", this.onDataChange, this);
33318             ds.on("add", this.onAdd, this);
33319             ds.on("remove", this.onRemove, this);
33320             ds.on("update", this.onUpdate, this);
33321             ds.on("clear", this.onClear, this);
33322         }
33323         this.ds = ds;
33324
33325         if(this.cm){
33326             this.cm.un("widthchange", this.onColWidthChange, this);
33327             this.cm.un("headerchange", this.onHeaderChange, this);
33328             this.cm.un("hiddenchange", this.onHiddenChange, this);
33329             this.cm.un("columnmoved", this.onColumnMove, this);
33330             this.cm.un("columnlockchange", this.onColumnLock, this);
33331         }
33332         if(cm){
33333             this.generateRules(cm);
33334             cm.on("widthchange", this.onColWidthChange, this);
33335             cm.on("headerchange", this.onHeaderChange, this);
33336             cm.on("hiddenchange", this.onHiddenChange, this);
33337             cm.on("columnmoved", this.onColumnMove, this);
33338             cm.on("columnlockchange", this.onColumnLock, this);
33339         }
33340         this.cm = cm;
33341     },
33342
33343     init: function(grid){
33344         Roo.grid.GridView.superclass.init.call(this, grid);
33345
33346         this.bind(grid.dataSource, grid.colModel);
33347
33348         grid.on("headerclick", this.handleHeaderClick, this);
33349
33350         if(grid.trackMouseOver){
33351             grid.on("mouseover", this.onRowOver, this);
33352             grid.on("mouseout", this.onRowOut, this);
33353         }
33354         grid.cancelTextSelection = function(){};
33355         this.gridId = grid.id;
33356
33357         var tpls = this.templates || {};
33358
33359         if(!tpls.master){
33360             tpls.master = new Roo.Template(
33361                '<div class="x-grid" hidefocus="true">',
33362                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33363                   '<div class="x-grid-topbar"></div>',
33364                   '<div class="x-grid-scroller"><div></div></div>',
33365                   '<div class="x-grid-locked">',
33366                       '<div class="x-grid-header">{lockedHeader}</div>',
33367                       '<div class="x-grid-body">{lockedBody}</div>',
33368                   "</div>",
33369                   '<div class="x-grid-viewport">',
33370                       '<div class="x-grid-header">{header}</div>',
33371                       '<div class="x-grid-body">{body}</div>',
33372                   "</div>",
33373                   '<div class="x-grid-bottombar"></div>',
33374                  
33375                   '<div class="x-grid-resize-proxy">&#160;</div>',
33376                "</div>"
33377             );
33378             tpls.master.disableformats = true;
33379         }
33380
33381         if(!tpls.header){
33382             tpls.header = new Roo.Template(
33383                '<table border="0" cellspacing="0" cellpadding="0">',
33384                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33385                "</table>{splits}"
33386             );
33387             tpls.header.disableformats = true;
33388         }
33389         tpls.header.compile();
33390
33391         if(!tpls.hcell){
33392             tpls.hcell = new Roo.Template(
33393                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33394                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33395                 "</div></td>"
33396              );
33397              tpls.hcell.disableFormats = true;
33398         }
33399         tpls.hcell.compile();
33400
33401         if(!tpls.hsplit){
33402             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33403                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33404             tpls.hsplit.disableFormats = true;
33405         }
33406         tpls.hsplit.compile();
33407
33408         if(!tpls.body){
33409             tpls.body = new Roo.Template(
33410                '<table border="0" cellspacing="0" cellpadding="0">',
33411                "<tbody>{rows}</tbody>",
33412                "</table>"
33413             );
33414             tpls.body.disableFormats = true;
33415         }
33416         tpls.body.compile();
33417
33418         if(!tpls.row){
33419             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33420             tpls.row.disableFormats = true;
33421         }
33422         tpls.row.compile();
33423
33424         if(!tpls.cell){
33425             tpls.cell = new Roo.Template(
33426                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33427                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33428                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33429                 "</td>"
33430             );
33431             tpls.cell.disableFormats = true;
33432         }
33433         tpls.cell.compile();
33434
33435         this.templates = tpls;
33436     },
33437
33438     // remap these for backwards compat
33439     onColWidthChange : function(){
33440         this.updateColumns.apply(this, arguments);
33441     },
33442     onHeaderChange : function(){
33443         this.updateHeaders.apply(this, arguments);
33444     }, 
33445     onHiddenChange : function(){
33446         this.handleHiddenChange.apply(this, arguments);
33447     },
33448     onColumnMove : function(){
33449         this.handleColumnMove.apply(this, arguments);
33450     },
33451     onColumnLock : function(){
33452         this.handleLockChange.apply(this, arguments);
33453     },
33454
33455     onDataChange : function(){
33456         this.refresh();
33457         this.updateHeaderSortState();
33458     },
33459
33460     onClear : function(){
33461         this.refresh();
33462     },
33463
33464     onUpdate : function(ds, record){
33465         this.refreshRow(record);
33466     },
33467
33468     refreshRow : function(record){
33469         var ds = this.ds, index;
33470         if(typeof record == 'number'){
33471             index = record;
33472             record = ds.getAt(index);
33473         }else{
33474             index = ds.indexOf(record);
33475         }
33476         this.insertRows(ds, index, index, true);
33477         this.onRemove(ds, record, index+1, true);
33478         this.syncRowHeights(index, index);
33479         this.layout();
33480         this.fireEvent("rowupdated", this, index, record);
33481     },
33482
33483     onAdd : function(ds, records, index){
33484         this.insertRows(ds, index, index + (records.length-1));
33485     },
33486
33487     onRemove : function(ds, record, index, isUpdate){
33488         if(isUpdate !== true){
33489             this.fireEvent("beforerowremoved", this, index, record);
33490         }
33491         var bt = this.getBodyTable(), lt = this.getLockedTable();
33492         if(bt.rows[index]){
33493             bt.firstChild.removeChild(bt.rows[index]);
33494         }
33495         if(lt.rows[index]){
33496             lt.firstChild.removeChild(lt.rows[index]);
33497         }
33498         if(isUpdate !== true){
33499             this.stripeRows(index);
33500             this.syncRowHeights(index, index);
33501             this.layout();
33502             this.fireEvent("rowremoved", this, index, record);
33503         }
33504     },
33505
33506     onLoad : function(){
33507         this.scrollToTop();
33508     },
33509
33510     /**
33511      * Scrolls the grid to the top
33512      */
33513     scrollToTop : function(){
33514         if(this.scroller){
33515             this.scroller.dom.scrollTop = 0;
33516             this.syncScroll();
33517         }
33518     },
33519
33520     /**
33521      * Gets a panel in the header of the grid that can be used for toolbars etc.
33522      * After modifying the contents of this panel a call to grid.autoSize() may be
33523      * required to register any changes in size.
33524      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33525      * @return Roo.Element
33526      */
33527     getHeaderPanel : function(doShow){
33528         if(doShow){
33529             this.headerPanel.show();
33530         }
33531         return this.headerPanel;
33532     },
33533
33534     /**
33535      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33536      * After modifying the contents of this panel a call to grid.autoSize() may be
33537      * required to register any changes in size.
33538      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33539      * @return Roo.Element
33540      */
33541     getFooterPanel : function(doShow){
33542         if(doShow){
33543             this.footerPanel.show();
33544         }
33545         return this.footerPanel;
33546     },
33547
33548     initElements : function(){
33549         var E = Roo.Element;
33550         var el = this.grid.getGridEl().dom.firstChild;
33551         var cs = el.childNodes;
33552
33553         this.el = new E(el);
33554         
33555          this.focusEl = new E(el.firstChild);
33556         this.focusEl.swallowEvent("click", true);
33557         
33558         this.headerPanel = new E(cs[1]);
33559         this.headerPanel.enableDisplayMode("block");
33560
33561         this.scroller = new E(cs[2]);
33562         this.scrollSizer = new E(this.scroller.dom.firstChild);
33563
33564         this.lockedWrap = new E(cs[3]);
33565         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33566         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33567
33568         this.mainWrap = new E(cs[4]);
33569         this.mainHd = new E(this.mainWrap.dom.firstChild);
33570         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33571
33572         this.footerPanel = new E(cs[5]);
33573         this.footerPanel.enableDisplayMode("block");
33574
33575         this.resizeProxy = new E(cs[6]);
33576
33577         this.headerSelector = String.format(
33578            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33579            this.lockedHd.id, this.mainHd.id
33580         );
33581
33582         this.splitterSelector = String.format(
33583            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33584            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33585         );
33586     },
33587     idToCssName : function(s)
33588     {
33589         return s.replace(/[^a-z0-9]+/ig, '-');
33590     },
33591
33592     getHeaderCell : function(index){
33593         return Roo.DomQuery.select(this.headerSelector)[index];
33594     },
33595
33596     getHeaderCellMeasure : function(index){
33597         return this.getHeaderCell(index).firstChild;
33598     },
33599
33600     getHeaderCellText : function(index){
33601         return this.getHeaderCell(index).firstChild.firstChild;
33602     },
33603
33604     getLockedTable : function(){
33605         return this.lockedBody.dom.firstChild;
33606     },
33607
33608     getBodyTable : function(){
33609         return this.mainBody.dom.firstChild;
33610     },
33611
33612     getLockedRow : function(index){
33613         return this.getLockedTable().rows[index];
33614     },
33615
33616     getRow : function(index){
33617         return this.getBodyTable().rows[index];
33618     },
33619
33620     getRowComposite : function(index){
33621         if(!this.rowEl){
33622             this.rowEl = new Roo.CompositeElementLite();
33623         }
33624         var els = [], lrow, mrow;
33625         if(lrow = this.getLockedRow(index)){
33626             els.push(lrow);
33627         }
33628         if(mrow = this.getRow(index)){
33629             els.push(mrow);
33630         }
33631         this.rowEl.elements = els;
33632         return this.rowEl;
33633     },
33634     /**
33635      * Gets the 'td' of the cell
33636      * 
33637      * @param {Integer} rowIndex row to select
33638      * @param {Integer} colIndex column to select
33639      * 
33640      * @return {Object} 
33641      */
33642     getCell : function(rowIndex, colIndex){
33643         var locked = this.cm.getLockedCount();
33644         var source;
33645         if(colIndex < locked){
33646             source = this.lockedBody.dom.firstChild;
33647         }else{
33648             source = this.mainBody.dom.firstChild;
33649             colIndex -= locked;
33650         }
33651         return source.rows[rowIndex].childNodes[colIndex];
33652     },
33653
33654     getCellText : function(rowIndex, colIndex){
33655         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33656     },
33657
33658     getCellBox : function(cell){
33659         var b = this.fly(cell).getBox();
33660         if(Roo.isOpera){ // opera fails to report the Y
33661             b.y = cell.offsetTop + this.mainBody.getY();
33662         }
33663         return b;
33664     },
33665
33666     getCellIndex : function(cell){
33667         var id = String(cell.className).match(this.cellRE);
33668         if(id){
33669             return parseInt(id[1], 10);
33670         }
33671         return 0;
33672     },
33673
33674     findHeaderIndex : function(n){
33675         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33676         return r ? this.getCellIndex(r) : false;
33677     },
33678
33679     findHeaderCell : function(n){
33680         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33681         return r ? r : false;
33682     },
33683
33684     findRowIndex : function(n){
33685         if(!n){
33686             return false;
33687         }
33688         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33689         return r ? r.rowIndex : false;
33690     },
33691
33692     findCellIndex : function(node){
33693         var stop = this.el.dom;
33694         while(node && node != stop){
33695             if(this.findRE.test(node.className)){
33696                 return this.getCellIndex(node);
33697             }
33698             node = node.parentNode;
33699         }
33700         return false;
33701     },
33702
33703     getColumnId : function(index){
33704         return this.cm.getColumnId(index);
33705     },
33706
33707     getSplitters : function()
33708     {
33709         if(this.splitterSelector){
33710            return Roo.DomQuery.select(this.splitterSelector);
33711         }else{
33712             return null;
33713       }
33714     },
33715
33716     getSplitter : function(index){
33717         return this.getSplitters()[index];
33718     },
33719
33720     onRowOver : function(e, t){
33721         var row;
33722         if((row = this.findRowIndex(t)) !== false){
33723             this.getRowComposite(row).addClass("x-grid-row-over");
33724         }
33725     },
33726
33727     onRowOut : function(e, t){
33728         var row;
33729         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33730             this.getRowComposite(row).removeClass("x-grid-row-over");
33731         }
33732     },
33733
33734     renderHeaders : function(){
33735         var cm = this.cm;
33736         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33737         var cb = [], lb = [], sb = [], lsb = [], p = {};
33738         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33739             p.cellId = "x-grid-hd-0-" + i;
33740             p.splitId = "x-grid-csplit-0-" + i;
33741             p.id = cm.getColumnId(i);
33742             p.value = cm.getColumnHeader(i) || "";
33743             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33744             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33745             if(!cm.isLocked(i)){
33746                 cb[cb.length] = ct.apply(p);
33747                 sb[sb.length] = st.apply(p);
33748             }else{
33749                 lb[lb.length] = ct.apply(p);
33750                 lsb[lsb.length] = st.apply(p);
33751             }
33752         }
33753         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33754                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33755     },
33756
33757     updateHeaders : function(){
33758         var html = this.renderHeaders();
33759         this.lockedHd.update(html[0]);
33760         this.mainHd.update(html[1]);
33761     },
33762
33763     /**
33764      * Focuses the specified row.
33765      * @param {Number} row The row index
33766      */
33767     focusRow : function(row)
33768     {
33769         //Roo.log('GridView.focusRow');
33770         var x = this.scroller.dom.scrollLeft;
33771         this.focusCell(row, 0, false);
33772         this.scroller.dom.scrollLeft = x;
33773     },
33774
33775     /**
33776      * Focuses the specified cell.
33777      * @param {Number} row The row index
33778      * @param {Number} col The column index
33779      * @param {Boolean} hscroll false to disable horizontal scrolling
33780      */
33781     focusCell : function(row, col, hscroll)
33782     {
33783         //Roo.log('GridView.focusCell');
33784         var el = this.ensureVisible(row, col, hscroll);
33785         this.focusEl.alignTo(el, "tl-tl");
33786         if(Roo.isGecko){
33787             this.focusEl.focus();
33788         }else{
33789             this.focusEl.focus.defer(1, this.focusEl);
33790         }
33791     },
33792
33793     /**
33794      * Scrolls the specified cell into view
33795      * @param {Number} row The row index
33796      * @param {Number} col The column index
33797      * @param {Boolean} hscroll false to disable horizontal scrolling
33798      */
33799     ensureVisible : function(row, col, hscroll)
33800     {
33801         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33802         //return null; //disable for testing.
33803         if(typeof row != "number"){
33804             row = row.rowIndex;
33805         }
33806         if(row < 0 && row >= this.ds.getCount()){
33807             return  null;
33808         }
33809         col = (col !== undefined ? col : 0);
33810         var cm = this.grid.colModel;
33811         while(cm.isHidden(col)){
33812             col++;
33813         }
33814
33815         var el = this.getCell(row, col);
33816         if(!el){
33817             return null;
33818         }
33819         var c = this.scroller.dom;
33820
33821         var ctop = parseInt(el.offsetTop, 10);
33822         var cleft = parseInt(el.offsetLeft, 10);
33823         var cbot = ctop + el.offsetHeight;
33824         var cright = cleft + el.offsetWidth;
33825         
33826         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33827         var stop = parseInt(c.scrollTop, 10);
33828         var sleft = parseInt(c.scrollLeft, 10);
33829         var sbot = stop + ch;
33830         var sright = sleft + c.clientWidth;
33831         /*
33832         Roo.log('GridView.ensureVisible:' +
33833                 ' ctop:' + ctop +
33834                 ' c.clientHeight:' + c.clientHeight +
33835                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33836                 ' stop:' + stop +
33837                 ' cbot:' + cbot +
33838                 ' sbot:' + sbot +
33839                 ' ch:' + ch  
33840                 );
33841         */
33842         if(ctop < stop){
33843             c.scrollTop = ctop;
33844             //Roo.log("set scrolltop to ctop DISABLE?");
33845         }else if(cbot > sbot){
33846             //Roo.log("set scrolltop to cbot-ch");
33847             c.scrollTop = cbot-ch;
33848         }
33849         
33850         if(hscroll !== false){
33851             if(cleft < sleft){
33852                 c.scrollLeft = cleft;
33853             }else if(cright > sright){
33854                 c.scrollLeft = cright-c.clientWidth;
33855             }
33856         }
33857          
33858         return el;
33859     },
33860
33861     updateColumns : function(){
33862         this.grid.stopEditing();
33863         var cm = this.grid.colModel, colIds = this.getColumnIds();
33864         //var totalWidth = cm.getTotalWidth();
33865         var pos = 0;
33866         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33867             //if(cm.isHidden(i)) continue;
33868             var w = cm.getColumnWidth(i);
33869             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33870             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33871         }
33872         this.updateSplitters();
33873     },
33874
33875     generateRules : function(cm){
33876         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33877         Roo.util.CSS.removeStyleSheet(rulesId);
33878         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33879             var cid = cm.getColumnId(i);
33880             var align = '';
33881             if(cm.config[i].align){
33882                 align = 'text-align:'+cm.config[i].align+';';
33883             }
33884             var hidden = '';
33885             if(cm.isHidden(i)){
33886                 hidden = 'display:none;';
33887             }
33888             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33889             ruleBuf.push(
33890                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33891                     this.hdSelector, cid, " {\n", align, width, "}\n",
33892                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33893                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33894         }
33895         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33896     },
33897
33898     updateSplitters : function(){
33899         var cm = this.cm, s = this.getSplitters();
33900         if(s){ // splitters not created yet
33901             var pos = 0, locked = true;
33902             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33903                 if(cm.isHidden(i)) {
33904                     continue;
33905                 }
33906                 var w = cm.getColumnWidth(i); // make sure it's a number
33907                 if(!cm.isLocked(i) && locked){
33908                     pos = 0;
33909                     locked = false;
33910                 }
33911                 pos += w;
33912                 s[i].style.left = (pos-this.splitOffset) + "px";
33913             }
33914         }
33915     },
33916
33917     handleHiddenChange : function(colModel, colIndex, hidden){
33918         if(hidden){
33919             this.hideColumn(colIndex);
33920         }else{
33921             this.unhideColumn(colIndex);
33922         }
33923     },
33924
33925     hideColumn : function(colIndex){
33926         var cid = this.getColumnId(colIndex);
33927         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33928         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33929         if(Roo.isSafari){
33930             this.updateHeaders();
33931         }
33932         this.updateSplitters();
33933         this.layout();
33934     },
33935
33936     unhideColumn : function(colIndex){
33937         var cid = this.getColumnId(colIndex);
33938         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33939         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33940
33941         if(Roo.isSafari){
33942             this.updateHeaders();
33943         }
33944         this.updateSplitters();
33945         this.layout();
33946     },
33947
33948     insertRows : function(dm, firstRow, lastRow, isUpdate){
33949         if(firstRow == 0 && lastRow == dm.getCount()-1){
33950             this.refresh();
33951         }else{
33952             if(!isUpdate){
33953                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33954             }
33955             var s = this.getScrollState();
33956             var markup = this.renderRows(firstRow, lastRow);
33957             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33958             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33959             this.restoreScroll(s);
33960             if(!isUpdate){
33961                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33962                 this.syncRowHeights(firstRow, lastRow);
33963                 this.stripeRows(firstRow);
33964                 this.layout();
33965             }
33966         }
33967     },
33968
33969     bufferRows : function(markup, target, index){
33970         var before = null, trows = target.rows, tbody = target.tBodies[0];
33971         if(index < trows.length){
33972             before = trows[index];
33973         }
33974         var b = document.createElement("div");
33975         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33976         var rows = b.firstChild.rows;
33977         for(var i = 0, len = rows.length; i < len; i++){
33978             if(before){
33979                 tbody.insertBefore(rows[0], before);
33980             }else{
33981                 tbody.appendChild(rows[0]);
33982             }
33983         }
33984         b.innerHTML = "";
33985         b = null;
33986     },
33987
33988     deleteRows : function(dm, firstRow, lastRow){
33989         if(dm.getRowCount()<1){
33990             this.fireEvent("beforerefresh", this);
33991             this.mainBody.update("");
33992             this.lockedBody.update("");
33993             this.fireEvent("refresh", this);
33994         }else{
33995             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33996             var bt = this.getBodyTable();
33997             var tbody = bt.firstChild;
33998             var rows = bt.rows;
33999             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34000                 tbody.removeChild(rows[firstRow]);
34001             }
34002             this.stripeRows(firstRow);
34003             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34004         }
34005     },
34006
34007     updateRows : function(dataSource, firstRow, lastRow){
34008         var s = this.getScrollState();
34009         this.refresh();
34010         this.restoreScroll(s);
34011     },
34012
34013     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34014         if(!noRefresh){
34015            this.refresh();
34016         }
34017         this.updateHeaderSortState();
34018     },
34019
34020     getScrollState : function(){
34021         
34022         var sb = this.scroller.dom;
34023         return {left: sb.scrollLeft, top: sb.scrollTop};
34024     },
34025
34026     stripeRows : function(startRow){
34027         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34028             return;
34029         }
34030         startRow = startRow || 0;
34031         var rows = this.getBodyTable().rows;
34032         var lrows = this.getLockedTable().rows;
34033         var cls = ' x-grid-row-alt ';
34034         for(var i = startRow, len = rows.length; i < len; i++){
34035             var row = rows[i], lrow = lrows[i];
34036             var isAlt = ((i+1) % 2 == 0);
34037             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34038             if(isAlt == hasAlt){
34039                 continue;
34040             }
34041             if(isAlt){
34042                 row.className += " x-grid-row-alt";
34043             }else{
34044                 row.className = row.className.replace("x-grid-row-alt", "");
34045             }
34046             if(lrow){
34047                 lrow.className = row.className;
34048             }
34049         }
34050     },
34051
34052     restoreScroll : function(state){
34053         //Roo.log('GridView.restoreScroll');
34054         var sb = this.scroller.dom;
34055         sb.scrollLeft = state.left;
34056         sb.scrollTop = state.top;
34057         this.syncScroll();
34058     },
34059
34060     syncScroll : function(){
34061         //Roo.log('GridView.syncScroll');
34062         var sb = this.scroller.dom;
34063         var sh = this.mainHd.dom;
34064         var bs = this.mainBody.dom;
34065         var lv = this.lockedBody.dom;
34066         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34067         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34068     },
34069
34070     handleScroll : function(e){
34071         this.syncScroll();
34072         var sb = this.scroller.dom;
34073         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34074         e.stopEvent();
34075     },
34076
34077     handleWheel : function(e){
34078         var d = e.getWheelDelta();
34079         this.scroller.dom.scrollTop -= d*22;
34080         // set this here to prevent jumpy scrolling on large tables
34081         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34082         e.stopEvent();
34083     },
34084
34085     renderRows : function(startRow, endRow){
34086         // pull in all the crap needed to render rows
34087         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34088         var colCount = cm.getColumnCount();
34089
34090         if(ds.getCount() < 1){
34091             return ["", ""];
34092         }
34093
34094         // build a map for all the columns
34095         var cs = [];
34096         for(var i = 0; i < colCount; i++){
34097             var name = cm.getDataIndex(i);
34098             cs[i] = {
34099                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34100                 renderer : cm.getRenderer(i),
34101                 id : cm.getColumnId(i),
34102                 locked : cm.isLocked(i),
34103                 has_editor : cm.isCellEditable(i)
34104             };
34105         }
34106
34107         startRow = startRow || 0;
34108         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34109
34110         // records to render
34111         var rs = ds.getRange(startRow, endRow);
34112
34113         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34114     },
34115
34116     // As much as I hate to duplicate code, this was branched because FireFox really hates
34117     // [].join("") on strings. The performance difference was substantial enough to
34118     // branch this function
34119     doRender : Roo.isGecko ?
34120             function(cs, rs, ds, startRow, colCount, stripe){
34121                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34122                 // buffers
34123                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34124                 
34125                 var hasListener = this.grid.hasListener('rowclass');
34126                 var rowcfg = {};
34127                 for(var j = 0, len = rs.length; j < len; j++){
34128                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34129                     for(var i = 0; i < colCount; i++){
34130                         c = cs[i];
34131                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34132                         p.id = c.id;
34133                         p.css = p.attr = "";
34134                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34135                         if(p.value == undefined || p.value === "") {
34136                             p.value = "&#160;";
34137                         }
34138                         if(c.has_editor){
34139                             p.css += ' x-grid-editable-cell';
34140                         }
34141                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34142                             p.css +=  ' x-grid-dirty-cell';
34143                         }
34144                         var markup = ct.apply(p);
34145                         if(!c.locked){
34146                             cb+= markup;
34147                         }else{
34148                             lcb+= markup;
34149                         }
34150                     }
34151                     var alt = [];
34152                     if(stripe && ((rowIndex+1) % 2 == 0)){
34153                         alt.push("x-grid-row-alt")
34154                     }
34155                     if(r.dirty){
34156                         alt.push(  " x-grid-dirty-row");
34157                     }
34158                     rp.cells = lcb;
34159                     if(this.getRowClass){
34160                         alt.push(this.getRowClass(r, rowIndex));
34161                     }
34162                     if (hasListener) {
34163                         rowcfg = {
34164                              
34165                             record: r,
34166                             rowIndex : rowIndex,
34167                             rowClass : ''
34168                         };
34169                         this.grid.fireEvent('rowclass', this, rowcfg);
34170                         alt.push(rowcfg.rowClass);
34171                     }
34172                     rp.alt = alt.join(" ");
34173                     lbuf+= rt.apply(rp);
34174                     rp.cells = cb;
34175                     buf+=  rt.apply(rp);
34176                 }
34177                 return [lbuf, buf];
34178             } :
34179             function(cs, rs, ds, startRow, colCount, stripe){
34180                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34181                 // buffers
34182                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34183                 var hasListener = this.grid.hasListener('rowclass');
34184  
34185                 var rowcfg = {};
34186                 for(var j = 0, len = rs.length; j < len; j++){
34187                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34188                     for(var i = 0; i < colCount; i++){
34189                         c = cs[i];
34190                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34191                         p.id = c.id;
34192                         p.css = p.attr = "";
34193                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34194                         if(p.value == undefined || p.value === "") {
34195                             p.value = "&#160;";
34196                         }
34197                         //Roo.log(c);
34198                          if(c.has_editor){
34199                             p.css += ' x-grid-editable-cell';
34200                         }
34201                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34202                             p.css += ' x-grid-dirty-cell' 
34203                         }
34204                         
34205                         var markup = ct.apply(p);
34206                         if(!c.locked){
34207                             cb[cb.length] = markup;
34208                         }else{
34209                             lcb[lcb.length] = markup;
34210                         }
34211                     }
34212                     var alt = [];
34213                     if(stripe && ((rowIndex+1) % 2 == 0)){
34214                         alt.push( "x-grid-row-alt");
34215                     }
34216                     if(r.dirty){
34217                         alt.push(" x-grid-dirty-row");
34218                     }
34219                     rp.cells = lcb;
34220                     if(this.getRowClass){
34221                         alt.push( this.getRowClass(r, rowIndex));
34222                     }
34223                     if (hasListener) {
34224                         rowcfg = {
34225                              
34226                             record: r,
34227                             rowIndex : rowIndex,
34228                             rowClass : ''
34229                         };
34230                         this.grid.fireEvent('rowclass', this, rowcfg);
34231                         alt.push(rowcfg.rowClass);
34232                     }
34233                     
34234                     rp.alt = alt.join(" ");
34235                     rp.cells = lcb.join("");
34236                     lbuf[lbuf.length] = rt.apply(rp);
34237                     rp.cells = cb.join("");
34238                     buf[buf.length] =  rt.apply(rp);
34239                 }
34240                 return [lbuf.join(""), buf.join("")];
34241             },
34242
34243     renderBody : function(){
34244         var markup = this.renderRows();
34245         var bt = this.templates.body;
34246         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34247     },
34248
34249     /**
34250      * Refreshes the grid
34251      * @param {Boolean} headersToo
34252      */
34253     refresh : function(headersToo){
34254         this.fireEvent("beforerefresh", this);
34255         this.grid.stopEditing();
34256         var result = this.renderBody();
34257         this.lockedBody.update(result[0]);
34258         this.mainBody.update(result[1]);
34259         if(headersToo === true){
34260             this.updateHeaders();
34261             this.updateColumns();
34262             this.updateSplitters();
34263             this.updateHeaderSortState();
34264         }
34265         this.syncRowHeights();
34266         this.layout();
34267         this.fireEvent("refresh", this);
34268     },
34269
34270     handleColumnMove : function(cm, oldIndex, newIndex){
34271         this.indexMap = null;
34272         var s = this.getScrollState();
34273         this.refresh(true);
34274         this.restoreScroll(s);
34275         this.afterMove(newIndex);
34276     },
34277
34278     afterMove : function(colIndex){
34279         if(this.enableMoveAnim && Roo.enableFx){
34280             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34281         }
34282         // if multisort - fix sortOrder, and reload..
34283         if (this.grid.dataSource.multiSort) {
34284             // the we can call sort again..
34285             var dm = this.grid.dataSource;
34286             var cm = this.grid.colModel;
34287             var so = [];
34288             for(var i = 0; i < cm.config.length; i++ ) {
34289                 
34290                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34291                     continue; // dont' bother, it's not in sort list or being set.
34292                 }
34293                 
34294                 so.push(cm.config[i].dataIndex);
34295             };
34296             dm.sortOrder = so;
34297             dm.load(dm.lastOptions);
34298             
34299             
34300         }
34301         
34302     },
34303
34304     updateCell : function(dm, rowIndex, dataIndex){
34305         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34306         if(typeof colIndex == "undefined"){ // not present in grid
34307             return;
34308         }
34309         var cm = this.grid.colModel;
34310         var cell = this.getCell(rowIndex, colIndex);
34311         var cellText = this.getCellText(rowIndex, colIndex);
34312
34313         var p = {
34314             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34315             id : cm.getColumnId(colIndex),
34316             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34317         };
34318         var renderer = cm.getRenderer(colIndex);
34319         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34320         if(typeof val == "undefined" || val === "") {
34321             val = "&#160;";
34322         }
34323         cellText.innerHTML = val;
34324         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34325         this.syncRowHeights(rowIndex, rowIndex);
34326     },
34327
34328     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34329         var maxWidth = 0;
34330         if(this.grid.autoSizeHeaders){
34331             var h = this.getHeaderCellMeasure(colIndex);
34332             maxWidth = Math.max(maxWidth, h.scrollWidth);
34333         }
34334         var tb, index;
34335         if(this.cm.isLocked(colIndex)){
34336             tb = this.getLockedTable();
34337             index = colIndex;
34338         }else{
34339             tb = this.getBodyTable();
34340             index = colIndex - this.cm.getLockedCount();
34341         }
34342         if(tb && tb.rows){
34343             var rows = tb.rows;
34344             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34345             for(var i = 0; i < stopIndex; i++){
34346                 var cell = rows[i].childNodes[index].firstChild;
34347                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34348             }
34349         }
34350         return maxWidth + /*margin for error in IE*/ 5;
34351     },
34352     /**
34353      * Autofit a column to its content.
34354      * @param {Number} colIndex
34355      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34356      */
34357      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34358          if(this.cm.isHidden(colIndex)){
34359              return; // can't calc a hidden column
34360          }
34361         if(forceMinSize){
34362             var cid = this.cm.getColumnId(colIndex);
34363             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34364            if(this.grid.autoSizeHeaders){
34365                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34366            }
34367         }
34368         var newWidth = this.calcColumnWidth(colIndex);
34369         this.cm.setColumnWidth(colIndex,
34370             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34371         if(!suppressEvent){
34372             this.grid.fireEvent("columnresize", colIndex, newWidth);
34373         }
34374     },
34375
34376     /**
34377      * Autofits all columns to their content and then expands to fit any extra space in the grid
34378      */
34379      autoSizeColumns : function(){
34380         var cm = this.grid.colModel;
34381         var colCount = cm.getColumnCount();
34382         for(var i = 0; i < colCount; i++){
34383             this.autoSizeColumn(i, true, true);
34384         }
34385         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34386             this.fitColumns();
34387         }else{
34388             this.updateColumns();
34389             this.layout();
34390         }
34391     },
34392
34393     /**
34394      * Autofits all columns to the grid's width proportionate with their current size
34395      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34396      */
34397     fitColumns : function(reserveScrollSpace){
34398         var cm = this.grid.colModel;
34399         var colCount = cm.getColumnCount();
34400         var cols = [];
34401         var width = 0;
34402         var i, w;
34403         for (i = 0; i < colCount; i++){
34404             if(!cm.isHidden(i) && !cm.isFixed(i)){
34405                 w = cm.getColumnWidth(i);
34406                 cols.push(i);
34407                 cols.push(w);
34408                 width += w;
34409             }
34410         }
34411         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34412         if(reserveScrollSpace){
34413             avail -= 17;
34414         }
34415         var frac = (avail - cm.getTotalWidth())/width;
34416         while (cols.length){
34417             w = cols.pop();
34418             i = cols.pop();
34419             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34420         }
34421         this.updateColumns();
34422         this.layout();
34423     },
34424
34425     onRowSelect : function(rowIndex){
34426         var row = this.getRowComposite(rowIndex);
34427         row.addClass("x-grid-row-selected");
34428     },
34429
34430     onRowDeselect : function(rowIndex){
34431         var row = this.getRowComposite(rowIndex);
34432         row.removeClass("x-grid-row-selected");
34433     },
34434
34435     onCellSelect : function(row, col){
34436         var cell = this.getCell(row, col);
34437         if(cell){
34438             Roo.fly(cell).addClass("x-grid-cell-selected");
34439         }
34440     },
34441
34442     onCellDeselect : function(row, col){
34443         var cell = this.getCell(row, col);
34444         if(cell){
34445             Roo.fly(cell).removeClass("x-grid-cell-selected");
34446         }
34447     },
34448
34449     updateHeaderSortState : function(){
34450         
34451         // sort state can be single { field: xxx, direction : yyy}
34452         // or   { xxx=>ASC , yyy : DESC ..... }
34453         
34454         var mstate = {};
34455         if (!this.ds.multiSort) { 
34456             var state = this.ds.getSortState();
34457             if(!state){
34458                 return;
34459             }
34460             mstate[state.field] = state.direction;
34461             // FIXME... - this is not used here.. but might be elsewhere..
34462             this.sortState = state;
34463             
34464         } else {
34465             mstate = this.ds.sortToggle;
34466         }
34467         //remove existing sort classes..
34468         
34469         var sc = this.sortClasses;
34470         var hds = this.el.select(this.headerSelector).removeClass(sc);
34471         
34472         for(var f in mstate) {
34473         
34474             var sortColumn = this.cm.findColumnIndex(f);
34475             
34476             if(sortColumn != -1){
34477                 var sortDir = mstate[f];        
34478                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34479             }
34480         }
34481         
34482          
34483         
34484     },
34485
34486
34487     handleHeaderClick : function(g, index,e){
34488         
34489         Roo.log("header click");
34490         
34491         if (Roo.isTouch) {
34492             // touch events on header are handled by context
34493             this.handleHdCtx(g,index,e);
34494             return;
34495         }
34496         
34497         
34498         if(this.headersDisabled){
34499             return;
34500         }
34501         var dm = g.dataSource, cm = g.colModel;
34502         if(!cm.isSortable(index)){
34503             return;
34504         }
34505         g.stopEditing();
34506         
34507         if (dm.multiSort) {
34508             // update the sortOrder
34509             var so = [];
34510             for(var i = 0; i < cm.config.length; i++ ) {
34511                 
34512                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34513                     continue; // dont' bother, it's not in sort list or being set.
34514                 }
34515                 
34516                 so.push(cm.config[i].dataIndex);
34517             };
34518             dm.sortOrder = so;
34519         }
34520         
34521         
34522         dm.sort(cm.getDataIndex(index));
34523     },
34524
34525
34526     destroy : function(){
34527         if(this.colMenu){
34528             this.colMenu.removeAll();
34529             Roo.menu.MenuMgr.unregister(this.colMenu);
34530             this.colMenu.getEl().remove();
34531             delete this.colMenu;
34532         }
34533         if(this.hmenu){
34534             this.hmenu.removeAll();
34535             Roo.menu.MenuMgr.unregister(this.hmenu);
34536             this.hmenu.getEl().remove();
34537             delete this.hmenu;
34538         }
34539         if(this.grid.enableColumnMove){
34540             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34541             if(dds){
34542                 for(var dd in dds){
34543                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34544                         var elid = dds[dd].dragElId;
34545                         dds[dd].unreg();
34546                         Roo.get(elid).remove();
34547                     } else if(dds[dd].config.isTarget){
34548                         dds[dd].proxyTop.remove();
34549                         dds[dd].proxyBottom.remove();
34550                         dds[dd].unreg();
34551                     }
34552                     if(Roo.dd.DDM.locationCache[dd]){
34553                         delete Roo.dd.DDM.locationCache[dd];
34554                     }
34555                 }
34556                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34557             }
34558         }
34559         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34560         this.bind(null, null);
34561         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34562     },
34563
34564     handleLockChange : function(){
34565         this.refresh(true);
34566     },
34567
34568     onDenyColumnLock : function(){
34569
34570     },
34571
34572     onDenyColumnHide : function(){
34573
34574     },
34575
34576     handleHdMenuClick : function(item){
34577         var index = this.hdCtxIndex;
34578         var cm = this.cm, ds = this.ds;
34579         switch(item.id){
34580             case "asc":
34581                 ds.sort(cm.getDataIndex(index), "ASC");
34582                 break;
34583             case "desc":
34584                 ds.sort(cm.getDataIndex(index), "DESC");
34585                 break;
34586             case "lock":
34587                 var lc = cm.getLockedCount();
34588                 if(cm.getColumnCount(true) <= lc+1){
34589                     this.onDenyColumnLock();
34590                     return;
34591                 }
34592                 if(lc != index){
34593                     cm.setLocked(index, true, true);
34594                     cm.moveColumn(index, lc);
34595                     this.grid.fireEvent("columnmove", index, lc);
34596                 }else{
34597                     cm.setLocked(index, true);
34598                 }
34599             break;
34600             case "unlock":
34601                 var lc = cm.getLockedCount();
34602                 if((lc-1) != index){
34603                     cm.setLocked(index, false, true);
34604                     cm.moveColumn(index, lc-1);
34605                     this.grid.fireEvent("columnmove", index, lc-1);
34606                 }else{
34607                     cm.setLocked(index, false);
34608                 }
34609             break;
34610             case 'wider': // used to expand cols on touch..
34611             case 'narrow':
34612                 var cw = cm.getColumnWidth(index);
34613                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34614                 cw = Math.max(0, cw);
34615                 cw = Math.min(cw,4000);
34616                 cm.setColumnWidth(index, cw);
34617                 break;
34618                 
34619             default:
34620                 index = cm.getIndexById(item.id.substr(4));
34621                 if(index != -1){
34622                     if(item.checked && cm.getColumnCount(true) <= 1){
34623                         this.onDenyColumnHide();
34624                         return false;
34625                     }
34626                     cm.setHidden(index, item.checked);
34627                 }
34628         }
34629         return true;
34630     },
34631
34632     beforeColMenuShow : function(){
34633         var cm = this.cm,  colCount = cm.getColumnCount();
34634         this.colMenu.removeAll();
34635         for(var i = 0; i < colCount; i++){
34636             this.colMenu.add(new Roo.menu.CheckItem({
34637                 id: "col-"+cm.getColumnId(i),
34638                 text: cm.getColumnHeader(i),
34639                 checked: !cm.isHidden(i),
34640                 hideOnClick:false
34641             }));
34642         }
34643     },
34644
34645     handleHdCtx : function(g, index, e){
34646         e.stopEvent();
34647         var hd = this.getHeaderCell(index);
34648         this.hdCtxIndex = index;
34649         var ms = this.hmenu.items, cm = this.cm;
34650         ms.get("asc").setDisabled(!cm.isSortable(index));
34651         ms.get("desc").setDisabled(!cm.isSortable(index));
34652         if(this.grid.enableColLock !== false){
34653             ms.get("lock").setDisabled(cm.isLocked(index));
34654             ms.get("unlock").setDisabled(!cm.isLocked(index));
34655         }
34656         this.hmenu.show(hd, "tl-bl");
34657     },
34658
34659     handleHdOver : function(e){
34660         var hd = this.findHeaderCell(e.getTarget());
34661         if(hd && !this.headersDisabled){
34662             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34663                this.fly(hd).addClass("x-grid-hd-over");
34664             }
34665         }
34666     },
34667
34668     handleHdOut : function(e){
34669         var hd = this.findHeaderCell(e.getTarget());
34670         if(hd){
34671             this.fly(hd).removeClass("x-grid-hd-over");
34672         }
34673     },
34674
34675     handleSplitDblClick : function(e, t){
34676         var i = this.getCellIndex(t);
34677         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34678             this.autoSizeColumn(i, true);
34679             this.layout();
34680         }
34681     },
34682
34683     render : function(){
34684
34685         var cm = this.cm;
34686         var colCount = cm.getColumnCount();
34687
34688         if(this.grid.monitorWindowResize === true){
34689             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34690         }
34691         var header = this.renderHeaders();
34692         var body = this.templates.body.apply({rows:""});
34693         var html = this.templates.master.apply({
34694             lockedBody: body,
34695             body: body,
34696             lockedHeader: header[0],
34697             header: header[1]
34698         });
34699
34700         //this.updateColumns();
34701
34702         this.grid.getGridEl().dom.innerHTML = html;
34703
34704         this.initElements();
34705         
34706         // a kludge to fix the random scolling effect in webkit
34707         this.el.on("scroll", function() {
34708             this.el.dom.scrollTop=0; // hopefully not recursive..
34709         },this);
34710
34711         this.scroller.on("scroll", this.handleScroll, this);
34712         this.lockedBody.on("mousewheel", this.handleWheel, this);
34713         this.mainBody.on("mousewheel", this.handleWheel, this);
34714
34715         this.mainHd.on("mouseover", this.handleHdOver, this);
34716         this.mainHd.on("mouseout", this.handleHdOut, this);
34717         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34718                 {delegate: "."+this.splitClass});
34719
34720         this.lockedHd.on("mouseover", this.handleHdOver, this);
34721         this.lockedHd.on("mouseout", this.handleHdOut, this);
34722         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34723                 {delegate: "."+this.splitClass});
34724
34725         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34726             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34727         }
34728
34729         this.updateSplitters();
34730
34731         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34732             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34733             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34734         }
34735
34736         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34737             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34738             this.hmenu.add(
34739                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34740                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34741             );
34742             if(this.grid.enableColLock !== false){
34743                 this.hmenu.add('-',
34744                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34745                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34746                 );
34747             }
34748             if (Roo.isTouch) {
34749                  this.hmenu.add('-',
34750                     {id:"wider", text: this.columnsWiderText},
34751                     {id:"narrow", text: this.columnsNarrowText }
34752                 );
34753                 
34754                  
34755             }
34756             
34757             if(this.grid.enableColumnHide !== false){
34758
34759                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34760                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34761                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34762
34763                 this.hmenu.add('-',
34764                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34765                 );
34766             }
34767             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34768
34769             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34770         }
34771
34772         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34773             this.dd = new Roo.grid.GridDragZone(this.grid, {
34774                 ddGroup : this.grid.ddGroup || 'GridDD'
34775             });
34776             
34777         }
34778
34779         /*
34780         for(var i = 0; i < colCount; i++){
34781             if(cm.isHidden(i)){
34782                 this.hideColumn(i);
34783             }
34784             if(cm.config[i].align){
34785                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34786                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34787             }
34788         }*/
34789         
34790         this.updateHeaderSortState();
34791
34792         this.beforeInitialResize();
34793         this.layout(true);
34794
34795         // two part rendering gives faster view to the user
34796         this.renderPhase2.defer(1, this);
34797     },
34798
34799     renderPhase2 : function(){
34800         // render the rows now
34801         this.refresh();
34802         if(this.grid.autoSizeColumns){
34803             this.autoSizeColumns();
34804         }
34805     },
34806
34807     beforeInitialResize : function(){
34808
34809     },
34810
34811     onColumnSplitterMoved : function(i, w){
34812         this.userResized = true;
34813         var cm = this.grid.colModel;
34814         cm.setColumnWidth(i, w, true);
34815         var cid = cm.getColumnId(i);
34816         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34817         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34818         this.updateSplitters();
34819         this.layout();
34820         this.grid.fireEvent("columnresize", i, w);
34821     },
34822
34823     syncRowHeights : function(startIndex, endIndex){
34824         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34825             startIndex = startIndex || 0;
34826             var mrows = this.getBodyTable().rows;
34827             var lrows = this.getLockedTable().rows;
34828             var len = mrows.length-1;
34829             endIndex = Math.min(endIndex || len, len);
34830             for(var i = startIndex; i <= endIndex; i++){
34831                 var m = mrows[i], l = lrows[i];
34832                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34833                 m.style.height = l.style.height = h + "px";
34834             }
34835         }
34836     },
34837
34838     layout : function(initialRender, is2ndPass)
34839     {
34840         var g = this.grid;
34841         var auto = g.autoHeight;
34842         var scrollOffset = 16;
34843         var c = g.getGridEl(), cm = this.cm,
34844                 expandCol = g.autoExpandColumn,
34845                 gv = this;
34846         //c.beginMeasure();
34847
34848         if(!c.dom.offsetWidth){ // display:none?
34849             if(initialRender){
34850                 this.lockedWrap.show();
34851                 this.mainWrap.show();
34852             }
34853             return;
34854         }
34855
34856         var hasLock = this.cm.isLocked(0);
34857
34858         var tbh = this.headerPanel.getHeight();
34859         var bbh = this.footerPanel.getHeight();
34860
34861         if(auto){
34862             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34863             var newHeight = ch + c.getBorderWidth("tb");
34864             if(g.maxHeight){
34865                 newHeight = Math.min(g.maxHeight, newHeight);
34866             }
34867             c.setHeight(newHeight);
34868         }
34869
34870         if(g.autoWidth){
34871             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34872         }
34873
34874         var s = this.scroller;
34875
34876         var csize = c.getSize(true);
34877
34878         this.el.setSize(csize.width, csize.height);
34879
34880         this.headerPanel.setWidth(csize.width);
34881         this.footerPanel.setWidth(csize.width);
34882
34883         var hdHeight = this.mainHd.getHeight();
34884         var vw = csize.width;
34885         var vh = csize.height - (tbh + bbh);
34886
34887         s.setSize(vw, vh);
34888
34889         var bt = this.getBodyTable();
34890         
34891         if(cm.getLockedCount() == cm.config.length){
34892             bt = this.getLockedTable();
34893         }
34894         
34895         var ltWidth = hasLock ?
34896                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34897
34898         var scrollHeight = bt.offsetHeight;
34899         var scrollWidth = ltWidth + bt.offsetWidth;
34900         var vscroll = false, hscroll = false;
34901
34902         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34903
34904         var lw = this.lockedWrap, mw = this.mainWrap;
34905         var lb = this.lockedBody, mb = this.mainBody;
34906
34907         setTimeout(function(){
34908             var t = s.dom.offsetTop;
34909             var w = s.dom.clientWidth,
34910                 h = s.dom.clientHeight;
34911
34912             lw.setTop(t);
34913             lw.setSize(ltWidth, h);
34914
34915             mw.setLeftTop(ltWidth, t);
34916             mw.setSize(w-ltWidth, h);
34917
34918             lb.setHeight(h-hdHeight);
34919             mb.setHeight(h-hdHeight);
34920
34921             if(is2ndPass !== true && !gv.userResized && expandCol){
34922                 // high speed resize without full column calculation
34923                 
34924                 var ci = cm.getIndexById(expandCol);
34925                 if (ci < 0) {
34926                     ci = cm.findColumnIndex(expandCol);
34927                 }
34928                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34929                 var expandId = cm.getColumnId(ci);
34930                 var  tw = cm.getTotalWidth(false);
34931                 var currentWidth = cm.getColumnWidth(ci);
34932                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34933                 if(currentWidth != cw){
34934                     cm.setColumnWidth(ci, cw, true);
34935                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34936                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34937                     gv.updateSplitters();
34938                     gv.layout(false, true);
34939                 }
34940             }
34941
34942             if(initialRender){
34943                 lw.show();
34944                 mw.show();
34945             }
34946             //c.endMeasure();
34947         }, 10);
34948     },
34949
34950     onWindowResize : function(){
34951         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34952             return;
34953         }
34954         this.layout();
34955     },
34956
34957     appendFooter : function(parentEl){
34958         return null;
34959     },
34960
34961     sortAscText : "Sort Ascending",
34962     sortDescText : "Sort Descending",
34963     lockText : "Lock Column",
34964     unlockText : "Unlock Column",
34965     columnsText : "Columns",
34966  
34967     columnsWiderText : "Wider",
34968     columnsNarrowText : "Thinner"
34969 });
34970
34971
34972 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34973     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34974     this.proxy.el.addClass('x-grid3-col-dd');
34975 };
34976
34977 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34978     handleMouseDown : function(e){
34979
34980     },
34981
34982     callHandleMouseDown : function(e){
34983         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34984     }
34985 });
34986 /*
34987  * Based on:
34988  * Ext JS Library 1.1.1
34989  * Copyright(c) 2006-2007, Ext JS, LLC.
34990  *
34991  * Originally Released Under LGPL - original licence link has changed is not relivant.
34992  *
34993  * Fork - LGPL
34994  * <script type="text/javascript">
34995  */
34996  /**
34997  * @extends Roo.dd.DDProxy
34998  * @class Roo.grid.SplitDragZone
34999  * Support for Column Header resizing
35000  * @constructor
35001  * @param {Object} config
35002  */
35003 // private
35004 // This is a support class used internally by the Grid components
35005 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35006     this.grid = grid;
35007     this.view = grid.getView();
35008     this.proxy = this.view.resizeProxy;
35009     Roo.grid.SplitDragZone.superclass.constructor.call(
35010         this,
35011         hd, // ID
35012         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35013         {  // CONFIG
35014             dragElId : Roo.id(this.proxy.dom),
35015             resizeFrame:false
35016         }
35017     );
35018     
35019     this.setHandleElId(Roo.id(hd));
35020     if (hd2 !== false) {
35021         this.setOuterHandleElId(Roo.id(hd2));
35022     }
35023     
35024     this.scroll = false;
35025 };
35026 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35027     fly: Roo.Element.fly,
35028
35029     b4StartDrag : function(x, y){
35030         this.view.headersDisabled = true;
35031         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35032                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35033         );
35034         this.proxy.setHeight(h);
35035         
35036         // for old system colWidth really stored the actual width?
35037         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35038         // which in reality did not work.. - it worked only for fixed sizes
35039         // for resizable we need to use actual sizes.
35040         var w = this.cm.getColumnWidth(this.cellIndex);
35041         if (!this.view.mainWrap) {
35042             // bootstrap.
35043             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35044         }
35045         
35046         
35047         
35048         // this was w-this.grid.minColumnWidth;
35049         // doesnt really make sense? - w = thie curren width or the rendered one?
35050         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35051         this.resetConstraints();
35052         this.setXConstraint(minw, 1000);
35053         this.setYConstraint(0, 0);
35054         this.minX = x - minw;
35055         this.maxX = x + 1000;
35056         this.startPos = x;
35057         if (!this.view.mainWrap) { // this is Bootstrap code..
35058             this.getDragEl().style.display='block';
35059         }
35060         
35061         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35062     },
35063
35064
35065     handleMouseDown : function(e){
35066         ev = Roo.EventObject.setEvent(e);
35067         var t = this.fly(ev.getTarget());
35068         if(t.hasClass("x-grid-split")){
35069             this.cellIndex = this.view.getCellIndex(t.dom);
35070             this.split = t.dom;
35071             this.cm = this.grid.colModel;
35072             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35073                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35074             }
35075         }
35076     },
35077
35078     endDrag : function(e){
35079         this.view.headersDisabled = false;
35080         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35081         var diff = endX - this.startPos;
35082         // 
35083         var w = this.cm.getColumnWidth(this.cellIndex);
35084         if (!this.view.mainWrap) {
35085             w = 0;
35086         }
35087         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35088     },
35089
35090     autoOffset : function(){
35091         this.setDelta(0,0);
35092     }
35093 });/*
35094  * Based on:
35095  * Ext JS Library 1.1.1
35096  * Copyright(c) 2006-2007, Ext JS, LLC.
35097  *
35098  * Originally Released Under LGPL - original licence link has changed is not relivant.
35099  *
35100  * Fork - LGPL
35101  * <script type="text/javascript">
35102  */
35103  
35104 // private
35105 // This is a support class used internally by the Grid components
35106 Roo.grid.GridDragZone = function(grid, config){
35107     this.view = grid.getView();
35108     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35109     if(this.view.lockedBody){
35110         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35111         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35112     }
35113     this.scroll = false;
35114     this.grid = grid;
35115     this.ddel = document.createElement('div');
35116     this.ddel.className = 'x-grid-dd-wrap';
35117 };
35118
35119 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35120     ddGroup : "GridDD",
35121
35122     getDragData : function(e){
35123         var t = Roo.lib.Event.getTarget(e);
35124         var rowIndex = this.view.findRowIndex(t);
35125         var sm = this.grid.selModel;
35126             
35127         //Roo.log(rowIndex);
35128         
35129         if (sm.getSelectedCell) {
35130             // cell selection..
35131             if (!sm.getSelectedCell()) {
35132                 return false;
35133             }
35134             if (rowIndex != sm.getSelectedCell()[0]) {
35135                 return false;
35136             }
35137         
35138         }
35139         if (sm.getSelections && sm.getSelections().length < 1) {
35140             return false;
35141         }
35142         
35143         
35144         // before it used to all dragging of unseleted... - now we dont do that.
35145         if(rowIndex !== false){
35146             
35147             // if editorgrid.. 
35148             
35149             
35150             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35151                
35152             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35153               //  
35154             //}
35155             if (e.hasModifier()){
35156                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35157             }
35158             
35159             Roo.log("getDragData");
35160             
35161             return {
35162                 grid: this.grid,
35163                 ddel: this.ddel,
35164                 rowIndex: rowIndex,
35165                 selections: sm.getSelections ? sm.getSelections() : (
35166                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35167             };
35168         }
35169         return false;
35170     },
35171     
35172     
35173     onInitDrag : function(e){
35174         var data = this.dragData;
35175         this.ddel.innerHTML = this.grid.getDragDropText();
35176         this.proxy.update(this.ddel);
35177         // fire start drag?
35178     },
35179
35180     afterRepair : function(){
35181         this.dragging = false;
35182     },
35183
35184     getRepairXY : function(e, data){
35185         return false;
35186     },
35187
35188     onEndDrag : function(data, e){
35189         // fire end drag?
35190     },
35191
35192     onValidDrop : function(dd, e, id){
35193         // fire drag drop?
35194         this.hideProxy();
35195     },
35196
35197     beforeInvalidDrop : function(e, id){
35198
35199     }
35200 });/*
35201  * Based on:
35202  * Ext JS Library 1.1.1
35203  * Copyright(c) 2006-2007, Ext JS, LLC.
35204  *
35205  * Originally Released Under LGPL - original licence link has changed is not relivant.
35206  *
35207  * Fork - LGPL
35208  * <script type="text/javascript">
35209  */
35210  
35211
35212 /**
35213  * @class Roo.grid.ColumnModel
35214  * @extends Roo.util.Observable
35215  * This is the default implementation of a ColumnModel used by the Grid. It defines
35216  * the columns in the grid.
35217  * <br>Usage:<br>
35218  <pre><code>
35219  var colModel = new Roo.grid.ColumnModel([
35220         {header: "Ticker", width: 60, sortable: true, locked: true},
35221         {header: "Company Name", width: 150, sortable: true},
35222         {header: "Market Cap.", width: 100, sortable: true},
35223         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35224         {header: "Employees", width: 100, sortable: true, resizable: false}
35225  ]);
35226  </code></pre>
35227  * <p>
35228  
35229  * The config options listed for this class are options which may appear in each
35230  * individual column definition.
35231  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35232  * @constructor
35233  * @param {Object} config An Array of column config objects. See this class's
35234  * config objects for details.
35235 */
35236 Roo.grid.ColumnModel = function(config){
35237         /**
35238      * The config passed into the constructor
35239      */
35240     this.config = []; //config;
35241     this.lookup = {};
35242
35243     // if no id, create one
35244     // if the column does not have a dataIndex mapping,
35245     // map it to the order it is in the config
35246     for(var i = 0, len = config.length; i < len; i++){
35247         this.addColumn(config[i]);
35248         
35249     }
35250
35251     /**
35252      * The width of columns which have no width specified (defaults to 100)
35253      * @type Number
35254      */
35255     this.defaultWidth = 100;
35256
35257     /**
35258      * Default sortable of columns which have no sortable specified (defaults to false)
35259      * @type Boolean
35260      */
35261     this.defaultSortable = false;
35262
35263     this.addEvents({
35264         /**
35265              * @event widthchange
35266              * Fires when the width of a column changes.
35267              * @param {ColumnModel} this
35268              * @param {Number} columnIndex The column index
35269              * @param {Number} newWidth The new width
35270              */
35271             "widthchange": true,
35272         /**
35273              * @event headerchange
35274              * Fires when the text of a header changes.
35275              * @param {ColumnModel} this
35276              * @param {Number} columnIndex The column index
35277              * @param {Number} newText The new header text
35278              */
35279             "headerchange": true,
35280         /**
35281              * @event hiddenchange
35282              * Fires when a column is hidden or "unhidden".
35283              * @param {ColumnModel} this
35284              * @param {Number} columnIndex The column index
35285              * @param {Boolean} hidden true if hidden, false otherwise
35286              */
35287             "hiddenchange": true,
35288             /**
35289          * @event columnmoved
35290          * Fires when a column is moved.
35291          * @param {ColumnModel} this
35292          * @param {Number} oldIndex
35293          * @param {Number} newIndex
35294          */
35295         "columnmoved" : true,
35296         /**
35297          * @event columlockchange
35298          * Fires when a column's locked state is changed
35299          * @param {ColumnModel} this
35300          * @param {Number} colIndex
35301          * @param {Boolean} locked true if locked
35302          */
35303         "columnlockchange" : true
35304     });
35305     Roo.grid.ColumnModel.superclass.constructor.call(this);
35306 };
35307 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35308     /**
35309      * @cfg {String} header The header text to display in the Grid view.
35310      */
35311         /**
35312      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35313      */
35314         /**
35315      * @cfg {String} smHeader Header at Bootsrap Small width
35316      */
35317         /**
35318      * @cfg {String} mdHeader Header at Bootsrap Medium width
35319      */
35320         /**
35321      * @cfg {String} lgHeader Header at Bootsrap Large width
35322      */
35323         /**
35324      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35325      */
35326     /**
35327      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35328      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35329      * specified, the column's index is used as an index into the Record's data Array.
35330      */
35331     /**
35332      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35333      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35334      */
35335     /**
35336      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35337      * Defaults to the value of the {@link #defaultSortable} property.
35338      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35339      */
35340     /**
35341      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35342      */
35343     /**
35344      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35345      */
35346     /**
35347      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35348      */
35349     /**
35350      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35351      */
35352     /**
35353      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35354      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35355      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35356      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35357      */
35358        /**
35359      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35360      */
35361     /**
35362      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35363      */
35364     /**
35365      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35366      */
35367     /**
35368      * @cfg {String} cursor (Optional)
35369      */
35370     /**
35371      * @cfg {String} tooltip (Optional)
35372      */
35373     /**
35374      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35375      */
35376     /**
35377      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35378      */
35379     /**
35380      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35381      */
35382     /**
35383      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35384      */
35385         /**
35386      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35387      */
35388     /**
35389      * Returns the id of the column at the specified index.
35390      * @param {Number} index The column index
35391      * @return {String} the id
35392      */
35393     getColumnId : function(index){
35394         return this.config[index].id;
35395     },
35396
35397     /**
35398      * Returns the column for a specified id.
35399      * @param {String} id The column id
35400      * @return {Object} the column
35401      */
35402     getColumnById : function(id){
35403         return this.lookup[id];
35404     },
35405
35406     
35407     /**
35408      * Returns the column Object for a specified dataIndex.
35409      * @param {String} dataIndex The column dataIndex
35410      * @return {Object|Boolean} the column or false if not found
35411      */
35412     getColumnByDataIndex: function(dataIndex){
35413         var index = this.findColumnIndex(dataIndex);
35414         return index > -1 ? this.config[index] : false;
35415     },
35416     
35417     /**
35418      * Returns the index for a specified column id.
35419      * @param {String} id The column id
35420      * @return {Number} the index, or -1 if not found
35421      */
35422     getIndexById : function(id){
35423         for(var i = 0, len = this.config.length; i < len; i++){
35424             if(this.config[i].id == id){
35425                 return i;
35426             }
35427         }
35428         return -1;
35429     },
35430     
35431     /**
35432      * Returns the index for a specified column dataIndex.
35433      * @param {String} dataIndex The column dataIndex
35434      * @return {Number} the index, or -1 if not found
35435      */
35436     
35437     findColumnIndex : function(dataIndex){
35438         for(var i = 0, len = this.config.length; i < len; i++){
35439             if(this.config[i].dataIndex == dataIndex){
35440                 return i;
35441             }
35442         }
35443         return -1;
35444     },
35445     
35446     
35447     moveColumn : function(oldIndex, newIndex){
35448         var c = this.config[oldIndex];
35449         this.config.splice(oldIndex, 1);
35450         this.config.splice(newIndex, 0, c);
35451         this.dataMap = null;
35452         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35453     },
35454
35455     isLocked : function(colIndex){
35456         return this.config[colIndex].locked === true;
35457     },
35458
35459     setLocked : function(colIndex, value, suppressEvent){
35460         if(this.isLocked(colIndex) == value){
35461             return;
35462         }
35463         this.config[colIndex].locked = value;
35464         if(!suppressEvent){
35465             this.fireEvent("columnlockchange", this, colIndex, value);
35466         }
35467     },
35468
35469     getTotalLockedWidth : function(){
35470         var totalWidth = 0;
35471         for(var i = 0; i < this.config.length; i++){
35472             if(this.isLocked(i) && !this.isHidden(i)){
35473                 this.totalWidth += this.getColumnWidth(i);
35474             }
35475         }
35476         return totalWidth;
35477     },
35478
35479     getLockedCount : function(){
35480         for(var i = 0, len = this.config.length; i < len; i++){
35481             if(!this.isLocked(i)){
35482                 return i;
35483             }
35484         }
35485         
35486         return this.config.length;
35487     },
35488
35489     /**
35490      * Returns the number of columns.
35491      * @return {Number}
35492      */
35493     getColumnCount : function(visibleOnly){
35494         if(visibleOnly === true){
35495             var c = 0;
35496             for(var i = 0, len = this.config.length; i < len; i++){
35497                 if(!this.isHidden(i)){
35498                     c++;
35499                 }
35500             }
35501             return c;
35502         }
35503         return this.config.length;
35504     },
35505
35506     /**
35507      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35508      * @param {Function} fn
35509      * @param {Object} scope (optional)
35510      * @return {Array} result
35511      */
35512     getColumnsBy : function(fn, scope){
35513         var r = [];
35514         for(var i = 0, len = this.config.length; i < len; i++){
35515             var c = this.config[i];
35516             if(fn.call(scope||this, c, i) === true){
35517                 r[r.length] = c;
35518             }
35519         }
35520         return r;
35521     },
35522
35523     /**
35524      * Returns true if the specified column is sortable.
35525      * @param {Number} col The column index
35526      * @return {Boolean}
35527      */
35528     isSortable : function(col){
35529         if(typeof this.config[col].sortable == "undefined"){
35530             return this.defaultSortable;
35531         }
35532         return this.config[col].sortable;
35533     },
35534
35535     /**
35536      * Returns the rendering (formatting) function defined for the column.
35537      * @param {Number} col The column index.
35538      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35539      */
35540     getRenderer : function(col){
35541         if(!this.config[col].renderer){
35542             return Roo.grid.ColumnModel.defaultRenderer;
35543         }
35544         return this.config[col].renderer;
35545     },
35546
35547     /**
35548      * Sets the rendering (formatting) function for a column.
35549      * @param {Number} col The column index
35550      * @param {Function} fn The function to use to process the cell's raw data
35551      * to return HTML markup for the grid view. The render function is called with
35552      * the following parameters:<ul>
35553      * <li>Data value.</li>
35554      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35555      * <li>css A CSS style string to apply to the table cell.</li>
35556      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35557      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35558      * <li>Row index</li>
35559      * <li>Column index</li>
35560      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35561      */
35562     setRenderer : function(col, fn){
35563         this.config[col].renderer = fn;
35564     },
35565
35566     /**
35567      * Returns the width for the specified column.
35568      * @param {Number} col The column index
35569      * @param (optional) {String} gridSize bootstrap width size.
35570      * @return {Number}
35571      */
35572     getColumnWidth : function(col, gridSize)
35573         {
35574                 var cfg = this.config[col];
35575                 
35576                 if (typeof(gridSize) == 'undefined') {
35577                         return cfg.width * 1 || this.defaultWidth;
35578                 }
35579                 if (gridSize === false) { // if we set it..
35580                         return cfg.width || false;
35581                 }
35582                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35583                 
35584                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35585                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35586                                 continue;
35587                         }
35588                         return cfg[ sizes[i] ];
35589                 }
35590                 return 1;
35591                 
35592     },
35593
35594     /**
35595      * Sets the width for a column.
35596      * @param {Number} col The column index
35597      * @param {Number} width The new width
35598      */
35599     setColumnWidth : function(col, width, suppressEvent){
35600         this.config[col].width = width;
35601         this.totalWidth = null;
35602         if(!suppressEvent){
35603              this.fireEvent("widthchange", this, col, width);
35604         }
35605     },
35606
35607     /**
35608      * Returns the total width of all columns.
35609      * @param {Boolean} includeHidden True to include hidden column widths
35610      * @return {Number}
35611      */
35612     getTotalWidth : function(includeHidden){
35613         if(!this.totalWidth){
35614             this.totalWidth = 0;
35615             for(var i = 0, len = this.config.length; i < len; i++){
35616                 if(includeHidden || !this.isHidden(i)){
35617                     this.totalWidth += this.getColumnWidth(i);
35618                 }
35619             }
35620         }
35621         return this.totalWidth;
35622     },
35623
35624     /**
35625      * Returns the header for the specified column.
35626      * @param {Number} col The column index
35627      * @return {String}
35628      */
35629     getColumnHeader : function(col){
35630         return this.config[col].header;
35631     },
35632
35633     /**
35634      * Sets the header for a column.
35635      * @param {Number} col The column index
35636      * @param {String} header The new header
35637      */
35638     setColumnHeader : function(col, header){
35639         this.config[col].header = header;
35640         this.fireEvent("headerchange", this, col, header);
35641     },
35642
35643     /**
35644      * Returns the tooltip for the specified column.
35645      * @param {Number} col The column index
35646      * @return {String}
35647      */
35648     getColumnTooltip : function(col){
35649             return this.config[col].tooltip;
35650     },
35651     /**
35652      * Sets the tooltip for a column.
35653      * @param {Number} col The column index
35654      * @param {String} tooltip The new tooltip
35655      */
35656     setColumnTooltip : function(col, tooltip){
35657             this.config[col].tooltip = tooltip;
35658     },
35659
35660     /**
35661      * Returns the dataIndex for the specified column.
35662      * @param {Number} col The column index
35663      * @return {Number}
35664      */
35665     getDataIndex : function(col){
35666         return this.config[col].dataIndex;
35667     },
35668
35669     /**
35670      * Sets the dataIndex for a column.
35671      * @param {Number} col The column index
35672      * @param {Number} dataIndex The new dataIndex
35673      */
35674     setDataIndex : function(col, dataIndex){
35675         this.config[col].dataIndex = dataIndex;
35676     },
35677
35678     
35679     
35680     /**
35681      * Returns true if the cell is editable.
35682      * @param {Number} colIndex The column index
35683      * @param {Number} rowIndex The row index - this is nto actually used..?
35684      * @return {Boolean}
35685      */
35686     isCellEditable : function(colIndex, rowIndex){
35687         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35688     },
35689
35690     /**
35691      * Returns the editor defined for the cell/column.
35692      * return false or null to disable editing.
35693      * @param {Number} colIndex The column index
35694      * @param {Number} rowIndex The row index
35695      * @return {Object}
35696      */
35697     getCellEditor : function(colIndex, rowIndex){
35698         return this.config[colIndex].editor;
35699     },
35700
35701     /**
35702      * Sets if a column is editable.
35703      * @param {Number} col The column index
35704      * @param {Boolean} editable True if the column is editable
35705      */
35706     setEditable : function(col, editable){
35707         this.config[col].editable = editable;
35708     },
35709
35710
35711     /**
35712      * Returns true if the column is hidden.
35713      * @param {Number} colIndex The column index
35714      * @return {Boolean}
35715      */
35716     isHidden : function(colIndex){
35717         return this.config[colIndex].hidden;
35718     },
35719
35720
35721     /**
35722      * Returns true if the column width cannot be changed
35723      */
35724     isFixed : function(colIndex){
35725         return this.config[colIndex].fixed;
35726     },
35727
35728     /**
35729      * Returns true if the column can be resized
35730      * @return {Boolean}
35731      */
35732     isResizable : function(colIndex){
35733         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35734     },
35735     /**
35736      * Sets if a column is hidden.
35737      * @param {Number} colIndex The column index
35738      * @param {Boolean} hidden True if the column is hidden
35739      */
35740     setHidden : function(colIndex, hidden){
35741         this.config[colIndex].hidden = hidden;
35742         this.totalWidth = null;
35743         this.fireEvent("hiddenchange", this, colIndex, hidden);
35744     },
35745
35746     /**
35747      * Sets the editor for a column.
35748      * @param {Number} col The column index
35749      * @param {Object} editor The editor object
35750      */
35751     setEditor : function(col, editor){
35752         this.config[col].editor = editor;
35753     },
35754     /**
35755      * Add a column (experimental...) - defaults to adding to the end..
35756      * @param {Object} config 
35757     */
35758     addColumn : function(c)
35759     {
35760     
35761         var i = this.config.length;
35762         this.config[i] = c;
35763         
35764         if(typeof c.dataIndex == "undefined"){
35765             c.dataIndex = i;
35766         }
35767         if(typeof c.renderer == "string"){
35768             c.renderer = Roo.util.Format[c.renderer];
35769         }
35770         if(typeof c.id == "undefined"){
35771             c.id = Roo.id();
35772         }
35773         if(c.editor && c.editor.xtype){
35774             c.editor  = Roo.factory(c.editor, Roo.grid);
35775         }
35776         if(c.editor && c.editor.isFormField){
35777             c.editor = new Roo.grid.GridEditor(c.editor);
35778         }
35779         this.lookup[c.id] = c;
35780     }
35781     
35782 });
35783
35784 Roo.grid.ColumnModel.defaultRenderer = function(value)
35785 {
35786     if(typeof value == "object") {
35787         return value;
35788     }
35789         if(typeof value == "string" && value.length < 1){
35790             return "&#160;";
35791         }
35792     
35793         return String.format("{0}", value);
35794 };
35795
35796 // Alias for backwards compatibility
35797 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35798 /*
35799  * Based on:
35800  * Ext JS Library 1.1.1
35801  * Copyright(c) 2006-2007, Ext JS, LLC.
35802  *
35803  * Originally Released Under LGPL - original licence link has changed is not relivant.
35804  *
35805  * Fork - LGPL
35806  * <script type="text/javascript">
35807  */
35808
35809 /**
35810  * @class Roo.grid.AbstractSelectionModel
35811  * @extends Roo.util.Observable
35812  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35813  * implemented by descendant classes.  This class should not be directly instantiated.
35814  * @constructor
35815  */
35816 Roo.grid.AbstractSelectionModel = function(){
35817     this.locked = false;
35818     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35819 };
35820
35821 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35822     /** @ignore Called by the grid automatically. Do not call directly. */
35823     init : function(grid){
35824         this.grid = grid;
35825         this.initEvents();
35826     },
35827
35828     /**
35829      * Locks the selections.
35830      */
35831     lock : function(){
35832         this.locked = true;
35833     },
35834
35835     /**
35836      * Unlocks the selections.
35837      */
35838     unlock : function(){
35839         this.locked = false;
35840     },
35841
35842     /**
35843      * Returns true if the selections are locked.
35844      * @return {Boolean}
35845      */
35846     isLocked : function(){
35847         return this.locked;
35848     }
35849 });/*
35850  * Based on:
35851  * Ext JS Library 1.1.1
35852  * Copyright(c) 2006-2007, Ext JS, LLC.
35853  *
35854  * Originally Released Under LGPL - original licence link has changed is not relivant.
35855  *
35856  * Fork - LGPL
35857  * <script type="text/javascript">
35858  */
35859 /**
35860  * @extends Roo.grid.AbstractSelectionModel
35861  * @class Roo.grid.RowSelectionModel
35862  * The default SelectionModel used by {@link Roo.grid.Grid}.
35863  * It supports multiple selections and keyboard selection/navigation. 
35864  * @constructor
35865  * @param {Object} config
35866  */
35867 Roo.grid.RowSelectionModel = function(config){
35868     Roo.apply(this, config);
35869     this.selections = new Roo.util.MixedCollection(false, function(o){
35870         return o.id;
35871     });
35872
35873     this.last = false;
35874     this.lastActive = false;
35875
35876     this.addEvents({
35877         /**
35878         * @event selectionchange
35879         * Fires when the selection changes
35880         * @param {SelectionModel} this
35881         */
35882        "selectionchange" : true,
35883        /**
35884         * @event afterselectionchange
35885         * Fires after the selection changes (eg. by key press or clicking)
35886         * @param {SelectionModel} this
35887         */
35888        "afterselectionchange" : true,
35889        /**
35890         * @event beforerowselect
35891         * Fires when a row is selected being selected, return false to cancel.
35892         * @param {SelectionModel} this
35893         * @param {Number} rowIndex The selected index
35894         * @param {Boolean} keepExisting False if other selections will be cleared
35895         */
35896        "beforerowselect" : true,
35897        /**
35898         * @event rowselect
35899         * Fires when a row is selected.
35900         * @param {SelectionModel} this
35901         * @param {Number} rowIndex The selected index
35902         * @param {Roo.data.Record} r The record
35903         */
35904        "rowselect" : true,
35905        /**
35906         * @event rowdeselect
35907         * Fires when a row is deselected.
35908         * @param {SelectionModel} this
35909         * @param {Number} rowIndex The selected index
35910         */
35911         "rowdeselect" : true
35912     });
35913     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35914     this.locked = false;
35915 };
35916
35917 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35918     /**
35919      * @cfg {Boolean} singleSelect
35920      * True to allow selection of only one row at a time (defaults to false)
35921      */
35922     singleSelect : false,
35923
35924     // private
35925     initEvents : function(){
35926
35927         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35928             this.grid.on("mousedown", this.handleMouseDown, this);
35929         }else{ // allow click to work like normal
35930             this.grid.on("rowclick", this.handleDragableRowClick, this);
35931         }
35932         // bootstrap does not have a view..
35933         var view = this.grid.view ? this.grid.view : this.grid;
35934         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35935             "up" : function(e){
35936                 if(!e.shiftKey){
35937                     this.selectPrevious(e.shiftKey);
35938                 }else if(this.last !== false && this.lastActive !== false){
35939                     var last = this.last;
35940                     this.selectRange(this.last,  this.lastActive-1);
35941                     view.focusRow(this.lastActive);
35942                     if(last !== false){
35943                         this.last = last;
35944                     }
35945                 }else{
35946                     this.selectFirstRow();
35947                 }
35948                 this.fireEvent("afterselectionchange", this);
35949             },
35950             "down" : function(e){
35951                 if(!e.shiftKey){
35952                     this.selectNext(e.shiftKey);
35953                 }else if(this.last !== false && this.lastActive !== false){
35954                     var last = this.last;
35955                     this.selectRange(this.last,  this.lastActive+1);
35956                     view.focusRow(this.lastActive);
35957                     if(last !== false){
35958                         this.last = last;
35959                     }
35960                 }else{
35961                     this.selectFirstRow();
35962                 }
35963                 this.fireEvent("afterselectionchange", this);
35964             },
35965             scope: this
35966         });
35967
35968          
35969         view.on("refresh", this.onRefresh, this);
35970         view.on("rowupdated", this.onRowUpdated, this);
35971         view.on("rowremoved", this.onRemove, this);
35972     },
35973
35974     // private
35975     onRefresh : function(){
35976         var ds = this.grid.ds, i, v = this.grid.view;
35977         var s = this.selections;
35978         s.each(function(r){
35979             if((i = ds.indexOfId(r.id)) != -1){
35980                 v.onRowSelect(i);
35981                 s.add(ds.getAt(i)); // updating the selection relate data
35982             }else{
35983                 s.remove(r);
35984             }
35985         });
35986     },
35987
35988     // private
35989     onRemove : function(v, index, r){
35990         this.selections.remove(r);
35991     },
35992
35993     // private
35994     onRowUpdated : function(v, index, r){
35995         if(this.isSelected(r)){
35996             v.onRowSelect(index);
35997         }
35998     },
35999
36000     /**
36001      * Select records.
36002      * @param {Array} records The records to select
36003      * @param {Boolean} keepExisting (optional) True to keep existing selections
36004      */
36005     selectRecords : function(records, keepExisting){
36006         if(!keepExisting){
36007             this.clearSelections();
36008         }
36009         var ds = this.grid.ds;
36010         for(var i = 0, len = records.length; i < len; i++){
36011             this.selectRow(ds.indexOf(records[i]), true);
36012         }
36013     },
36014
36015     /**
36016      * Gets the number of selected rows.
36017      * @return {Number}
36018      */
36019     getCount : function(){
36020         return this.selections.length;
36021     },
36022
36023     /**
36024      * Selects the first row in the grid.
36025      */
36026     selectFirstRow : function(){
36027         this.selectRow(0);
36028     },
36029
36030     /**
36031      * Select the last row.
36032      * @param {Boolean} keepExisting (optional) True to keep existing selections
36033      */
36034     selectLastRow : function(keepExisting){
36035         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36036     },
36037
36038     /**
36039      * Selects the row immediately following the last selected row.
36040      * @param {Boolean} keepExisting (optional) True to keep existing selections
36041      */
36042     selectNext : function(keepExisting){
36043         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36044             this.selectRow(this.last+1, keepExisting);
36045             var view = this.grid.view ? this.grid.view : this.grid;
36046             view.focusRow(this.last);
36047         }
36048     },
36049
36050     /**
36051      * Selects the row that precedes the last selected row.
36052      * @param {Boolean} keepExisting (optional) True to keep existing selections
36053      */
36054     selectPrevious : function(keepExisting){
36055         if(this.last){
36056             this.selectRow(this.last-1, keepExisting);
36057             var view = this.grid.view ? this.grid.view : this.grid;
36058             view.focusRow(this.last);
36059         }
36060     },
36061
36062     /**
36063      * Returns the selected records
36064      * @return {Array} Array of selected records
36065      */
36066     getSelections : function(){
36067         return [].concat(this.selections.items);
36068     },
36069
36070     /**
36071      * Returns the first selected record.
36072      * @return {Record}
36073      */
36074     getSelected : function(){
36075         return this.selections.itemAt(0);
36076     },
36077
36078
36079     /**
36080      * Clears all selections.
36081      */
36082     clearSelections : function(fast){
36083         if(this.locked) {
36084             return;
36085         }
36086         if(fast !== true){
36087             var ds = this.grid.ds;
36088             var s = this.selections;
36089             s.each(function(r){
36090                 this.deselectRow(ds.indexOfId(r.id));
36091             }, this);
36092             s.clear();
36093         }else{
36094             this.selections.clear();
36095         }
36096         this.last = false;
36097     },
36098
36099
36100     /**
36101      * Selects all rows.
36102      */
36103     selectAll : function(){
36104         if(this.locked) {
36105             return;
36106         }
36107         this.selections.clear();
36108         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36109             this.selectRow(i, true);
36110         }
36111     },
36112
36113     /**
36114      * Returns True if there is a selection.
36115      * @return {Boolean}
36116      */
36117     hasSelection : function(){
36118         return this.selections.length > 0;
36119     },
36120
36121     /**
36122      * Returns True if the specified row is selected.
36123      * @param {Number/Record} record The record or index of the record to check
36124      * @return {Boolean}
36125      */
36126     isSelected : function(index){
36127         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36128         return (r && this.selections.key(r.id) ? true : false);
36129     },
36130
36131     /**
36132      * Returns True if the specified record id is selected.
36133      * @param {String} id The id of record to check
36134      * @return {Boolean}
36135      */
36136     isIdSelected : function(id){
36137         return (this.selections.key(id) ? true : false);
36138     },
36139
36140     // private
36141     handleMouseDown : function(e, t)
36142     {
36143         var view = this.grid.view ? this.grid.view : this.grid;
36144         var rowIndex;
36145         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36146             return;
36147         };
36148         if(e.shiftKey && this.last !== false){
36149             var last = this.last;
36150             this.selectRange(last, rowIndex, e.ctrlKey);
36151             this.last = last; // reset the last
36152             view.focusRow(rowIndex);
36153         }else{
36154             var isSelected = this.isSelected(rowIndex);
36155             if(e.button !== 0 && isSelected){
36156                 view.focusRow(rowIndex);
36157             }else if(e.ctrlKey && isSelected){
36158                 this.deselectRow(rowIndex);
36159             }else if(!isSelected){
36160                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36161                 view.focusRow(rowIndex);
36162             }
36163         }
36164         this.fireEvent("afterselectionchange", this);
36165     },
36166     // private
36167     handleDragableRowClick :  function(grid, rowIndex, e) 
36168     {
36169         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36170             this.selectRow(rowIndex, false);
36171             var view = this.grid.view ? this.grid.view : this.grid;
36172             view.focusRow(rowIndex);
36173              this.fireEvent("afterselectionchange", this);
36174         }
36175     },
36176     
36177     /**
36178      * Selects multiple rows.
36179      * @param {Array} rows Array of the indexes of the row to select
36180      * @param {Boolean} keepExisting (optional) True to keep existing selections
36181      */
36182     selectRows : function(rows, keepExisting){
36183         if(!keepExisting){
36184             this.clearSelections();
36185         }
36186         for(var i = 0, len = rows.length; i < len; i++){
36187             this.selectRow(rows[i], true);
36188         }
36189     },
36190
36191     /**
36192      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36193      * @param {Number} startRow The index of the first row in the range
36194      * @param {Number} endRow The index of the last row in the range
36195      * @param {Boolean} keepExisting (optional) True to retain existing selections
36196      */
36197     selectRange : function(startRow, endRow, keepExisting){
36198         if(this.locked) {
36199             return;
36200         }
36201         if(!keepExisting){
36202             this.clearSelections();
36203         }
36204         if(startRow <= endRow){
36205             for(var i = startRow; i <= endRow; i++){
36206                 this.selectRow(i, true);
36207             }
36208         }else{
36209             for(var i = startRow; i >= endRow; i--){
36210                 this.selectRow(i, true);
36211             }
36212         }
36213     },
36214
36215     /**
36216      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36217      * @param {Number} startRow The index of the first row in the range
36218      * @param {Number} endRow The index of the last row in the range
36219      */
36220     deselectRange : function(startRow, endRow, preventViewNotify){
36221         if(this.locked) {
36222             return;
36223         }
36224         for(var i = startRow; i <= endRow; i++){
36225             this.deselectRow(i, preventViewNotify);
36226         }
36227     },
36228
36229     /**
36230      * Selects a row.
36231      * @param {Number} row The index of the row to select
36232      * @param {Boolean} keepExisting (optional) True to keep existing selections
36233      */
36234     selectRow : function(index, keepExisting, preventViewNotify){
36235         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36236             return;
36237         }
36238         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36239             if(!keepExisting || this.singleSelect){
36240                 this.clearSelections();
36241             }
36242             var r = this.grid.ds.getAt(index);
36243             this.selections.add(r);
36244             this.last = this.lastActive = index;
36245             if(!preventViewNotify){
36246                 var view = this.grid.view ? this.grid.view : this.grid;
36247                 view.onRowSelect(index);
36248             }
36249             this.fireEvent("rowselect", this, index, r);
36250             this.fireEvent("selectionchange", this);
36251         }
36252     },
36253
36254     /**
36255      * Deselects a row.
36256      * @param {Number} row The index of the row to deselect
36257      */
36258     deselectRow : function(index, preventViewNotify){
36259         if(this.locked) {
36260             return;
36261         }
36262         if(this.last == index){
36263             this.last = false;
36264         }
36265         if(this.lastActive == index){
36266             this.lastActive = false;
36267         }
36268         var r = this.grid.ds.getAt(index);
36269         this.selections.remove(r);
36270         if(!preventViewNotify){
36271             var view = this.grid.view ? this.grid.view : this.grid;
36272             view.onRowDeselect(index);
36273         }
36274         this.fireEvent("rowdeselect", this, index);
36275         this.fireEvent("selectionchange", this);
36276     },
36277
36278     // private
36279     restoreLast : function(){
36280         if(this._last){
36281             this.last = this._last;
36282         }
36283     },
36284
36285     // private
36286     acceptsNav : function(row, col, cm){
36287         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36288     },
36289
36290     // private
36291     onEditorKey : function(field, e){
36292         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36293         if(k == e.TAB){
36294             e.stopEvent();
36295             ed.completeEdit();
36296             if(e.shiftKey){
36297                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36298             }else{
36299                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36300             }
36301         }else if(k == e.ENTER && !e.ctrlKey){
36302             e.stopEvent();
36303             ed.completeEdit();
36304             if(e.shiftKey){
36305                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36306             }else{
36307                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36308             }
36309         }else if(k == e.ESC){
36310             ed.cancelEdit();
36311         }
36312         if(newCell){
36313             g.startEditing(newCell[0], newCell[1]);
36314         }
36315     }
36316 });/*
36317  * Based on:
36318  * Ext JS Library 1.1.1
36319  * Copyright(c) 2006-2007, Ext JS, LLC.
36320  *
36321  * Originally Released Under LGPL - original licence link has changed is not relivant.
36322  *
36323  * Fork - LGPL
36324  * <script type="text/javascript">
36325  */
36326 /**
36327  * @class Roo.grid.CellSelectionModel
36328  * @extends Roo.grid.AbstractSelectionModel
36329  * This class provides the basic implementation for cell selection in a grid.
36330  * @constructor
36331  * @param {Object} config The object containing the configuration of this model.
36332  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36333  */
36334 Roo.grid.CellSelectionModel = function(config){
36335     Roo.apply(this, config);
36336
36337     this.selection = null;
36338
36339     this.addEvents({
36340         /**
36341              * @event beforerowselect
36342              * Fires before a cell is selected.
36343              * @param {SelectionModel} this
36344              * @param {Number} rowIndex The selected row index
36345              * @param {Number} colIndex The selected cell index
36346              */
36347             "beforecellselect" : true,
36348         /**
36349              * @event cellselect
36350              * Fires when a cell is selected.
36351              * @param {SelectionModel} this
36352              * @param {Number} rowIndex The selected row index
36353              * @param {Number} colIndex The selected cell index
36354              */
36355             "cellselect" : true,
36356         /**
36357              * @event selectionchange
36358              * Fires when the active selection changes.
36359              * @param {SelectionModel} this
36360              * @param {Object} selection null for no selection or an object (o) with two properties
36361                 <ul>
36362                 <li>o.record: the record object for the row the selection is in</li>
36363                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36364                 </ul>
36365              */
36366             "selectionchange" : true,
36367         /**
36368              * @event tabend
36369              * Fires when the tab (or enter) was pressed on the last editable cell
36370              * You can use this to trigger add new row.
36371              * @param {SelectionModel} this
36372              */
36373             "tabend" : true,
36374          /**
36375              * @event beforeeditnext
36376              * Fires before the next editable sell is made active
36377              * You can use this to skip to another cell or fire the tabend
36378              *    if you set cell to false
36379              * @param {Object} eventdata object : { cell : [ row, col ] } 
36380              */
36381             "beforeeditnext" : true
36382     });
36383     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36384 };
36385
36386 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36387     
36388     enter_is_tab: false,
36389
36390     /** @ignore */
36391     initEvents : function(){
36392         this.grid.on("mousedown", this.handleMouseDown, this);
36393         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36394         var view = this.grid.view;
36395         view.on("refresh", this.onViewChange, this);
36396         view.on("rowupdated", this.onRowUpdated, this);
36397         view.on("beforerowremoved", this.clearSelections, this);
36398         view.on("beforerowsinserted", this.clearSelections, this);
36399         if(this.grid.isEditor){
36400             this.grid.on("beforeedit", this.beforeEdit,  this);
36401         }
36402     },
36403
36404         //private
36405     beforeEdit : function(e){
36406         this.select(e.row, e.column, false, true, e.record);
36407     },
36408
36409         //private
36410     onRowUpdated : function(v, index, r){
36411         if(this.selection && this.selection.record == r){
36412             v.onCellSelect(index, this.selection.cell[1]);
36413         }
36414     },
36415
36416         //private
36417     onViewChange : function(){
36418         this.clearSelections(true);
36419     },
36420
36421         /**
36422          * Returns the currently selected cell,.
36423          * @return {Array} The selected cell (row, column) or null if none selected.
36424          */
36425     getSelectedCell : function(){
36426         return this.selection ? this.selection.cell : null;
36427     },
36428
36429     /**
36430      * Clears all selections.
36431      * @param {Boolean} true to prevent the gridview from being notified about the change.
36432      */
36433     clearSelections : function(preventNotify){
36434         var s = this.selection;
36435         if(s){
36436             if(preventNotify !== true){
36437                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36438             }
36439             this.selection = null;
36440             this.fireEvent("selectionchange", this, null);
36441         }
36442     },
36443
36444     /**
36445      * Returns true if there is a selection.
36446      * @return {Boolean}
36447      */
36448     hasSelection : function(){
36449         return this.selection ? true : false;
36450     },
36451
36452     /** @ignore */
36453     handleMouseDown : function(e, t){
36454         var v = this.grid.getView();
36455         if(this.isLocked()){
36456             return;
36457         };
36458         var row = v.findRowIndex(t);
36459         var cell = v.findCellIndex(t);
36460         if(row !== false && cell !== false){
36461             this.select(row, cell);
36462         }
36463     },
36464
36465     /**
36466      * Selects a cell.
36467      * @param {Number} rowIndex
36468      * @param {Number} collIndex
36469      */
36470     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36471         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36472             this.clearSelections();
36473             r = r || this.grid.dataSource.getAt(rowIndex);
36474             this.selection = {
36475                 record : r,
36476                 cell : [rowIndex, colIndex]
36477             };
36478             if(!preventViewNotify){
36479                 var v = this.grid.getView();
36480                 v.onCellSelect(rowIndex, colIndex);
36481                 if(preventFocus !== true){
36482                     v.focusCell(rowIndex, colIndex);
36483                 }
36484             }
36485             this.fireEvent("cellselect", this, rowIndex, colIndex);
36486             this.fireEvent("selectionchange", this, this.selection);
36487         }
36488     },
36489
36490         //private
36491     isSelectable : function(rowIndex, colIndex, cm){
36492         return !cm.isHidden(colIndex);
36493     },
36494
36495     /** @ignore */
36496     handleKeyDown : function(e){
36497         //Roo.log('Cell Sel Model handleKeyDown');
36498         if(!e.isNavKeyPress()){
36499             return;
36500         }
36501         var g = this.grid, s = this.selection;
36502         if(!s){
36503             e.stopEvent();
36504             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36505             if(cell){
36506                 this.select(cell[0], cell[1]);
36507             }
36508             return;
36509         }
36510         var sm = this;
36511         var walk = function(row, col, step){
36512             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36513         };
36514         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36515         var newCell;
36516
36517       
36518
36519         switch(k){
36520             case e.TAB:
36521                 // handled by onEditorKey
36522                 if (g.isEditor && g.editing) {
36523                     return;
36524                 }
36525                 if(e.shiftKey) {
36526                     newCell = walk(r, c-1, -1);
36527                 } else {
36528                     newCell = walk(r, c+1, 1);
36529                 }
36530                 break;
36531             
36532             case e.DOWN:
36533                newCell = walk(r+1, c, 1);
36534                 break;
36535             
36536             case e.UP:
36537                 newCell = walk(r-1, c, -1);
36538                 break;
36539             
36540             case e.RIGHT:
36541                 newCell = walk(r, c+1, 1);
36542                 break;
36543             
36544             case e.LEFT:
36545                 newCell = walk(r, c-1, -1);
36546                 break;
36547             
36548             case e.ENTER:
36549                 
36550                 if(g.isEditor && !g.editing){
36551                    g.startEditing(r, c);
36552                    e.stopEvent();
36553                    return;
36554                 }
36555                 
36556                 
36557              break;
36558         };
36559         if(newCell){
36560             this.select(newCell[0], newCell[1]);
36561             e.stopEvent();
36562             
36563         }
36564     },
36565
36566     acceptsNav : function(row, col, cm){
36567         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36568     },
36569     /**
36570      * Selects a cell.
36571      * @param {Number} field (not used) - as it's normally used as a listener
36572      * @param {Number} e - event - fake it by using
36573      *
36574      * var e = Roo.EventObjectImpl.prototype;
36575      * e.keyCode = e.TAB
36576      *
36577      * 
36578      */
36579     onEditorKey : function(field, e){
36580         
36581         var k = e.getKey(),
36582             newCell,
36583             g = this.grid,
36584             ed = g.activeEditor,
36585             forward = false;
36586         ///Roo.log('onEditorKey' + k);
36587         
36588         
36589         if (this.enter_is_tab && k == e.ENTER) {
36590             k = e.TAB;
36591         }
36592         
36593         if(k == e.TAB){
36594             if(e.shiftKey){
36595                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36596             }else{
36597                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36598                 forward = true;
36599             }
36600             
36601             e.stopEvent();
36602             
36603         } else if(k == e.ENTER &&  !e.ctrlKey){
36604             ed.completeEdit();
36605             e.stopEvent();
36606             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36607         
36608                 } else if(k == e.ESC){
36609             ed.cancelEdit();
36610         }
36611                 
36612         if (newCell) {
36613             var ecall = { cell : newCell, forward : forward };
36614             this.fireEvent('beforeeditnext', ecall );
36615             newCell = ecall.cell;
36616                         forward = ecall.forward;
36617         }
36618                 
36619         if(newCell){
36620             //Roo.log('next cell after edit');
36621             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36622         } else if (forward) {
36623             // tabbed past last
36624             this.fireEvent.defer(100, this, ['tabend',this]);
36625         }
36626     }
36627 });/*
36628  * Based on:
36629  * Ext JS Library 1.1.1
36630  * Copyright(c) 2006-2007, Ext JS, LLC.
36631  *
36632  * Originally Released Under LGPL - original licence link has changed is not relivant.
36633  *
36634  * Fork - LGPL
36635  * <script type="text/javascript">
36636  */
36637  
36638 /**
36639  * @class Roo.grid.EditorGrid
36640  * @extends Roo.grid.Grid
36641  * Class for creating and editable grid.
36642  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36643  * The container MUST have some type of size defined for the grid to fill. The container will be 
36644  * automatically set to position relative if it isn't already.
36645  * @param {Object} dataSource The data model to bind to
36646  * @param {Object} colModel The column model with info about this grid's columns
36647  */
36648 Roo.grid.EditorGrid = function(container, config){
36649     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36650     this.getGridEl().addClass("xedit-grid");
36651
36652     if(!this.selModel){
36653         this.selModel = new Roo.grid.CellSelectionModel();
36654     }
36655
36656     this.activeEditor = null;
36657
36658         this.addEvents({
36659             /**
36660              * @event beforeedit
36661              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36662              * <ul style="padding:5px;padding-left:16px;">
36663              * <li>grid - This grid</li>
36664              * <li>record - The record being edited</li>
36665              * <li>field - The field name being edited</li>
36666              * <li>value - The value for the field being edited.</li>
36667              * <li>row - The grid row index</li>
36668              * <li>column - The grid column index</li>
36669              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36670              * </ul>
36671              * @param {Object} e An edit event (see above for description)
36672              */
36673             "beforeedit" : true,
36674             /**
36675              * @event afteredit
36676              * Fires after a cell is edited. <br />
36677              * <ul style="padding:5px;padding-left:16px;">
36678              * <li>grid - This grid</li>
36679              * <li>record - The record being edited</li>
36680              * <li>field - The field name being edited</li>
36681              * <li>value - The value being set</li>
36682              * <li>originalValue - The original value for the field, before the edit.</li>
36683              * <li>row - The grid row index</li>
36684              * <li>column - The grid column index</li>
36685              * </ul>
36686              * @param {Object} e An edit event (see above for description)
36687              */
36688             "afteredit" : true,
36689             /**
36690              * @event validateedit
36691              * Fires after a cell is edited, but before the value is set in the record. 
36692          * You can use this to modify the value being set in the field, Return false
36693              * to cancel the change. The edit event object has the following properties <br />
36694              * <ul style="padding:5px;padding-left:16px;">
36695          * <li>editor - This editor</li>
36696              * <li>grid - This grid</li>
36697              * <li>record - The record being edited</li>
36698              * <li>field - The field name being edited</li>
36699              * <li>value - The value being set</li>
36700              * <li>originalValue - The original value for the field, before the edit.</li>
36701              * <li>row - The grid row index</li>
36702              * <li>column - The grid column index</li>
36703              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36704              * </ul>
36705              * @param {Object} e An edit event (see above for description)
36706              */
36707             "validateedit" : true
36708         });
36709     this.on("bodyscroll", this.stopEditing,  this);
36710     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36711 };
36712
36713 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36714     /**
36715      * @cfg {Number} clicksToEdit
36716      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36717      */
36718     clicksToEdit: 2,
36719
36720     // private
36721     isEditor : true,
36722     // private
36723     trackMouseOver: false, // causes very odd FF errors
36724
36725     onCellDblClick : function(g, row, col){
36726         this.startEditing(row, col);
36727     },
36728
36729     onEditComplete : function(ed, value, startValue){
36730         this.editing = false;
36731         this.activeEditor = null;
36732         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36733         var r = ed.record;
36734         var field = this.colModel.getDataIndex(ed.col);
36735         var e = {
36736             grid: this,
36737             record: r,
36738             field: field,
36739             originalValue: startValue,
36740             value: value,
36741             row: ed.row,
36742             column: ed.col,
36743             cancel:false,
36744             editor: ed
36745         };
36746         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36747         cell.show();
36748           
36749         if(String(value) !== String(startValue)){
36750             
36751             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36752                 r.set(field, e.value);
36753                 // if we are dealing with a combo box..
36754                 // then we also set the 'name' colum to be the displayField
36755                 if (ed.field.displayField && ed.field.name) {
36756                     r.set(ed.field.name, ed.field.el.dom.value);
36757                 }
36758                 
36759                 delete e.cancel; //?? why!!!
36760                 this.fireEvent("afteredit", e);
36761             }
36762         } else {
36763             this.fireEvent("afteredit", e); // always fire it!
36764         }
36765         this.view.focusCell(ed.row, ed.col);
36766     },
36767
36768     /**
36769      * Starts editing the specified for the specified row/column
36770      * @param {Number} rowIndex
36771      * @param {Number} colIndex
36772      */
36773     startEditing : function(row, col){
36774         this.stopEditing();
36775         if(this.colModel.isCellEditable(col, row)){
36776             this.view.ensureVisible(row, col, true);
36777           
36778             var r = this.dataSource.getAt(row);
36779             var field = this.colModel.getDataIndex(col);
36780             var cell = Roo.get(this.view.getCell(row,col));
36781             var e = {
36782                 grid: this,
36783                 record: r,
36784                 field: field,
36785                 value: r.data[field],
36786                 row: row,
36787                 column: col,
36788                 cancel:false 
36789             };
36790             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36791                 this.editing = true;
36792                 var ed = this.colModel.getCellEditor(col, row);
36793                 
36794                 if (!ed) {
36795                     return;
36796                 }
36797                 if(!ed.rendered){
36798                     ed.render(ed.parentEl || document.body);
36799                 }
36800                 ed.field.reset();
36801                
36802                 cell.hide();
36803                 
36804                 (function(){ // complex but required for focus issues in safari, ie and opera
36805                     ed.row = row;
36806                     ed.col = col;
36807                     ed.record = r;
36808                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36809                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36810                     this.activeEditor = ed;
36811                     var v = r.data[field];
36812                     ed.startEdit(this.view.getCell(row, col), v);
36813                     // combo's with 'displayField and name set
36814                     if (ed.field.displayField && ed.field.name) {
36815                         ed.field.el.dom.value = r.data[ed.field.name];
36816                     }
36817                     
36818                     
36819                 }).defer(50, this);
36820             }
36821         }
36822     },
36823         
36824     /**
36825      * Stops any active editing
36826      */
36827     stopEditing : function(){
36828         if(this.activeEditor){
36829             this.activeEditor.completeEdit();
36830         }
36831         this.activeEditor = null;
36832     },
36833         
36834          /**
36835      * Called to get grid's drag proxy text, by default returns this.ddText.
36836      * @return {String}
36837      */
36838     getDragDropText : function(){
36839         var count = this.selModel.getSelectedCell() ? 1 : 0;
36840         return String.format(this.ddText, count, count == 1 ? '' : 's');
36841     }
36842         
36843 });/*
36844  * Based on:
36845  * Ext JS Library 1.1.1
36846  * Copyright(c) 2006-2007, Ext JS, LLC.
36847  *
36848  * Originally Released Under LGPL - original licence link has changed is not relivant.
36849  *
36850  * Fork - LGPL
36851  * <script type="text/javascript">
36852  */
36853
36854 // private - not really -- you end up using it !
36855 // This is a support class used internally by the Grid components
36856
36857 /**
36858  * @class Roo.grid.GridEditor
36859  * @extends Roo.Editor
36860  * Class for creating and editable grid elements.
36861  * @param {Object} config any settings (must include field)
36862  */
36863 Roo.grid.GridEditor = function(field, config){
36864     if (!config && field.field) {
36865         config = field;
36866         field = Roo.factory(config.field, Roo.form);
36867     }
36868     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36869     field.monitorTab = false;
36870 };
36871
36872 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36873     
36874     /**
36875      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36876      */
36877     
36878     alignment: "tl-tl",
36879     autoSize: "width",
36880     hideEl : false,
36881     cls: "x-small-editor x-grid-editor",
36882     shim:false,
36883     shadow:"frame"
36884 });/*
36885  * Based on:
36886  * Ext JS Library 1.1.1
36887  * Copyright(c) 2006-2007, Ext JS, LLC.
36888  *
36889  * Originally Released Under LGPL - original licence link has changed is not relivant.
36890  *
36891  * Fork - LGPL
36892  * <script type="text/javascript">
36893  */
36894   
36895
36896   
36897 Roo.grid.PropertyRecord = Roo.data.Record.create([
36898     {name:'name',type:'string'},  'value'
36899 ]);
36900
36901
36902 Roo.grid.PropertyStore = function(grid, source){
36903     this.grid = grid;
36904     this.store = new Roo.data.Store({
36905         recordType : Roo.grid.PropertyRecord
36906     });
36907     this.store.on('update', this.onUpdate,  this);
36908     if(source){
36909         this.setSource(source);
36910     }
36911     Roo.grid.PropertyStore.superclass.constructor.call(this);
36912 };
36913
36914
36915
36916 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36917     setSource : function(o){
36918         this.source = o;
36919         this.store.removeAll();
36920         var data = [];
36921         for(var k in o){
36922             if(this.isEditableValue(o[k])){
36923                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36924             }
36925         }
36926         this.store.loadRecords({records: data}, {}, true);
36927     },
36928
36929     onUpdate : function(ds, record, type){
36930         if(type == Roo.data.Record.EDIT){
36931             var v = record.data['value'];
36932             var oldValue = record.modified['value'];
36933             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36934                 this.source[record.id] = v;
36935                 record.commit();
36936                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36937             }else{
36938                 record.reject();
36939             }
36940         }
36941     },
36942
36943     getProperty : function(row){
36944        return this.store.getAt(row);
36945     },
36946
36947     isEditableValue: function(val){
36948         if(val && val instanceof Date){
36949             return true;
36950         }else if(typeof val == 'object' || typeof val == 'function'){
36951             return false;
36952         }
36953         return true;
36954     },
36955
36956     setValue : function(prop, value){
36957         this.source[prop] = value;
36958         this.store.getById(prop).set('value', value);
36959     },
36960
36961     getSource : function(){
36962         return this.source;
36963     }
36964 });
36965
36966 Roo.grid.PropertyColumnModel = function(grid, store){
36967     this.grid = grid;
36968     var g = Roo.grid;
36969     g.PropertyColumnModel.superclass.constructor.call(this, [
36970         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36971         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36972     ]);
36973     this.store = store;
36974     this.bselect = Roo.DomHelper.append(document.body, {
36975         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36976             {tag: 'option', value: 'true', html: 'true'},
36977             {tag: 'option', value: 'false', html: 'false'}
36978         ]
36979     });
36980     Roo.id(this.bselect);
36981     var f = Roo.form;
36982     this.editors = {
36983         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36984         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36985         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36986         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36987         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36988     };
36989     this.renderCellDelegate = this.renderCell.createDelegate(this);
36990     this.renderPropDelegate = this.renderProp.createDelegate(this);
36991 };
36992
36993 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36994     
36995     
36996     nameText : 'Name',
36997     valueText : 'Value',
36998     
36999     dateFormat : 'm/j/Y',
37000     
37001     
37002     renderDate : function(dateVal){
37003         return dateVal.dateFormat(this.dateFormat);
37004     },
37005
37006     renderBool : function(bVal){
37007         return bVal ? 'true' : 'false';
37008     },
37009
37010     isCellEditable : function(colIndex, rowIndex){
37011         return colIndex == 1;
37012     },
37013
37014     getRenderer : function(col){
37015         return col == 1 ?
37016             this.renderCellDelegate : this.renderPropDelegate;
37017     },
37018
37019     renderProp : function(v){
37020         return this.getPropertyName(v);
37021     },
37022
37023     renderCell : function(val){
37024         var rv = val;
37025         if(val instanceof Date){
37026             rv = this.renderDate(val);
37027         }else if(typeof val == 'boolean'){
37028             rv = this.renderBool(val);
37029         }
37030         return Roo.util.Format.htmlEncode(rv);
37031     },
37032
37033     getPropertyName : function(name){
37034         var pn = this.grid.propertyNames;
37035         return pn && pn[name] ? pn[name] : name;
37036     },
37037
37038     getCellEditor : function(colIndex, rowIndex){
37039         var p = this.store.getProperty(rowIndex);
37040         var n = p.data['name'], val = p.data['value'];
37041         
37042         if(typeof(this.grid.customEditors[n]) == 'string'){
37043             return this.editors[this.grid.customEditors[n]];
37044         }
37045         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37046             return this.grid.customEditors[n];
37047         }
37048         if(val instanceof Date){
37049             return this.editors['date'];
37050         }else if(typeof val == 'number'){
37051             return this.editors['number'];
37052         }else if(typeof val == 'boolean'){
37053             return this.editors['boolean'];
37054         }else{
37055             return this.editors['string'];
37056         }
37057     }
37058 });
37059
37060 /**
37061  * @class Roo.grid.PropertyGrid
37062  * @extends Roo.grid.EditorGrid
37063  * This class represents the  interface of a component based property grid control.
37064  * <br><br>Usage:<pre><code>
37065  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37066       
37067  });
37068  // set any options
37069  grid.render();
37070  * </code></pre>
37071   
37072  * @constructor
37073  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37074  * The container MUST have some type of size defined for the grid to fill. The container will be
37075  * automatically set to position relative if it isn't already.
37076  * @param {Object} config A config object that sets properties on this grid.
37077  */
37078 Roo.grid.PropertyGrid = function(container, config){
37079     config = config || {};
37080     var store = new Roo.grid.PropertyStore(this);
37081     this.store = store;
37082     var cm = new Roo.grid.PropertyColumnModel(this, store);
37083     store.store.sort('name', 'ASC');
37084     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37085         ds: store.store,
37086         cm: cm,
37087         enableColLock:false,
37088         enableColumnMove:false,
37089         stripeRows:false,
37090         trackMouseOver: false,
37091         clicksToEdit:1
37092     }, config));
37093     this.getGridEl().addClass('x-props-grid');
37094     this.lastEditRow = null;
37095     this.on('columnresize', this.onColumnResize, this);
37096     this.addEvents({
37097          /**
37098              * @event beforepropertychange
37099              * Fires before a property changes (return false to stop?)
37100              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37101              * @param {String} id Record Id
37102              * @param {String} newval New Value
37103          * @param {String} oldval Old Value
37104              */
37105         "beforepropertychange": true,
37106         /**
37107              * @event propertychange
37108              * Fires after a property changes
37109              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37110              * @param {String} id Record Id
37111              * @param {String} newval New Value
37112          * @param {String} oldval Old Value
37113              */
37114         "propertychange": true
37115     });
37116     this.customEditors = this.customEditors || {};
37117 };
37118 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37119     
37120      /**
37121      * @cfg {Object} customEditors map of colnames=> custom editors.
37122      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37123      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37124      * false disables editing of the field.
37125          */
37126     
37127       /**
37128      * @cfg {Object} propertyNames map of property Names to their displayed value
37129          */
37130     
37131     render : function(){
37132         Roo.grid.PropertyGrid.superclass.render.call(this);
37133         this.autoSize.defer(100, this);
37134     },
37135
37136     autoSize : function(){
37137         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37138         if(this.view){
37139             this.view.fitColumns();
37140         }
37141     },
37142
37143     onColumnResize : function(){
37144         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37145         this.autoSize();
37146     },
37147     /**
37148      * Sets the data for the Grid
37149      * accepts a Key => Value object of all the elements avaiable.
37150      * @param {Object} data  to appear in grid.
37151      */
37152     setSource : function(source){
37153         this.store.setSource(source);
37154         //this.autoSize();
37155     },
37156     /**
37157      * Gets all the data from the grid.
37158      * @return {Object} data  data stored in grid
37159      */
37160     getSource : function(){
37161         return this.store.getSource();
37162     }
37163 });/*
37164   
37165  * Licence LGPL
37166  
37167  */
37168  
37169 /**
37170  * @class Roo.grid.Calendar
37171  * @extends Roo.util.Grid
37172  * This class extends the Grid to provide a calendar widget
37173  * <br><br>Usage:<pre><code>
37174  var grid = new Roo.grid.Calendar("my-container-id", {
37175      ds: myDataStore,
37176      cm: myColModel,
37177      selModel: mySelectionModel,
37178      autoSizeColumns: true,
37179      monitorWindowResize: false,
37180      trackMouseOver: true
37181      eventstore : real data store..
37182  });
37183  // set any options
37184  grid.render();
37185   
37186   * @constructor
37187  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37188  * The container MUST have some type of size defined for the grid to fill. The container will be
37189  * automatically set to position relative if it isn't already.
37190  * @param {Object} config A config object that sets properties on this grid.
37191  */
37192 Roo.grid.Calendar = function(container, config){
37193         // initialize the container
37194         this.container = Roo.get(container);
37195         this.container.update("");
37196         this.container.setStyle("overflow", "hidden");
37197     this.container.addClass('x-grid-container');
37198
37199     this.id = this.container.id;
37200
37201     Roo.apply(this, config);
37202     // check and correct shorthanded configs
37203     
37204     var rows = [];
37205     var d =1;
37206     for (var r = 0;r < 6;r++) {
37207         
37208         rows[r]=[];
37209         for (var c =0;c < 7;c++) {
37210             rows[r][c]= '';
37211         }
37212     }
37213     if (this.eventStore) {
37214         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37215         this.eventStore.on('load',this.onLoad, this);
37216         this.eventStore.on('beforeload',this.clearEvents, this);
37217          
37218     }
37219     
37220     this.dataSource = new Roo.data.Store({
37221             proxy: new Roo.data.MemoryProxy(rows),
37222             reader: new Roo.data.ArrayReader({}, [
37223                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37224     });
37225
37226     this.dataSource.load();
37227     this.ds = this.dataSource;
37228     this.ds.xmodule = this.xmodule || false;
37229     
37230     
37231     var cellRender = function(v,x,r)
37232     {
37233         return String.format(
37234             '<div class="fc-day  fc-widget-content"><div>' +
37235                 '<div class="fc-event-container"></div>' +
37236                 '<div class="fc-day-number">{0}</div>'+
37237                 
37238                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37239             '</div></div>', v);
37240     
37241     }
37242     
37243     
37244     this.colModel = new Roo.grid.ColumnModel( [
37245         {
37246             xtype: 'ColumnModel',
37247             xns: Roo.grid,
37248             dataIndex : 'weekday0',
37249             header : 'Sunday',
37250             renderer : cellRender
37251         },
37252         {
37253             xtype: 'ColumnModel',
37254             xns: Roo.grid,
37255             dataIndex : 'weekday1',
37256             header : 'Monday',
37257             renderer : cellRender
37258         },
37259         {
37260             xtype: 'ColumnModel',
37261             xns: Roo.grid,
37262             dataIndex : 'weekday2',
37263             header : 'Tuesday',
37264             renderer : cellRender
37265         },
37266         {
37267             xtype: 'ColumnModel',
37268             xns: Roo.grid,
37269             dataIndex : 'weekday3',
37270             header : 'Wednesday',
37271             renderer : cellRender
37272         },
37273         {
37274             xtype: 'ColumnModel',
37275             xns: Roo.grid,
37276             dataIndex : 'weekday4',
37277             header : 'Thursday',
37278             renderer : cellRender
37279         },
37280         {
37281             xtype: 'ColumnModel',
37282             xns: Roo.grid,
37283             dataIndex : 'weekday5',
37284             header : 'Friday',
37285             renderer : cellRender
37286         },
37287         {
37288             xtype: 'ColumnModel',
37289             xns: Roo.grid,
37290             dataIndex : 'weekday6',
37291             header : 'Saturday',
37292             renderer : cellRender
37293         }
37294     ]);
37295     this.cm = this.colModel;
37296     this.cm.xmodule = this.xmodule || false;
37297  
37298         
37299           
37300     //this.selModel = new Roo.grid.CellSelectionModel();
37301     //this.sm = this.selModel;
37302     //this.selModel.init(this);
37303     
37304     
37305     if(this.width){
37306         this.container.setWidth(this.width);
37307     }
37308
37309     if(this.height){
37310         this.container.setHeight(this.height);
37311     }
37312     /** @private */
37313         this.addEvents({
37314         // raw events
37315         /**
37316          * @event click
37317          * The raw click event for the entire grid.
37318          * @param {Roo.EventObject} e
37319          */
37320         "click" : true,
37321         /**
37322          * @event dblclick
37323          * The raw dblclick event for the entire grid.
37324          * @param {Roo.EventObject} e
37325          */
37326         "dblclick" : true,
37327         /**
37328          * @event contextmenu
37329          * The raw contextmenu event for the entire grid.
37330          * @param {Roo.EventObject} e
37331          */
37332         "contextmenu" : true,
37333         /**
37334          * @event mousedown
37335          * The raw mousedown event for the entire grid.
37336          * @param {Roo.EventObject} e
37337          */
37338         "mousedown" : true,
37339         /**
37340          * @event mouseup
37341          * The raw mouseup event for the entire grid.
37342          * @param {Roo.EventObject} e
37343          */
37344         "mouseup" : true,
37345         /**
37346          * @event mouseover
37347          * The raw mouseover event for the entire grid.
37348          * @param {Roo.EventObject} e
37349          */
37350         "mouseover" : true,
37351         /**
37352          * @event mouseout
37353          * The raw mouseout event for the entire grid.
37354          * @param {Roo.EventObject} e
37355          */
37356         "mouseout" : true,
37357         /**
37358          * @event keypress
37359          * The raw keypress event for the entire grid.
37360          * @param {Roo.EventObject} e
37361          */
37362         "keypress" : true,
37363         /**
37364          * @event keydown
37365          * The raw keydown event for the entire grid.
37366          * @param {Roo.EventObject} e
37367          */
37368         "keydown" : true,
37369
37370         // custom events
37371
37372         /**
37373          * @event cellclick
37374          * Fires when a cell is clicked
37375          * @param {Grid} this
37376          * @param {Number} rowIndex
37377          * @param {Number} columnIndex
37378          * @param {Roo.EventObject} e
37379          */
37380         "cellclick" : true,
37381         /**
37382          * @event celldblclick
37383          * Fires when a cell is double clicked
37384          * @param {Grid} this
37385          * @param {Number} rowIndex
37386          * @param {Number} columnIndex
37387          * @param {Roo.EventObject} e
37388          */
37389         "celldblclick" : true,
37390         /**
37391          * @event rowclick
37392          * Fires when a row is clicked
37393          * @param {Grid} this
37394          * @param {Number} rowIndex
37395          * @param {Roo.EventObject} e
37396          */
37397         "rowclick" : true,
37398         /**
37399          * @event rowdblclick
37400          * Fires when a row is double clicked
37401          * @param {Grid} this
37402          * @param {Number} rowIndex
37403          * @param {Roo.EventObject} e
37404          */
37405         "rowdblclick" : true,
37406         /**
37407          * @event headerclick
37408          * Fires when a header is clicked
37409          * @param {Grid} this
37410          * @param {Number} columnIndex
37411          * @param {Roo.EventObject} e
37412          */
37413         "headerclick" : true,
37414         /**
37415          * @event headerdblclick
37416          * Fires when a header cell is double clicked
37417          * @param {Grid} this
37418          * @param {Number} columnIndex
37419          * @param {Roo.EventObject} e
37420          */
37421         "headerdblclick" : true,
37422         /**
37423          * @event rowcontextmenu
37424          * Fires when a row is right clicked
37425          * @param {Grid} this
37426          * @param {Number} rowIndex
37427          * @param {Roo.EventObject} e
37428          */
37429         "rowcontextmenu" : true,
37430         /**
37431          * @event cellcontextmenu
37432          * Fires when a cell is right clicked
37433          * @param {Grid} this
37434          * @param {Number} rowIndex
37435          * @param {Number} cellIndex
37436          * @param {Roo.EventObject} e
37437          */
37438          "cellcontextmenu" : true,
37439         /**
37440          * @event headercontextmenu
37441          * Fires when a header is right clicked
37442          * @param {Grid} this
37443          * @param {Number} columnIndex
37444          * @param {Roo.EventObject} e
37445          */
37446         "headercontextmenu" : true,
37447         /**
37448          * @event bodyscroll
37449          * Fires when the body element is scrolled
37450          * @param {Number} scrollLeft
37451          * @param {Number} scrollTop
37452          */
37453         "bodyscroll" : true,
37454         /**
37455          * @event columnresize
37456          * Fires when the user resizes a column
37457          * @param {Number} columnIndex
37458          * @param {Number} newSize
37459          */
37460         "columnresize" : true,
37461         /**
37462          * @event columnmove
37463          * Fires when the user moves a column
37464          * @param {Number} oldIndex
37465          * @param {Number} newIndex
37466          */
37467         "columnmove" : true,
37468         /**
37469          * @event startdrag
37470          * Fires when row(s) start being dragged
37471          * @param {Grid} this
37472          * @param {Roo.GridDD} dd The drag drop object
37473          * @param {event} e The raw browser event
37474          */
37475         "startdrag" : true,
37476         /**
37477          * @event enddrag
37478          * Fires when a drag operation is complete
37479          * @param {Grid} this
37480          * @param {Roo.GridDD} dd The drag drop object
37481          * @param {event} e The raw browser event
37482          */
37483         "enddrag" : true,
37484         /**
37485          * @event dragdrop
37486          * Fires when dragged row(s) are dropped on a valid DD target
37487          * @param {Grid} this
37488          * @param {Roo.GridDD} dd The drag drop object
37489          * @param {String} targetId The target drag drop object
37490          * @param {event} e The raw browser event
37491          */
37492         "dragdrop" : true,
37493         /**
37494          * @event dragover
37495          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37496          * @param {Grid} this
37497          * @param {Roo.GridDD} dd The drag drop object
37498          * @param {String} targetId The target drag drop object
37499          * @param {event} e The raw browser event
37500          */
37501         "dragover" : true,
37502         /**
37503          * @event dragenter
37504          *  Fires when the dragged row(s) first cross another DD target while being dragged
37505          * @param {Grid} this
37506          * @param {Roo.GridDD} dd The drag drop object
37507          * @param {String} targetId The target drag drop object
37508          * @param {event} e The raw browser event
37509          */
37510         "dragenter" : true,
37511         /**
37512          * @event dragout
37513          * Fires when the dragged row(s) leave another DD target while being dragged
37514          * @param {Grid} this
37515          * @param {Roo.GridDD} dd The drag drop object
37516          * @param {String} targetId The target drag drop object
37517          * @param {event} e The raw browser event
37518          */
37519         "dragout" : true,
37520         /**
37521          * @event rowclass
37522          * Fires when a row is rendered, so you can change add a style to it.
37523          * @param {GridView} gridview   The grid view
37524          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37525          */
37526         'rowclass' : true,
37527
37528         /**
37529          * @event render
37530          * Fires when the grid is rendered
37531          * @param {Grid} grid
37532          */
37533         'render' : true,
37534             /**
37535              * @event select
37536              * Fires when a date is selected
37537              * @param {DatePicker} this
37538              * @param {Date} date The selected date
37539              */
37540         'select': true,
37541         /**
37542              * @event monthchange
37543              * Fires when the displayed month changes 
37544              * @param {DatePicker} this
37545              * @param {Date} date The selected month
37546              */
37547         'monthchange': true,
37548         /**
37549              * @event evententer
37550              * Fires when mouse over an event
37551              * @param {Calendar} this
37552              * @param {event} Event
37553              */
37554         'evententer': true,
37555         /**
37556              * @event eventleave
37557              * Fires when the mouse leaves an
37558              * @param {Calendar} this
37559              * @param {event}
37560              */
37561         'eventleave': true,
37562         /**
37563              * @event eventclick
37564              * Fires when the mouse click an
37565              * @param {Calendar} this
37566              * @param {event}
37567              */
37568         'eventclick': true,
37569         /**
37570              * @event eventrender
37571              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37572              * @param {Calendar} this
37573              * @param {data} data to be modified
37574              */
37575         'eventrender': true
37576         
37577     });
37578
37579     Roo.grid.Grid.superclass.constructor.call(this);
37580     this.on('render', function() {
37581         this.view.el.addClass('x-grid-cal'); 
37582         
37583         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37584
37585     },this);
37586     
37587     if (!Roo.grid.Calendar.style) {
37588         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37589             
37590             
37591             '.x-grid-cal .x-grid-col' :  {
37592                 height: 'auto !important',
37593                 'vertical-align': 'top'
37594             },
37595             '.x-grid-cal  .fc-event-hori' : {
37596                 height: '14px'
37597             }
37598              
37599             
37600         }, Roo.id());
37601     }
37602
37603     
37604     
37605 };
37606 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37607     /**
37608      * @cfg {Store} eventStore The store that loads events.
37609      */
37610     eventStore : 25,
37611
37612      
37613     activeDate : false,
37614     startDay : 0,
37615     autoWidth : true,
37616     monitorWindowResize : false,
37617
37618     
37619     resizeColumns : function() {
37620         var col = (this.view.el.getWidth() / 7) - 3;
37621         // loop through cols, and setWidth
37622         for(var i =0 ; i < 7 ; i++){
37623             this.cm.setColumnWidth(i, col);
37624         }
37625     },
37626      setDate :function(date) {
37627         
37628         Roo.log('setDate?');
37629         
37630         this.resizeColumns();
37631         var vd = this.activeDate;
37632         this.activeDate = date;
37633 //        if(vd && this.el){
37634 //            var t = date.getTime();
37635 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37636 //                Roo.log('using add remove');
37637 //                
37638 //                this.fireEvent('monthchange', this, date);
37639 //                
37640 //                this.cells.removeClass("fc-state-highlight");
37641 //                this.cells.each(function(c){
37642 //                   if(c.dateValue == t){
37643 //                       c.addClass("fc-state-highlight");
37644 //                       setTimeout(function(){
37645 //                            try{c.dom.firstChild.focus();}catch(e){}
37646 //                       }, 50);
37647 //                       return false;
37648 //                   }
37649 //                   return true;
37650 //                });
37651 //                return;
37652 //            }
37653 //        }
37654         
37655         var days = date.getDaysInMonth();
37656         
37657         var firstOfMonth = date.getFirstDateOfMonth();
37658         var startingPos = firstOfMonth.getDay()-this.startDay;
37659         
37660         if(startingPos < this.startDay){
37661             startingPos += 7;
37662         }
37663         
37664         var pm = date.add(Date.MONTH, -1);
37665         var prevStart = pm.getDaysInMonth()-startingPos;
37666 //        
37667         
37668         
37669         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37670         
37671         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37672         //this.cells.addClassOnOver('fc-state-hover');
37673         
37674         var cells = this.cells.elements;
37675         var textEls = this.textNodes;
37676         
37677         //Roo.each(cells, function(cell){
37678         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37679         //});
37680         
37681         days += startingPos;
37682
37683         // convert everything to numbers so it's fast
37684         var day = 86400000;
37685         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37686         //Roo.log(d);
37687         //Roo.log(pm);
37688         //Roo.log(prevStart);
37689         
37690         var today = new Date().clearTime().getTime();
37691         var sel = date.clearTime().getTime();
37692         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37693         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37694         var ddMatch = this.disabledDatesRE;
37695         var ddText = this.disabledDatesText;
37696         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37697         var ddaysText = this.disabledDaysText;
37698         var format = this.format;
37699         
37700         var setCellClass = function(cal, cell){
37701             
37702             //Roo.log('set Cell Class');
37703             cell.title = "";
37704             var t = d.getTime();
37705             
37706             //Roo.log(d);
37707             
37708             
37709             cell.dateValue = t;
37710             if(t == today){
37711                 cell.className += " fc-today";
37712                 cell.className += " fc-state-highlight";
37713                 cell.title = cal.todayText;
37714             }
37715             if(t == sel){
37716                 // disable highlight in other month..
37717                 cell.className += " fc-state-highlight";
37718                 
37719             }
37720             // disabling
37721             if(t < min) {
37722                 //cell.className = " fc-state-disabled";
37723                 cell.title = cal.minText;
37724                 return;
37725             }
37726             if(t > max) {
37727                 //cell.className = " fc-state-disabled";
37728                 cell.title = cal.maxText;
37729                 return;
37730             }
37731             if(ddays){
37732                 if(ddays.indexOf(d.getDay()) != -1){
37733                     // cell.title = ddaysText;
37734                    // cell.className = " fc-state-disabled";
37735                 }
37736             }
37737             if(ddMatch && format){
37738                 var fvalue = d.dateFormat(format);
37739                 if(ddMatch.test(fvalue)){
37740                     cell.title = ddText.replace("%0", fvalue);
37741                    cell.className = " fc-state-disabled";
37742                 }
37743             }
37744             
37745             if (!cell.initialClassName) {
37746                 cell.initialClassName = cell.dom.className;
37747             }
37748             
37749             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37750         };
37751
37752         var i = 0;
37753         
37754         for(; i < startingPos; i++) {
37755             cells[i].dayName =  (++prevStart);
37756             Roo.log(textEls[i]);
37757             d.setDate(d.getDate()+1);
37758             
37759             //cells[i].className = "fc-past fc-other-month";
37760             setCellClass(this, cells[i]);
37761         }
37762         
37763         var intDay = 0;
37764         
37765         for(; i < days; i++){
37766             intDay = i - startingPos + 1;
37767             cells[i].dayName =  (intDay);
37768             d.setDate(d.getDate()+1);
37769             
37770             cells[i].className = ''; // "x-date-active";
37771             setCellClass(this, cells[i]);
37772         }
37773         var extraDays = 0;
37774         
37775         for(; i < 42; i++) {
37776             //textEls[i].innerHTML = (++extraDays);
37777             
37778             d.setDate(d.getDate()+1);
37779             cells[i].dayName = (++extraDays);
37780             cells[i].className = "fc-future fc-other-month";
37781             setCellClass(this, cells[i]);
37782         }
37783         
37784         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37785         
37786         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37787         
37788         // this will cause all the cells to mis
37789         var rows= [];
37790         var i =0;
37791         for (var r = 0;r < 6;r++) {
37792             for (var c =0;c < 7;c++) {
37793                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37794             }    
37795         }
37796         
37797         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37798         for(i=0;i<cells.length;i++) {
37799             
37800             this.cells.elements[i].dayName = cells[i].dayName ;
37801             this.cells.elements[i].className = cells[i].className;
37802             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37803             this.cells.elements[i].title = cells[i].title ;
37804             this.cells.elements[i].dateValue = cells[i].dateValue ;
37805         }
37806         
37807         
37808         
37809         
37810         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37811         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37812         
37813         ////if(totalRows != 6){
37814             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37815            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37816        // }
37817         
37818         this.fireEvent('monthchange', this, date);
37819         
37820         
37821     },
37822  /**
37823      * Returns the grid's SelectionModel.
37824      * @return {SelectionModel}
37825      */
37826     getSelectionModel : function(){
37827         if(!this.selModel){
37828             this.selModel = new Roo.grid.CellSelectionModel();
37829         }
37830         return this.selModel;
37831     },
37832
37833     load: function() {
37834         this.eventStore.load()
37835         
37836         
37837         
37838     },
37839     
37840     findCell : function(dt) {
37841         dt = dt.clearTime().getTime();
37842         var ret = false;
37843         this.cells.each(function(c){
37844             //Roo.log("check " +c.dateValue + '?=' + dt);
37845             if(c.dateValue == dt){
37846                 ret = c;
37847                 return false;
37848             }
37849             return true;
37850         });
37851         
37852         return ret;
37853     },
37854     
37855     findCells : function(rec) {
37856         var s = rec.data.start_dt.clone().clearTime().getTime();
37857        // Roo.log(s);
37858         var e= rec.data.end_dt.clone().clearTime().getTime();
37859        // Roo.log(e);
37860         var ret = [];
37861         this.cells.each(function(c){
37862              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37863             
37864             if(c.dateValue > e){
37865                 return ;
37866             }
37867             if(c.dateValue < s){
37868                 return ;
37869             }
37870             ret.push(c);
37871         });
37872         
37873         return ret;    
37874     },
37875     
37876     findBestRow: function(cells)
37877     {
37878         var ret = 0;
37879         
37880         for (var i =0 ; i < cells.length;i++) {
37881             ret  = Math.max(cells[i].rows || 0,ret);
37882         }
37883         return ret;
37884         
37885     },
37886     
37887     
37888     addItem : function(rec)
37889     {
37890         // look for vertical location slot in
37891         var cells = this.findCells(rec);
37892         
37893         rec.row = this.findBestRow(cells);
37894         
37895         // work out the location.
37896         
37897         var crow = false;
37898         var rows = [];
37899         for(var i =0; i < cells.length; i++) {
37900             if (!crow) {
37901                 crow = {
37902                     start : cells[i],
37903                     end :  cells[i]
37904                 };
37905                 continue;
37906             }
37907             if (crow.start.getY() == cells[i].getY()) {
37908                 // on same row.
37909                 crow.end = cells[i];
37910                 continue;
37911             }
37912             // different row.
37913             rows.push(crow);
37914             crow = {
37915                 start: cells[i],
37916                 end : cells[i]
37917             };
37918             
37919         }
37920         
37921         rows.push(crow);
37922         rec.els = [];
37923         rec.rows = rows;
37924         rec.cells = cells;
37925         for (var i = 0; i < cells.length;i++) {
37926             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37927             
37928         }
37929         
37930         
37931     },
37932     
37933     clearEvents: function() {
37934         
37935         if (!this.eventStore.getCount()) {
37936             return;
37937         }
37938         // reset number of rows in cells.
37939         Roo.each(this.cells.elements, function(c){
37940             c.rows = 0;
37941         });
37942         
37943         this.eventStore.each(function(e) {
37944             this.clearEvent(e);
37945         },this);
37946         
37947     },
37948     
37949     clearEvent : function(ev)
37950     {
37951         if (ev.els) {
37952             Roo.each(ev.els, function(el) {
37953                 el.un('mouseenter' ,this.onEventEnter, this);
37954                 el.un('mouseleave' ,this.onEventLeave, this);
37955                 el.remove();
37956             },this);
37957             ev.els = [];
37958         }
37959     },
37960     
37961     
37962     renderEvent : function(ev,ctr) {
37963         if (!ctr) {
37964              ctr = this.view.el.select('.fc-event-container',true).first();
37965         }
37966         
37967          
37968         this.clearEvent(ev);
37969             //code
37970        
37971         
37972         
37973         ev.els = [];
37974         var cells = ev.cells;
37975         var rows = ev.rows;
37976         this.fireEvent('eventrender', this, ev);
37977         
37978         for(var i =0; i < rows.length; i++) {
37979             
37980             cls = '';
37981             if (i == 0) {
37982                 cls += ' fc-event-start';
37983             }
37984             if ((i+1) == rows.length) {
37985                 cls += ' fc-event-end';
37986             }
37987             
37988             //Roo.log(ev.data);
37989             // how many rows should it span..
37990             var cg = this.eventTmpl.append(ctr,Roo.apply({
37991                 fccls : cls
37992                 
37993             }, ev.data) , true);
37994             
37995             
37996             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37997             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37998             cg.on('click', this.onEventClick, this, ev);
37999             
38000             ev.els.push(cg);
38001             
38002             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38003             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38004             //Roo.log(cg);
38005              
38006             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38007             cg.setWidth(ebox.right - sbox.x -2);
38008         }
38009     },
38010     
38011     renderEvents: function()
38012     {   
38013         // first make sure there is enough space..
38014         
38015         if (!this.eventTmpl) {
38016             this.eventTmpl = new Roo.Template(
38017                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38018                     '<div class="fc-event-inner">' +
38019                         '<span class="fc-event-time">{time}</span>' +
38020                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38021                     '</div>' +
38022                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38023                 '</div>'
38024             );
38025                 
38026         }
38027                
38028         
38029         
38030         this.cells.each(function(c) {
38031             //Roo.log(c.select('.fc-day-content div',true).first());
38032             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38033         });
38034         
38035         var ctr = this.view.el.select('.fc-event-container',true).first();
38036         
38037         var cls;
38038         this.eventStore.each(function(ev){
38039             
38040             this.renderEvent(ev);
38041              
38042              
38043         }, this);
38044         this.view.layout();
38045         
38046     },
38047     
38048     onEventEnter: function (e, el,event,d) {
38049         this.fireEvent('evententer', this, el, event);
38050     },
38051     
38052     onEventLeave: function (e, el,event,d) {
38053         this.fireEvent('eventleave', this, el, event);
38054     },
38055     
38056     onEventClick: function (e, el,event,d) {
38057         this.fireEvent('eventclick', this, el, event);
38058     },
38059     
38060     onMonthChange: function () {
38061         this.store.load();
38062     },
38063     
38064     onLoad: function () {
38065         
38066         //Roo.log('calendar onload');
38067 //         
38068         if(this.eventStore.getCount() > 0){
38069             
38070            
38071             
38072             this.eventStore.each(function(d){
38073                 
38074                 
38075                 // FIXME..
38076                 var add =   d.data;
38077                 if (typeof(add.end_dt) == 'undefined')  {
38078                     Roo.log("Missing End time in calendar data: ");
38079                     Roo.log(d);
38080                     return;
38081                 }
38082                 if (typeof(add.start_dt) == 'undefined')  {
38083                     Roo.log("Missing Start time in calendar data: ");
38084                     Roo.log(d);
38085                     return;
38086                 }
38087                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38088                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38089                 add.id = add.id || d.id;
38090                 add.title = add.title || '??';
38091                 
38092                 this.addItem(d);
38093                 
38094              
38095             },this);
38096         }
38097         
38098         this.renderEvents();
38099     }
38100     
38101
38102 });
38103 /*
38104  grid : {
38105                 xtype: 'Grid',
38106                 xns: Roo.grid,
38107                 listeners : {
38108                     render : function ()
38109                     {
38110                         _this.grid = this;
38111                         
38112                         if (!this.view.el.hasClass('course-timesheet')) {
38113                             this.view.el.addClass('course-timesheet');
38114                         }
38115                         if (this.tsStyle) {
38116                             this.ds.load({});
38117                             return; 
38118                         }
38119                         Roo.log('width');
38120                         Roo.log(_this.grid.view.el.getWidth());
38121                         
38122                         
38123                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38124                             '.course-timesheet .x-grid-row' : {
38125                                 height: '80px'
38126                             },
38127                             '.x-grid-row td' : {
38128                                 'vertical-align' : 0
38129                             },
38130                             '.course-edit-link' : {
38131                                 'color' : 'blue',
38132                                 'text-overflow' : 'ellipsis',
38133                                 'overflow' : 'hidden',
38134                                 'white-space' : 'nowrap',
38135                                 'cursor' : 'pointer'
38136                             },
38137                             '.sub-link' : {
38138                                 'color' : 'green'
38139                             },
38140                             '.de-act-sup-link' : {
38141                                 'color' : 'purple',
38142                                 'text-decoration' : 'line-through'
38143                             },
38144                             '.de-act-link' : {
38145                                 'color' : 'red',
38146                                 'text-decoration' : 'line-through'
38147                             },
38148                             '.course-timesheet .course-highlight' : {
38149                                 'border-top-style': 'dashed !important',
38150                                 'border-bottom-bottom': 'dashed !important'
38151                             },
38152                             '.course-timesheet .course-item' : {
38153                                 'font-family'   : 'tahoma, arial, helvetica',
38154                                 'font-size'     : '11px',
38155                                 'overflow'      : 'hidden',
38156                                 'padding-left'  : '10px',
38157                                 'padding-right' : '10px',
38158                                 'padding-top' : '10px' 
38159                             }
38160                             
38161                         }, Roo.id());
38162                                 this.ds.load({});
38163                     }
38164                 },
38165                 autoWidth : true,
38166                 monitorWindowResize : false,
38167                 cellrenderer : function(v,x,r)
38168                 {
38169                     return v;
38170                 },
38171                 sm : {
38172                     xtype: 'CellSelectionModel',
38173                     xns: Roo.grid
38174                 },
38175                 dataSource : {
38176                     xtype: 'Store',
38177                     xns: Roo.data,
38178                     listeners : {
38179                         beforeload : function (_self, options)
38180                         {
38181                             options.params = options.params || {};
38182                             options.params._month = _this.monthField.getValue();
38183                             options.params.limit = 9999;
38184                             options.params['sort'] = 'when_dt';    
38185                             options.params['dir'] = 'ASC';    
38186                             this.proxy.loadResponse = this.loadResponse;
38187                             Roo.log("load?");
38188                             //this.addColumns();
38189                         },
38190                         load : function (_self, records, options)
38191                         {
38192                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38193                                 // if you click on the translation.. you can edit it...
38194                                 var el = Roo.get(this);
38195                                 var id = el.dom.getAttribute('data-id');
38196                                 var d = el.dom.getAttribute('data-date');
38197                                 var t = el.dom.getAttribute('data-time');
38198                                 //var id = this.child('span').dom.textContent;
38199                                 
38200                                 //Roo.log(this);
38201                                 Pman.Dialog.CourseCalendar.show({
38202                                     id : id,
38203                                     when_d : d,
38204                                     when_t : t,
38205                                     productitem_active : id ? 1 : 0
38206                                 }, function() {
38207                                     _this.grid.ds.load({});
38208                                 });
38209                            
38210                            });
38211                            
38212                            _this.panel.fireEvent('resize', [ '', '' ]);
38213                         }
38214                     },
38215                     loadResponse : function(o, success, response){
38216                             // this is overridden on before load..
38217                             
38218                             Roo.log("our code?");       
38219                             //Roo.log(success);
38220                             //Roo.log(response)
38221                             delete this.activeRequest;
38222                             if(!success){
38223                                 this.fireEvent("loadexception", this, o, response);
38224                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38225                                 return;
38226                             }
38227                             var result;
38228                             try {
38229                                 result = o.reader.read(response);
38230                             }catch(e){
38231                                 Roo.log("load exception?");
38232                                 this.fireEvent("loadexception", this, o, response, e);
38233                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38234                                 return;
38235                             }
38236                             Roo.log("ready...");        
38237                             // loop through result.records;
38238                             // and set this.tdate[date] = [] << array of records..
38239                             _this.tdata  = {};
38240                             Roo.each(result.records, function(r){
38241                                 //Roo.log(r.data);
38242                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38243                                     _this.tdata[r.data.when_dt.format('j')] = [];
38244                                 }
38245                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38246                             });
38247                             
38248                             //Roo.log(_this.tdata);
38249                             
38250                             result.records = [];
38251                             result.totalRecords = 6;
38252                     
38253                             // let's generate some duumy records for the rows.
38254                             //var st = _this.dateField.getValue();
38255                             
38256                             // work out monday..
38257                             //st = st.add(Date.DAY, -1 * st.format('w'));
38258                             
38259                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38260                             
38261                             var firstOfMonth = date.getFirstDayOfMonth();
38262                             var days = date.getDaysInMonth();
38263                             var d = 1;
38264                             var firstAdded = false;
38265                             for (var i = 0; i < result.totalRecords ; i++) {
38266                                 //var d= st.add(Date.DAY, i);
38267                                 var row = {};
38268                                 var added = 0;
38269                                 for(var w = 0 ; w < 7 ; w++){
38270                                     if(!firstAdded && firstOfMonth != w){
38271                                         continue;
38272                                     }
38273                                     if(d > days){
38274                                         continue;
38275                                     }
38276                                     firstAdded = true;
38277                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38278                                     row['weekday'+w] = String.format(
38279                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38280                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38281                                                     d,
38282                                                     date.format('Y-m-')+dd
38283                                                 );
38284                                     added++;
38285                                     if(typeof(_this.tdata[d]) != 'undefined'){
38286                                         Roo.each(_this.tdata[d], function(r){
38287                                             var is_sub = '';
38288                                             var deactive = '';
38289                                             var id = r.id;
38290                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38291                                             if(r.parent_id*1>0){
38292                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38293                                                 id = r.parent_id;
38294                                             }
38295                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38296                                                 deactive = 'de-act-link';
38297                                             }
38298                                             
38299                                             row['weekday'+w] += String.format(
38300                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38301                                                     id, //0
38302                                                     r.product_id_name, //1
38303                                                     r.when_dt.format('h:ia'), //2
38304                                                     is_sub, //3
38305                                                     deactive, //4
38306                                                     desc // 5
38307                                             );
38308                                         });
38309                                     }
38310                                     d++;
38311                                 }
38312                                 
38313                                 // only do this if something added..
38314                                 if(added > 0){ 
38315                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38316                                 }
38317                                 
38318                                 
38319                                 // push it twice. (second one with an hour..
38320                                 
38321                             }
38322                             //Roo.log(result);
38323                             this.fireEvent("load", this, o, o.request.arg);
38324                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38325                         },
38326                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38327                     proxy : {
38328                         xtype: 'HttpProxy',
38329                         xns: Roo.data,
38330                         method : 'GET',
38331                         url : baseURL + '/Roo/Shop_course.php'
38332                     },
38333                     reader : {
38334                         xtype: 'JsonReader',
38335                         xns: Roo.data,
38336                         id : 'id',
38337                         fields : [
38338                             {
38339                                 'name': 'id',
38340                                 'type': 'int'
38341                             },
38342                             {
38343                                 'name': 'when_dt',
38344                                 'type': 'string'
38345                             },
38346                             {
38347                                 'name': 'end_dt',
38348                                 'type': 'string'
38349                             },
38350                             {
38351                                 'name': 'parent_id',
38352                                 'type': 'int'
38353                             },
38354                             {
38355                                 'name': 'product_id',
38356                                 'type': 'int'
38357                             },
38358                             {
38359                                 'name': 'productitem_id',
38360                                 'type': 'int'
38361                             },
38362                             {
38363                                 'name': 'guid',
38364                                 'type': 'int'
38365                             }
38366                         ]
38367                     }
38368                 },
38369                 toolbar : {
38370                     xtype: 'Toolbar',
38371                     xns: Roo,
38372                     items : [
38373                         {
38374                             xtype: 'Button',
38375                             xns: Roo.Toolbar,
38376                             listeners : {
38377                                 click : function (_self, e)
38378                                 {
38379                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38380                                     sd.setMonth(sd.getMonth()-1);
38381                                     _this.monthField.setValue(sd.format('Y-m-d'));
38382                                     _this.grid.ds.load({});
38383                                 }
38384                             },
38385                             text : "Back"
38386                         },
38387                         {
38388                             xtype: 'Separator',
38389                             xns: Roo.Toolbar
38390                         },
38391                         {
38392                             xtype: 'MonthField',
38393                             xns: Roo.form,
38394                             listeners : {
38395                                 render : function (_self)
38396                                 {
38397                                     _this.monthField = _self;
38398                                    // _this.monthField.set  today
38399                                 },
38400                                 select : function (combo, date)
38401                                 {
38402                                     _this.grid.ds.load({});
38403                                 }
38404                             },
38405                             value : (function() { return new Date(); })()
38406                         },
38407                         {
38408                             xtype: 'Separator',
38409                             xns: Roo.Toolbar
38410                         },
38411                         {
38412                             xtype: 'TextItem',
38413                             xns: Roo.Toolbar,
38414                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38415                         },
38416                         {
38417                             xtype: 'Fill',
38418                             xns: Roo.Toolbar
38419                         },
38420                         {
38421                             xtype: 'Button',
38422                             xns: Roo.Toolbar,
38423                             listeners : {
38424                                 click : function (_self, e)
38425                                 {
38426                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38427                                     sd.setMonth(sd.getMonth()+1);
38428                                     _this.monthField.setValue(sd.format('Y-m-d'));
38429                                     _this.grid.ds.load({});
38430                                 }
38431                             },
38432                             text : "Next"
38433                         }
38434                     ]
38435                 },
38436                  
38437             }
38438         };
38439         
38440         *//*
38441  * Based on:
38442  * Ext JS Library 1.1.1
38443  * Copyright(c) 2006-2007, Ext JS, LLC.
38444  *
38445  * Originally Released Under LGPL - original licence link has changed is not relivant.
38446  *
38447  * Fork - LGPL
38448  * <script type="text/javascript">
38449  */
38450  
38451 /**
38452  * @class Roo.LoadMask
38453  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38454  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38455  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38456  * element's UpdateManager load indicator and will be destroyed after the initial load.
38457  * @constructor
38458  * Create a new LoadMask
38459  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38460  * @param {Object} config The config object
38461  */
38462 Roo.LoadMask = function(el, config){
38463     this.el = Roo.get(el);
38464     Roo.apply(this, config);
38465     if(this.store){
38466         this.store.on('beforeload', this.onBeforeLoad, this);
38467         this.store.on('load', this.onLoad, this);
38468         this.store.on('loadexception', this.onLoadException, this);
38469         this.removeMask = false;
38470     }else{
38471         var um = this.el.getUpdateManager();
38472         um.showLoadIndicator = false; // disable the default indicator
38473         um.on('beforeupdate', this.onBeforeLoad, this);
38474         um.on('update', this.onLoad, this);
38475         um.on('failure', this.onLoad, this);
38476         this.removeMask = true;
38477     }
38478 };
38479
38480 Roo.LoadMask.prototype = {
38481     /**
38482      * @cfg {Boolean} removeMask
38483      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38484      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38485      */
38486     removeMask : false,
38487     /**
38488      * @cfg {String} msg
38489      * The text to display in a centered loading message box (defaults to 'Loading...')
38490      */
38491     msg : 'Loading...',
38492     /**
38493      * @cfg {String} msgCls
38494      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38495      */
38496     msgCls : 'x-mask-loading',
38497
38498     /**
38499      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38500      * @type Boolean
38501      */
38502     disabled: false,
38503
38504     /**
38505      * Disables the mask to prevent it from being displayed
38506      */
38507     disable : function(){
38508        this.disabled = true;
38509     },
38510
38511     /**
38512      * Enables the mask so that it can be displayed
38513      */
38514     enable : function(){
38515         this.disabled = false;
38516     },
38517     
38518     onLoadException : function()
38519     {
38520         Roo.log(arguments);
38521         
38522         if (typeof(arguments[3]) != 'undefined') {
38523             Roo.MessageBox.alert("Error loading",arguments[3]);
38524         } 
38525         /*
38526         try {
38527             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38528                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38529             }   
38530         } catch(e) {
38531             
38532         }
38533         */
38534     
38535         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38536     },
38537     // private
38538     onLoad : function()
38539     {
38540         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38541     },
38542
38543     // private
38544     onBeforeLoad : function(){
38545         if(!this.disabled){
38546             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38547         }
38548     },
38549
38550     // private
38551     destroy : function(){
38552         if(this.store){
38553             this.store.un('beforeload', this.onBeforeLoad, this);
38554             this.store.un('load', this.onLoad, this);
38555             this.store.un('loadexception', this.onLoadException, this);
38556         }else{
38557             var um = this.el.getUpdateManager();
38558             um.un('beforeupdate', this.onBeforeLoad, this);
38559             um.un('update', this.onLoad, this);
38560             um.un('failure', this.onLoad, this);
38561         }
38562     }
38563 };/*
38564  * Based on:
38565  * Ext JS Library 1.1.1
38566  * Copyright(c) 2006-2007, Ext JS, LLC.
38567  *
38568  * Originally Released Under LGPL - original licence link has changed is not relivant.
38569  *
38570  * Fork - LGPL
38571  * <script type="text/javascript">
38572  */
38573
38574
38575 /**
38576  * @class Roo.XTemplate
38577  * @extends Roo.Template
38578  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38579 <pre><code>
38580 var t = new Roo.XTemplate(
38581         '&lt;select name="{name}"&gt;',
38582                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38583         '&lt;/select&gt;'
38584 );
38585  
38586 // then append, applying the master template values
38587  </code></pre>
38588  *
38589  * Supported features:
38590  *
38591  *  Tags:
38592
38593 <pre><code>
38594       {a_variable} - output encoded.
38595       {a_variable.format:("Y-m-d")} - call a method on the variable
38596       {a_variable:raw} - unencoded output
38597       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38598       {a_variable:this.method_on_template(...)} - call a method on the template object.
38599  
38600 </code></pre>
38601  *  The tpl tag:
38602 <pre><code>
38603         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38604         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38605         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38606         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38607   
38608         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38609         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38610 </code></pre>
38611  *      
38612  */
38613 Roo.XTemplate = function()
38614 {
38615     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38616     if (this.html) {
38617         this.compile();
38618     }
38619 };
38620
38621
38622 Roo.extend(Roo.XTemplate, Roo.Template, {
38623
38624     /**
38625      * The various sub templates
38626      */
38627     tpls : false,
38628     /**
38629      *
38630      * basic tag replacing syntax
38631      * WORD:WORD()
38632      *
38633      * // you can fake an object call by doing this
38634      *  x.t:(test,tesT) 
38635      * 
38636      */
38637     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38638
38639     /**
38640      * compile the template
38641      *
38642      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38643      *
38644      */
38645     compile: function()
38646     {
38647         var s = this.html;
38648      
38649         s = ['<tpl>', s, '</tpl>'].join('');
38650     
38651         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38652             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38653             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38654             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38655             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38656             m,
38657             id     = 0,
38658             tpls   = [];
38659     
38660         while(true == !!(m = s.match(re))){
38661             var forMatch   = m[0].match(nameRe),
38662                 ifMatch   = m[0].match(ifRe),
38663                 execMatch   = m[0].match(execRe),
38664                 namedMatch   = m[0].match(namedRe),
38665                 
38666                 exp  = null, 
38667                 fn   = null,
38668                 exec = null,
38669                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38670                 
38671             if (ifMatch) {
38672                 // if - puts fn into test..
38673                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38674                 if(exp){
38675                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38676                 }
38677             }
38678             
38679             if (execMatch) {
38680                 // exec - calls a function... returns empty if true is  returned.
38681                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38682                 if(exp){
38683                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38684                 }
38685             }
38686             
38687             
38688             if (name) {
38689                 // for = 
38690                 switch(name){
38691                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38692                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38693                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38694                 }
38695             }
38696             var uid = namedMatch ? namedMatch[1] : id;
38697             
38698             
38699             tpls.push({
38700                 id:     namedMatch ? namedMatch[1] : id,
38701                 target: name,
38702                 exec:   exec,
38703                 test:   fn,
38704                 body:   m[1] || ''
38705             });
38706             if (namedMatch) {
38707                 s = s.replace(m[0], '');
38708             } else { 
38709                 s = s.replace(m[0], '{xtpl'+ id + '}');
38710             }
38711             ++id;
38712         }
38713         this.tpls = [];
38714         for(var i = tpls.length-1; i >= 0; --i){
38715             this.compileTpl(tpls[i]);
38716             this.tpls[tpls[i].id] = tpls[i];
38717         }
38718         this.master = tpls[tpls.length-1];
38719         return this;
38720     },
38721     /**
38722      * same as applyTemplate, except it's done to one of the subTemplates
38723      * when using named templates, you can do:
38724      *
38725      * var str = pl.applySubTemplate('your-name', values);
38726      *
38727      * 
38728      * @param {Number} id of the template
38729      * @param {Object} values to apply to template
38730      * @param {Object} parent (normaly the instance of this object)
38731      */
38732     applySubTemplate : function(id, values, parent)
38733     {
38734         
38735         
38736         var t = this.tpls[id];
38737         
38738         
38739         try { 
38740             if(t.test && !t.test.call(this, values, parent)){
38741                 return '';
38742             }
38743         } catch(e) {
38744             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38745             Roo.log(e.toString());
38746             Roo.log(t.test);
38747             return ''
38748         }
38749         try { 
38750             
38751             if(t.exec && t.exec.call(this, values, parent)){
38752                 return '';
38753             }
38754         } catch(e) {
38755             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38756             Roo.log(e.toString());
38757             Roo.log(t.exec);
38758             return ''
38759         }
38760         try {
38761             var vs = t.target ? t.target.call(this, values, parent) : values;
38762             parent = t.target ? values : parent;
38763             if(t.target && vs instanceof Array){
38764                 var buf = [];
38765                 for(var i = 0, len = vs.length; i < len; i++){
38766                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38767                 }
38768                 return buf.join('');
38769             }
38770             return t.compiled.call(this, vs, parent);
38771         } catch (e) {
38772             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38773             Roo.log(e.toString());
38774             Roo.log(t.compiled);
38775             return '';
38776         }
38777     },
38778
38779     compileTpl : function(tpl)
38780     {
38781         var fm = Roo.util.Format;
38782         var useF = this.disableFormats !== true;
38783         var sep = Roo.isGecko ? "+" : ",";
38784         var undef = function(str) {
38785             Roo.log("Property not found :"  + str);
38786             return '';
38787         };
38788         
38789         var fn = function(m, name, format, args)
38790         {
38791             //Roo.log(arguments);
38792             args = args ? args.replace(/\\'/g,"'") : args;
38793             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38794             if (typeof(format) == 'undefined') {
38795                 format= 'htmlEncode';
38796             }
38797             if (format == 'raw' ) {
38798                 format = false;
38799             }
38800             
38801             if(name.substr(0, 4) == 'xtpl'){
38802                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38803             }
38804             
38805             // build an array of options to determine if value is undefined..
38806             
38807             // basically get 'xxxx.yyyy' then do
38808             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38809             //    (function () { Roo.log("Property not found"); return ''; })() :
38810             //    ......
38811             
38812             var udef_ar = [];
38813             var lookfor = '';
38814             Roo.each(name.split('.'), function(st) {
38815                 lookfor += (lookfor.length ? '.': '') + st;
38816                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38817             });
38818             
38819             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38820             
38821             
38822             if(format && useF){
38823                 
38824                 args = args ? ',' + args : "";
38825                  
38826                 if(format.substr(0, 5) != "this."){
38827                     format = "fm." + format + '(';
38828                 }else{
38829                     format = 'this.call("'+ format.substr(5) + '", ';
38830                     args = ", values";
38831                 }
38832                 
38833                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38834             }
38835              
38836             if (args.length) {
38837                 // called with xxyx.yuu:(test,test)
38838                 // change to ()
38839                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38840             }
38841             // raw.. - :raw modifier..
38842             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38843             
38844         };
38845         var body;
38846         // branched to use + in gecko and [].join() in others
38847         if(Roo.isGecko){
38848             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38849                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38850                     "';};};";
38851         }else{
38852             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38853             body.push(tpl.body.replace(/(\r\n|\n)/g,
38854                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38855             body.push("'].join('');};};");
38856             body = body.join('');
38857         }
38858         
38859         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38860        
38861         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38862         eval(body);
38863         
38864         return this;
38865     },
38866
38867     applyTemplate : function(values){
38868         return this.master.compiled.call(this, values, {});
38869         //var s = this.subs;
38870     },
38871
38872     apply : function(){
38873         return this.applyTemplate.apply(this, arguments);
38874     }
38875
38876  });
38877
38878 Roo.XTemplate.from = function(el){
38879     el = Roo.getDom(el);
38880     return new Roo.XTemplate(el.value || el.innerHTML);
38881 };