roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Object} an existing reader (eg. copied from another store)
1092  * @cfg {Array} data The multi-dimensional array of data
1093  * @constructor
1094  * @param {Object} config
1095  */
1096 Roo.data.SimpleStore = function(config)
1097 {
1098     Roo.data.SimpleStore.superclass.constructor.call(this, {
1099         isLocal : true,
1100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1101                 id: config.id
1102             },
1103             Roo.data.Record.create(config.fields)
1104         ),
1105         proxy : new Roo.data.MemoryProxy(config.data)
1106     });
1107     this.load();
1108 };
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1110  * Based on:
1111  * Ext JS Library 1.1.1
1112  * Copyright(c) 2006-2007, Ext JS, LLC.
1113  *
1114  * Originally Released Under LGPL - original licence link has changed is not relivant.
1115  *
1116  * Fork - LGPL
1117  * <script type="text/javascript">
1118  */
1119
1120 /**
1121 /**
1122  * @extends Roo.data.Store
1123  * @class Roo.data.JsonStore
1124  * Small helper class to make creating Stores for JSON data easier. <br/>
1125 <pre><code>
1126 var store = new Roo.data.JsonStore({
1127     url: 'get-images.php',
1128     root: 'images',
1129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 });
1131 </code></pre>
1132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133  * JsonReader and HttpProxy (unless inline data is provided).</b>
1134  * @cfg {Array} fields An array of field definition objects, or field name strings.
1135  * @constructor
1136  * @param {Object} config
1137  */
1138 Roo.data.JsonStore = function(c){
1139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141         reader: new Roo.data.JsonReader(c, c.fields)
1142     }));
1143 };
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1145  * Based on:
1146  * Ext JS Library 1.1.1
1147  * Copyright(c) 2006-2007, Ext JS, LLC.
1148  *
1149  * Originally Released Under LGPL - original licence link has changed is not relivant.
1150  *
1151  * Fork - LGPL
1152  * <script type="text/javascript">
1153  */
1154
1155  
1156 Roo.data.Field = function(config){
1157     if(typeof config == "string"){
1158         config = {name: config};
1159     }
1160     Roo.apply(this, config);
1161     
1162     if(!this.type){
1163         this.type = "auto";
1164     }
1165     
1166     var st = Roo.data.SortTypes;
1167     // named sortTypes are supported, here we look them up
1168     if(typeof this.sortType == "string"){
1169         this.sortType = st[this.sortType];
1170     }
1171     
1172     // set default sortType for strings and dates
1173     if(!this.sortType){
1174         switch(this.type){
1175             case "string":
1176                 this.sortType = st.asUCString;
1177                 break;
1178             case "date":
1179                 this.sortType = st.asDate;
1180                 break;
1181             default:
1182                 this.sortType = st.none;
1183         }
1184     }
1185
1186     // define once
1187     var stripRe = /[\$,%]/g;
1188
1189     // prebuilt conversion function for this field, instead of
1190     // switching every time we're reading a value
1191     if(!this.convert){
1192         var cv, dateFormat = this.dateFormat;
1193         switch(this.type){
1194             case "":
1195             case "auto":
1196             case undefined:
1197                 cv = function(v){ return v; };
1198                 break;
1199             case "string":
1200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1201                 break;
1202             case "int":
1203                 cv = function(v){
1204                     return v !== undefined && v !== null && v !== '' ?
1205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1206                     };
1207                 break;
1208             case "float":
1209                 cv = function(v){
1210                     return v !== undefined && v !== null && v !== '' ?
1211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1212                     };
1213                 break;
1214             case "bool":
1215             case "boolean":
1216                 cv = function(v){ return v === true || v === "true" || v == 1; };
1217                 break;
1218             case "date":
1219                 cv = function(v){
1220                     if(!v){
1221                         return '';
1222                     }
1223                     if(v instanceof Date){
1224                         return v;
1225                     }
1226                     if(dateFormat){
1227                         if(dateFormat == "timestamp"){
1228                             return new Date(v*1000);
1229                         }
1230                         return Date.parseDate(v, dateFormat);
1231                     }
1232                     var parsed = Date.parse(v);
1233                     return parsed ? new Date(parsed) : null;
1234                 };
1235              break;
1236             
1237         }
1238         this.convert = cv;
1239     }
1240 };
1241
1242 Roo.data.Field.prototype = {
1243     dateFormat: null,
1244     defaultValue: "",
1245     mapping: null,
1246     sortType : null,
1247     sortDir : "ASC"
1248 };/*
1249  * Based on:
1250  * Ext JS Library 1.1.1
1251  * Copyright(c) 2006-2007, Ext JS, LLC.
1252  *
1253  * Originally Released Under LGPL - original licence link has changed is not relivant.
1254  *
1255  * Fork - LGPL
1256  * <script type="text/javascript">
1257  */
1258  
1259 // Base class for reading structured data from a data source.  This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261
1262 /**
1263  * @class Roo.data.DataReader
1264  * Base class for reading structured data from a data source.  This class is intended to be
1265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266  */
1267
1268 Roo.data.DataReader = function(meta, recordType){
1269     
1270     this.meta = meta;
1271     
1272     this.recordType = recordType instanceof Array ? 
1273         Roo.data.Record.create(recordType) : recordType;
1274 };
1275
1276 Roo.data.DataReader.prototype = {
1277     
1278     
1279     readerType : 'Data',
1280      /**
1281      * Create an empty record
1282      * @param {Object} data (optional) - overlay some values
1283      * @return {Roo.data.Record} record created.
1284      */
1285     newRow :  function(d) {
1286         var da =  {};
1287         this.recordType.prototype.fields.each(function(c) {
1288             switch( c.type) {
1289                 case 'int' : da[c.name] = 0; break;
1290                 case 'date' : da[c.name] = new Date(); break;
1291                 case 'float' : da[c.name] = 0.0; break;
1292                 case 'boolean' : da[c.name] = false; break;
1293                 default : da[c.name] = ""; break;
1294             }
1295             
1296         });
1297         return new this.recordType(Roo.apply(da, d));
1298     }
1299     
1300     
1301 };/*
1302  * Based on:
1303  * Ext JS Library 1.1.1
1304  * Copyright(c) 2006-2007, Ext JS, LLC.
1305  *
1306  * Originally Released Under LGPL - original licence link has changed is not relivant.
1307  *
1308  * Fork - LGPL
1309  * <script type="text/javascript">
1310  */
1311
1312 /**
1313  * @class Roo.data.DataProxy
1314  * @extends Roo.data.Observable
1315  * This class is an abstract base class for implementations which provide retrieval of
1316  * unformatted data objects.<br>
1317  * <p>
1318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319  * (of the appropriate type which knows how to parse the data object) to provide a block of
1320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1321  * <p>
1322  * Custom implementations must implement the load method as described in
1323  * {@link Roo.data.HttpProxy#load}.
1324  */
1325 Roo.data.DataProxy = function(){
1326     this.addEvents({
1327         /**
1328          * @event beforeload
1329          * Fires before a network request is made to retrieve a data object.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} params The params parameter to the load function.
1332          */
1333         beforeload : true,
1334         /**
1335          * @event load
1336          * Fires before the load method's callback is called.
1337          * @param {Object} This DataProxy object.
1338          * @param {Object} o The data object.
1339          * @param {Object} arg The callback argument object passed to the load function.
1340          */
1341         load : true,
1342         /**
1343          * @event loadexception
1344          * Fires if an Exception occurs during data retrieval.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} o The data object.
1347          * @param {Object} arg The callback argument object passed to the load function.
1348          * @param {Object} e The Exception.
1349          */
1350         loadexception : true
1351     });
1352     Roo.data.DataProxy.superclass.constructor.call(this);
1353 };
1354
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1356
1357     /**
1358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1359      */
1360 /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370 /**
1371  * @class Roo.data.MemoryProxy
1372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373  * to the Reader when its load method is called.
1374  * @constructor
1375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1376  */
1377 Roo.data.MemoryProxy = function(data){
1378     if (data.data) {
1379         data = data.data;
1380     }
1381     Roo.data.MemoryProxy.superclass.constructor.call(this);
1382     this.data = data;
1383 };
1384
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1386     
1387     /**
1388      * Load data from the requested source (in this case an in-memory
1389      * data object passed to the constructor), read the data object into
1390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391      * process that block using the passed callback.
1392      * @param {Object} params This parameter is not used by the MemoryProxy class.
1393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394      * object into a block of Roo.data.Records.
1395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396      * The function must be passed <ul>
1397      * <li>The Record block object</li>
1398      * <li>The "arg" argument from the load function</li>
1399      * <li>A boolean success indicator</li>
1400      * </ul>
1401      * @param {Object} scope The scope in which to call the callback
1402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1403      */
1404     load : function(params, reader, callback, scope, arg){
1405         params = params || {};
1406         var result;
1407         try {
1408             result = reader.readRecords(params.data ? params.data :this.data);
1409         }catch(e){
1410             this.fireEvent("loadexception", this, arg, null, e);
1411             callback.call(scope, null, arg, false);
1412             return;
1413         }
1414         callback.call(scope, result, arg, true);
1415     },
1416     
1417     // private
1418     update : function(params, records){
1419         
1420     }
1421 });/*
1422  * Based on:
1423  * Ext JS Library 1.1.1
1424  * Copyright(c) 2006-2007, Ext JS, LLC.
1425  *
1426  * Originally Released Under LGPL - original licence link has changed is not relivant.
1427  *
1428  * Fork - LGPL
1429  * <script type="text/javascript">
1430  */
1431 /**
1432  * @class Roo.data.HttpProxy
1433  * @extends Roo.data.DataProxy
1434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435  * configured to reference a certain URL.<br><br>
1436  * <p>
1437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438  * from which the running page was served.<br><br>
1439  * <p>
1440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1441  * <p>
1442  * Be aware that to enable the browser to parse an XML document, the server must set
1443  * the Content-Type header in the HTTP response to "text/xml".
1444  * @constructor
1445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447  * will be used to make the request.
1448  */
1449 Roo.data.HttpProxy = function(conn){
1450     Roo.data.HttpProxy.superclass.constructor.call(this);
1451     // is conn a conn config or a real conn?
1452     this.conn = conn;
1453     this.useAjax = !conn || !conn.events;
1454   
1455 };
1456
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458     // thse are take from connection...
1459     
1460     /**
1461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1462      */
1463     /**
1464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465      * extra parameters to each request made by this object. (defaults to undefined)
1466      */
1467     /**
1468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469      *  to each request made by this object. (defaults to undefined)
1470      */
1471     /**
1472      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1473      */
1474     /**
1475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1476      */
1477      /**
1478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1479      * @type Boolean
1480      */
1481   
1482
1483     /**
1484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1485      * @type Boolean
1486      */
1487     /**
1488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490      * a finer-grained basis than the DataProxy events.
1491      */
1492     getConnection : function(){
1493         return this.useAjax ? Roo.Ajax : this.conn;
1494     },
1495
1496     /**
1497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499      * process that block using the passed callback.
1500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501      * for the request to the remote server.
1502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503      * object into a block of Roo.data.Records.
1504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505      * The function must be passed <ul>
1506      * <li>The Record block object</li>
1507      * <li>The "arg" argument from the load function</li>
1508      * <li>A boolean success indicator</li>
1509      * </ul>
1510      * @param {Object} scope The scope in which to call the callback
1511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1512      */
1513     load : function(params, reader, callback, scope, arg){
1514         if(this.fireEvent("beforeload", this, params) !== false){
1515             var  o = {
1516                 params : params || {},
1517                 request: {
1518                     callback : callback,
1519                     scope : scope,
1520                     arg : arg
1521                 },
1522                 reader: reader,
1523                 callback : this.loadResponse,
1524                 scope: this
1525             };
1526             if(this.useAjax){
1527                 Roo.applyIf(o, this.conn);
1528                 if(this.activeRequest){
1529                     Roo.Ajax.abort(this.activeRequest);
1530                 }
1531                 this.activeRequest = Roo.Ajax.request(o);
1532             }else{
1533                 this.conn.request(o);
1534             }
1535         }else{
1536             callback.call(scope||this, null, arg, false);
1537         }
1538     },
1539
1540     // private
1541     loadResponse : function(o, success, response){
1542         delete this.activeRequest;
1543         if(!success){
1544             this.fireEvent("loadexception", this, o, response);
1545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1546             return;
1547         }
1548         var result;
1549         try {
1550             result = o.reader.read(response);
1551         }catch(e){
1552             this.fireEvent("loadexception", this, o, response, e);
1553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1554             return;
1555         }
1556         
1557         this.fireEvent("load", this, o, o.request.arg);
1558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1559     },
1560
1561     // private
1562     update : function(dataSet){
1563
1564     },
1565
1566     // private
1567     updateResponse : function(dataSet){
1568
1569     }
1570 });/*
1571  * Based on:
1572  * Ext JS Library 1.1.1
1573  * Copyright(c) 2006-2007, Ext JS, LLC.
1574  *
1575  * Originally Released Under LGPL - original licence link has changed is not relivant.
1576  *
1577  * Fork - LGPL
1578  * <script type="text/javascript">
1579  */
1580
1581 /**
1582  * @class Roo.data.ScriptTagProxy
1583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584  * other than the originating domain of the running page.<br><br>
1585  * <p>
1586  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1588  * <p>
1589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590  * source code that is used as the source inside a &lt;script> tag.<br><br>
1591  * <p>
1592  * In order for the browser to process the returned data, the server must wrap the data object
1593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595  * depending on whether the callback name was passed:
1596  * <p>
1597  * <pre><code>
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1600 if (cb != null) {
1601     scriptTag = true;
1602     response.setContentType("text/javascript");
1603 } else {
1604     response.setContentType("application/x-json");
1605 }
1606 Writer out = response.getWriter();
1607 if (scriptTag) {
1608     out.write(cb + "(");
1609 }
1610 out.print(dataBlock.toJsonString());
1611 if (scriptTag) {
1612     out.write(");");
1613 }
1614 </pre></code>
1615  *
1616  * @constructor
1617  * @param {Object} config A configuration object.
1618  */
1619 Roo.data.ScriptTagProxy = function(config){
1620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621     Roo.apply(this, config);
1622     this.head = document.getElementsByTagName("head")[0];
1623 };
1624
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1626
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1628     /**
1629      * @cfg {String} url The URL from which to request the data object.
1630      */
1631     /**
1632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1633      */
1634     timeout : 30000,
1635     /**
1636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637      * the server the name of the callback function set up by the load call to process the returned data object.
1638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639      * javascript output which calls this named function passing the data object as its only parameter.
1640      */
1641     callbackParam : "callback",
1642     /**
1643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644      * name to the request.
1645      */
1646     nocache : true,
1647
1648     /**
1649      * Load data from the configured URL, read the data object into
1650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651      * process that block using the passed callback.
1652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653      * for the request to the remote server.
1654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655      * object into a block of Roo.data.Records.
1656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657      * The function must be passed <ul>
1658      * <li>The Record block object</li>
1659      * <li>The "arg" argument from the load function</li>
1660      * <li>A boolean success indicator</li>
1661      * </ul>
1662      * @param {Object} scope The scope in which to call the callback
1663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1664      */
1665     load : function(params, reader, callback, scope, arg){
1666         if(this.fireEvent("beforeload", this, params) !== false){
1667
1668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1669
1670             var url = this.url;
1671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1672             if(this.nocache){
1673                 url += "&_dc=" + (new Date().getTime());
1674             }
1675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1676             var trans = {
1677                 id : transId,
1678                 cb : "stcCallback"+transId,
1679                 scriptId : "stcScript"+transId,
1680                 params : params,
1681                 arg : arg,
1682                 url : url,
1683                 callback : callback,
1684                 scope : scope,
1685                 reader : reader
1686             };
1687             var conn = this;
1688
1689             window[trans.cb] = function(o){
1690                 conn.handleResponse(o, trans);
1691             };
1692
1693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1694
1695             if(this.autoAbort !== false){
1696                 this.abort();
1697             }
1698
1699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1700
1701             var script = document.createElement("script");
1702             script.setAttribute("src", url);
1703             script.setAttribute("type", "text/javascript");
1704             script.setAttribute("id", trans.scriptId);
1705             this.head.appendChild(script);
1706
1707             this.trans = trans;
1708         }else{
1709             callback.call(scope||this, null, arg, false);
1710         }
1711     },
1712
1713     // private
1714     isLoading : function(){
1715         return this.trans ? true : false;
1716     },
1717
1718     /**
1719      * Abort the current server request.
1720      */
1721     abort : function(){
1722         if(this.isLoading()){
1723             this.destroyTrans(this.trans);
1724         }
1725     },
1726
1727     // private
1728     destroyTrans : function(trans, isLoaded){
1729         this.head.removeChild(document.getElementById(trans.scriptId));
1730         clearTimeout(trans.timeoutId);
1731         if(isLoaded){
1732             window[trans.cb] = undefined;
1733             try{
1734                 delete window[trans.cb];
1735             }catch(e){}
1736         }else{
1737             // if hasn't been loaded, wait for load to remove it to prevent script error
1738             window[trans.cb] = function(){
1739                 window[trans.cb] = undefined;
1740                 try{
1741                     delete window[trans.cb];
1742                 }catch(e){}
1743             };
1744         }
1745     },
1746
1747     // private
1748     handleResponse : function(o, trans){
1749         this.trans = false;
1750         this.destroyTrans(trans, true);
1751         var result;
1752         try {
1753             result = trans.reader.readRecords(o);
1754         }catch(e){
1755             this.fireEvent("loadexception", this, o, trans.arg, e);
1756             trans.callback.call(trans.scope||window, null, trans.arg, false);
1757             return;
1758         }
1759         this.fireEvent("load", this, o, trans.arg);
1760         trans.callback.call(trans.scope||window, result, trans.arg, true);
1761     },
1762
1763     // private
1764     handleFailure : function(trans){
1765         this.trans = false;
1766         this.destroyTrans(trans, false);
1767         this.fireEvent("loadexception", this, null, trans.arg);
1768         trans.callback.call(trans.scope||window, null, trans.arg, false);
1769     }
1770 });/*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.data.JsonReader
1783  * @extends Roo.data.DataReader
1784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785  * based on mappings in a provided Roo.data.Record constructor.
1786  * 
1787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788  * in the reply previously. 
1789  * 
1790  * <p>
1791  * Example code:
1792  * <pre><code>
1793 var RecordDef = Roo.data.Record.create([
1794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1796 ]);
1797 var myReader = new Roo.data.JsonReader({
1798     totalProperty: "results",    // The property which contains the total dataset size (optional)
1799     root: "rows",                // The property which contains an Array of row objects
1800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1801 }, RecordDef);
1802 </code></pre>
1803  * <p>
1804  * This would consume a JSON file like this:
1805  * <pre><code>
1806 { 'results': 2, 'rows': [
1807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1809 }
1810 </code></pre>
1811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813  * paged from the remote server.
1814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815  * @cfg {String} root name of the property which contains the Array of row objects.
1816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817  * @cfg {Array} fields Array of field definition objects
1818  * @constructor
1819  * Create a new JsonReader
1820  * @param {Object} meta Metadata configuration options
1821  * @param {Object} recordType Either an Array of field definition objects,
1822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1823  */
1824 Roo.data.JsonReader = function(meta, recordType){
1825     
1826     meta = meta || {};
1827     // set some defaults:
1828     Roo.applyIf(meta, {
1829         totalProperty: 'total',
1830         successProperty : 'success',
1831         root : 'data',
1832         id : 'id'
1833     });
1834     
1835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1836 };
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1838     
1839     readerType : 'Json',
1840     
1841     /**
1842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1843      * Used by Store query builder to append _requestMeta to params.
1844      * 
1845      */
1846     metaFromRemote : false,
1847     /**
1848      * This method is only used by a DataProxy which has retrieved data from a remote server.
1849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850      * @return {Object} data A data block which is used by an Roo.data.Store object as
1851      * a cache of Roo.data.Records.
1852      */
1853     read : function(response){
1854         var json = response.responseText;
1855        
1856         var o = /* eval:var:o */ eval("("+json+")");
1857         if(!o) {
1858             throw {message: "JsonReader.read: Json object not found"};
1859         }
1860         
1861         if(o.metaData){
1862             
1863             delete this.ef;
1864             this.metaFromRemote = true;
1865             this.meta = o.metaData;
1866             this.recordType = Roo.data.Record.create(o.metaData.fields);
1867             this.onMetaChange(this.meta, this.recordType, o);
1868         }
1869         return this.readRecords(o);
1870     },
1871
1872     // private function a store will implement
1873     onMetaChange : function(meta, recordType, o){
1874
1875     },
1876
1877     /**
1878          * @ignore
1879          */
1880     simpleAccess: function(obj, subsc) {
1881         return obj[subsc];
1882     },
1883
1884         /**
1885          * @ignore
1886          */
1887     getJsonAccessor: function(){
1888         var re = /[\[\.]/;
1889         return function(expr) {
1890             try {
1891                 return(re.test(expr))
1892                     ? new Function("obj", "return obj." + expr)
1893                     : function(obj){
1894                         return obj[expr];
1895                     };
1896             } catch(e){}
1897             return Roo.emptyFn;
1898         };
1899     }(),
1900
1901     /**
1902      * Create a data block containing Roo.data.Records from an XML document.
1903      * @param {Object} o An object which contains an Array of row objects in the property specified
1904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905      * which contains the total size of the dataset.
1906      * @return {Object} data A data block which is used by an Roo.data.Store object as
1907      * a cache of Roo.data.Records.
1908      */
1909     readRecords : function(o){
1910         /**
1911          * After any data loads, the raw JSON data is available for further custom processing.
1912          * @type Object
1913          */
1914         this.o = o;
1915         var s = this.meta, Record = this.recordType,
1916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1917
1918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1919         if (!this.ef) {
1920             if(s.totalProperty) {
1921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1922                 }
1923                 if(s.successProperty) {
1924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1925                 }
1926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1927                 if (s.id) {
1928                         var g = this.getJsonAccessor(s.id);
1929                         this.getId = function(rec) {
1930                                 var r = g(rec);  
1931                                 return (r === undefined || r === "") ? null : r;
1932                         };
1933                 } else {
1934                         this.getId = function(){return null;};
1935                 }
1936             this.ef = [];
1937             for(var jj = 0; jj < fl; jj++){
1938                 f = fi[jj];
1939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940                 this.ef[jj] = this.getJsonAccessor(map);
1941             }
1942         }
1943
1944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945         if(s.totalProperty){
1946             var vt = parseInt(this.getTotal(o), 10);
1947             if(!isNaN(vt)){
1948                 totalRecords = vt;
1949             }
1950         }
1951         if(s.successProperty){
1952             var vs = this.getSuccess(o);
1953             if(vs === false || vs === 'false'){
1954                 success = false;
1955             }
1956         }
1957         var records = [];
1958         for(var i = 0; i < c; i++){
1959                 var n = root[i];
1960             var values = {};
1961             var id = this.getId(n);
1962             for(var j = 0; j < fl; j++){
1963                 f = fi[j];
1964             var v = this.ef[j](n);
1965             if (!f.convert) {
1966                 Roo.log('missing convert for ' + f.name);
1967                 Roo.log(f);
1968                 continue;
1969             }
1970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1971             }
1972             var record = new Record(values, id);
1973             record.json = n;
1974             records[i] = record;
1975         }
1976         return {
1977             raw : o,
1978             success : success,
1979             records : records,
1980             totalRecords : totalRecords
1981         };
1982     }
1983 });/*
1984  * Based on:
1985  * Ext JS Library 1.1.1
1986  * Copyright(c) 2006-2007, Ext JS, LLC.
1987  *
1988  * Originally Released Under LGPL - original licence link has changed is not relivant.
1989  *
1990  * Fork - LGPL
1991  * <script type="text/javascript">
1992  */
1993
1994 /**
1995  * @class Roo.data.XmlReader
1996  * @extends Roo.data.DataReader
1997  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1998  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1999  * <p>
2000  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2001  * header in the HTTP response must be set to "text/xml".</em>
2002  * <p>
2003  * Example code:
2004  * <pre><code>
2005 var RecordDef = Roo.data.Record.create([
2006    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2007    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2008 ]);
2009 var myReader = new Roo.data.XmlReader({
2010    totalRecords: "results", // The element which contains the total dataset size (optional)
2011    record: "row",           // The repeated element which contains row information
2012    id: "id"                 // The element within the row that provides an ID for the record (optional)
2013 }, RecordDef);
2014 </code></pre>
2015  * <p>
2016  * This would consume an XML file like this:
2017  * <pre><code>
2018 &lt;?xml?>
2019 &lt;dataset>
2020  &lt;results>2&lt;/results>
2021  &lt;row>
2022    &lt;id>1&lt;/id>
2023    &lt;name>Bill&lt;/name>
2024    &lt;occupation>Gardener&lt;/occupation>
2025  &lt;/row>
2026  &lt;row>
2027    &lt;id>2&lt;/id>
2028    &lt;name>Ben&lt;/name>
2029    &lt;occupation>Horticulturalist&lt;/occupation>
2030  &lt;/row>
2031 &lt;/dataset>
2032 </code></pre>
2033  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2034  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2035  * paged from the remote server.
2036  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2037  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2038  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2039  * a record identifier value.
2040  * @constructor
2041  * Create a new XmlReader
2042  * @param {Object} meta Metadata configuration options
2043  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2044  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2045  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2046  */
2047 Roo.data.XmlReader = function(meta, recordType){
2048     meta = meta || {};
2049     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2050 };
2051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2052     
2053     readerType : 'Xml',
2054     
2055     /**
2056      * This method is only used by a DataProxy which has retrieved data from a remote server.
2057          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2058          * to contain a method called 'responseXML' that returns an XML document object.
2059      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2060      * a cache of Roo.data.Records.
2061      */
2062     read : function(response){
2063         var doc = response.responseXML;
2064         if(!doc) {
2065             throw {message: "XmlReader.read: XML Document not available"};
2066         }
2067         return this.readRecords(doc);
2068     },
2069
2070     /**
2071      * Create a data block containing Roo.data.Records from an XML document.
2072          * @param {Object} doc A parsed XML document.
2073      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2074      * a cache of Roo.data.Records.
2075      */
2076     readRecords : function(doc){
2077         /**
2078          * After any data loads/reads, the raw XML Document is available for further custom processing.
2079          * @type XMLDocument
2080          */
2081         this.xmlData = doc;
2082         var root = doc.documentElement || doc;
2083         var q = Roo.DomQuery;
2084         var recordType = this.recordType, fields = recordType.prototype.fields;
2085         var sid = this.meta.id;
2086         var totalRecords = 0, success = true;
2087         if(this.meta.totalRecords){
2088             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2089         }
2090         
2091         if(this.meta.success){
2092             var sv = q.selectValue(this.meta.success, root, true);
2093             success = sv !== false && sv !== 'false';
2094         }
2095         var records = [];
2096         var ns = q.select(this.meta.record, root);
2097         for(var i = 0, len = ns.length; i < len; i++) {
2098                 var n = ns[i];
2099                 var values = {};
2100                 var id = sid ? q.selectValue(sid, n) : undefined;
2101                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2102                     var f = fields.items[j];
2103                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2104                     v = f.convert(v);
2105                     values[f.name] = v;
2106                 }
2107                 var record = new recordType(values, id);
2108                 record.node = n;
2109                 records[records.length] = record;
2110             }
2111
2112             return {
2113                 success : success,
2114                 records : records,
2115                 totalRecords : totalRecords || records.length
2116             };
2117     }
2118 });/*
2119  * Based on:
2120  * Ext JS Library 1.1.1
2121  * Copyright(c) 2006-2007, Ext JS, LLC.
2122  *
2123  * Originally Released Under LGPL - original licence link has changed is not relivant.
2124  *
2125  * Fork - LGPL
2126  * <script type="text/javascript">
2127  */
2128
2129 /**
2130  * @class Roo.data.ArrayReader
2131  * @extends Roo.data.DataReader
2132  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2133  * Each element of that Array represents a row of data fields. The
2134  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2135  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2136  * <p>
2137  * Example code:.
2138  * <pre><code>
2139 var RecordDef = Roo.data.Record.create([
2140     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2141     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2142 ]);
2143 var myReader = new Roo.data.ArrayReader({
2144     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2145 }, RecordDef);
2146 </code></pre>
2147  * <p>
2148  * This would consume an Array like this:
2149  * <pre><code>
2150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2151   </code></pre>
2152  
2153  * @constructor
2154  * Create a new JsonReader
2155  * @param {Object} meta Metadata configuration options.
2156  * @param {Object|Array} recordType Either an Array of field definition objects
2157  * 
2158  * @cfg {Array} fields Array of field definition objects
2159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2160  * as specified to {@link Roo.data.Record#create},
2161  * or an {@link Roo.data.Record} object
2162  *
2163  * 
2164  * created using {@link Roo.data.Record#create}.
2165  */
2166 Roo.data.ArrayReader = function(meta, recordType)
2167 {    
2168     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2169 };
2170
2171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2172     
2173       /**
2174      * Create a data block containing Roo.data.Records from an XML document.
2175      * @param {Object} o An Array of row objects which represents the dataset.
2176      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2177      * a cache of Roo.data.Records.
2178      */
2179     readRecords : function(o)
2180     {
2181         var sid = this.meta ? this.meta.id : null;
2182         var recordType = this.recordType, fields = recordType.prototype.fields;
2183         var records = [];
2184         var root = o;
2185         for(var i = 0; i < root.length; i++){
2186                 var n = root[i];
2187             var values = {};
2188             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2189             for(var j = 0, jlen = fields.length; j < jlen; j++){
2190                 var f = fields.items[j];
2191                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2192                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2193                 v = f.convert(v);
2194                 values[f.name] = v;
2195             }
2196             var record = new recordType(values, id);
2197             record.json = n;
2198             records[records.length] = record;
2199         }
2200         return {
2201             records : records,
2202             totalRecords : records.length
2203         };
2204     },
2205     /**
2206      * using 'cn' the nested child reader read the child array into it's child stores.
2207      * @param {Object} rec The record with a 'children array
2208      */
2209     loadDataFromChildren: function(rec)
2210     {
2211         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2212         return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
2213         
2214     }
2215     
2216     
2217 });/*
2218  * Based on:
2219  * Ext JS Library 1.1.1
2220  * Copyright(c) 2006-2007, Ext JS, LLC.
2221  *
2222  * Originally Released Under LGPL - original licence link has changed is not relivant.
2223  *
2224  * Fork - LGPL
2225  * <script type="text/javascript">
2226  */
2227
2228
2229 /**
2230  * @class Roo.data.Tree
2231  * @extends Roo.util.Observable
2232  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2233  * in the tree have most standard DOM functionality.
2234  * @constructor
2235  * @param {Node} root (optional) The root node
2236  */
2237 Roo.data.Tree = function(root){
2238    this.nodeHash = {};
2239    /**
2240     * The root node for this tree
2241     * @type Node
2242     */
2243    this.root = null;
2244    if(root){
2245        this.setRootNode(root);
2246    }
2247    this.addEvents({
2248        /**
2249         * @event append
2250         * Fires when a new child node is appended to a node in this tree.
2251         * @param {Tree} tree The owner tree
2252         * @param {Node} parent The parent node
2253         * @param {Node} node The newly appended node
2254         * @param {Number} index The index of the newly appended node
2255         */
2256        "append" : true,
2257        /**
2258         * @event remove
2259         * Fires when a child node is removed from a node in this tree.
2260         * @param {Tree} tree The owner tree
2261         * @param {Node} parent The parent node
2262         * @param {Node} node The child node removed
2263         */
2264        "remove" : true,
2265        /**
2266         * @event move
2267         * Fires when a node is moved to a new location in the tree
2268         * @param {Tree} tree The owner tree
2269         * @param {Node} node The node moved
2270         * @param {Node} oldParent The old parent of this node
2271         * @param {Node} newParent The new parent of this node
2272         * @param {Number} index The index it was moved to
2273         */
2274        "move" : true,
2275        /**
2276         * @event insert
2277         * Fires when a new child node is inserted in a node in this tree.
2278         * @param {Tree} tree The owner tree
2279         * @param {Node} parent The parent node
2280         * @param {Node} node The child node inserted
2281         * @param {Node} refNode The child node the node was inserted before
2282         */
2283        "insert" : true,
2284        /**
2285         * @event beforeappend
2286         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2287         * @param {Tree} tree The owner tree
2288         * @param {Node} parent The parent node
2289         * @param {Node} node The child node to be appended
2290         */
2291        "beforeappend" : true,
2292        /**
2293         * @event beforeremove
2294         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2295         * @param {Tree} tree The owner tree
2296         * @param {Node} parent The parent node
2297         * @param {Node} node The child node to be removed
2298         */
2299        "beforeremove" : true,
2300        /**
2301         * @event beforemove
2302         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2303         * @param {Tree} tree The owner tree
2304         * @param {Node} node The node being moved
2305         * @param {Node} oldParent The parent of the node
2306         * @param {Node} newParent The new parent the node is moving to
2307         * @param {Number} index The index it is being moved to
2308         */
2309        "beforemove" : true,
2310        /**
2311         * @event beforeinsert
2312         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2313         * @param {Tree} tree The owner tree
2314         * @param {Node} parent The parent node
2315         * @param {Node} node The child node to be inserted
2316         * @param {Node} refNode The child node the node is being inserted before
2317         */
2318        "beforeinsert" : true
2319    });
2320
2321     Roo.data.Tree.superclass.constructor.call(this);
2322 };
2323
2324 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2325     pathSeparator: "/",
2326
2327     proxyNodeEvent : function(){
2328         return this.fireEvent.apply(this, arguments);
2329     },
2330
2331     /**
2332      * Returns the root node for this tree.
2333      * @return {Node}
2334      */
2335     getRootNode : function(){
2336         return this.root;
2337     },
2338
2339     /**
2340      * Sets the root node for this tree.
2341      * @param {Node} node
2342      * @return {Node}
2343      */
2344     setRootNode : function(node){
2345         this.root = node;
2346         node.ownerTree = this;
2347         node.isRoot = true;
2348         this.registerNode(node);
2349         return node;
2350     },
2351
2352     /**
2353      * Gets a node in this tree by its id.
2354      * @param {String} id
2355      * @return {Node}
2356      */
2357     getNodeById : function(id){
2358         return this.nodeHash[id];
2359     },
2360
2361     registerNode : function(node){
2362         this.nodeHash[node.id] = node;
2363     },
2364
2365     unregisterNode : function(node){
2366         delete this.nodeHash[node.id];
2367     },
2368
2369     toString : function(){
2370         return "[Tree"+(this.id?" "+this.id:"")+"]";
2371     }
2372 });
2373
2374 /**
2375  * @class Roo.data.Node
2376  * @extends Roo.util.Observable
2377  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2378  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2379  * @constructor
2380  * @param {Object} attributes The attributes/config for the node
2381  */
2382 Roo.data.Node = function(attributes){
2383     /**
2384      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2385      * @type {Object}
2386      */
2387     this.attributes = attributes || {};
2388     this.leaf = this.attributes.leaf;
2389     /**
2390      * The node id. @type String
2391      */
2392     this.id = this.attributes.id;
2393     if(!this.id){
2394         this.id = Roo.id(null, "ynode-");
2395         this.attributes.id = this.id;
2396     }
2397      
2398     
2399     /**
2400      * All child nodes of this node. @type Array
2401      */
2402     this.childNodes = [];
2403     if(!this.childNodes.indexOf){ // indexOf is a must
2404         this.childNodes.indexOf = function(o){
2405             for(var i = 0, len = this.length; i < len; i++){
2406                 if(this[i] == o) {
2407                     return i;
2408                 }
2409             }
2410             return -1;
2411         };
2412     }
2413     /**
2414      * The parent node for this node. @type Node
2415      */
2416     this.parentNode = null;
2417     /**
2418      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2419      */
2420     this.firstChild = null;
2421     /**
2422      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2423      */
2424     this.lastChild = null;
2425     /**
2426      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2427      */
2428     this.previousSibling = null;
2429     /**
2430      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2431      */
2432     this.nextSibling = null;
2433
2434     this.addEvents({
2435        /**
2436         * @event append
2437         * Fires when a new child node is appended
2438         * @param {Tree} tree The owner tree
2439         * @param {Node} this This node
2440         * @param {Node} node The newly appended node
2441         * @param {Number} index The index of the newly appended node
2442         */
2443        "append" : true,
2444        /**
2445         * @event remove
2446         * Fires when a child node is removed
2447         * @param {Tree} tree The owner tree
2448         * @param {Node} this This node
2449         * @param {Node} node The removed node
2450         */
2451        "remove" : true,
2452        /**
2453         * @event move
2454         * Fires when this node is moved to a new location in the tree
2455         * @param {Tree} tree The owner tree
2456         * @param {Node} this This node
2457         * @param {Node} oldParent The old parent of this node
2458         * @param {Node} newParent The new parent of this node
2459         * @param {Number} index The index it was moved to
2460         */
2461        "move" : true,
2462        /**
2463         * @event insert
2464         * Fires when a new child node is inserted.
2465         * @param {Tree} tree The owner tree
2466         * @param {Node} this This node
2467         * @param {Node} node The child node inserted
2468         * @param {Node} refNode The child node the node was inserted before
2469         */
2470        "insert" : true,
2471        /**
2472         * @event beforeappend
2473         * Fires before a new child is appended, return false to cancel the append.
2474         * @param {Tree} tree The owner tree
2475         * @param {Node} this This node
2476         * @param {Node} node The child node to be appended
2477         */
2478        "beforeappend" : true,
2479        /**
2480         * @event beforeremove
2481         * Fires before a child is removed, return false to cancel the remove.
2482         * @param {Tree} tree The owner tree
2483         * @param {Node} this This node
2484         * @param {Node} node The child node to be removed
2485         */
2486        "beforeremove" : true,
2487        /**
2488         * @event beforemove
2489         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2490         * @param {Tree} tree The owner tree
2491         * @param {Node} this This node
2492         * @param {Node} oldParent The parent of this node
2493         * @param {Node} newParent The new parent this node is moving to
2494         * @param {Number} index The index it is being moved to
2495         */
2496        "beforemove" : true,
2497        /**
2498         * @event beforeinsert
2499         * Fires before a new child is inserted, return false to cancel the insert.
2500         * @param {Tree} tree The owner tree
2501         * @param {Node} this This node
2502         * @param {Node} node The child node to be inserted
2503         * @param {Node} refNode The child node the node is being inserted before
2504         */
2505        "beforeinsert" : true
2506    });
2507     this.listeners = this.attributes.listeners;
2508     Roo.data.Node.superclass.constructor.call(this);
2509 };
2510
2511 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2512     fireEvent : function(evtName){
2513         // first do standard event for this node
2514         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2515             return false;
2516         }
2517         // then bubble it up to the tree if the event wasn't cancelled
2518         var ot = this.getOwnerTree();
2519         if(ot){
2520             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2521                 return false;
2522             }
2523         }
2524         return true;
2525     },
2526
2527     /**
2528      * Returns true if this node is a leaf
2529      * @return {Boolean}
2530      */
2531     isLeaf : function(){
2532         return this.leaf === true;
2533     },
2534
2535     // private
2536     setFirstChild : function(node){
2537         this.firstChild = node;
2538     },
2539
2540     //private
2541     setLastChild : function(node){
2542         this.lastChild = node;
2543     },
2544
2545
2546     /**
2547      * Returns true if this node is the last child of its parent
2548      * @return {Boolean}
2549      */
2550     isLast : function(){
2551        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2552     },
2553
2554     /**
2555      * Returns true if this node is the first child of its parent
2556      * @return {Boolean}
2557      */
2558     isFirst : function(){
2559        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2560     },
2561
2562     hasChildNodes : function(){
2563         return !this.isLeaf() && this.childNodes.length > 0;
2564     },
2565
2566     /**
2567      * Insert node(s) as the last child node of this node.
2568      * @param {Node/Array} node The node or Array of nodes to append
2569      * @return {Node} The appended node if single append, or null if an array was passed
2570      */
2571     appendChild : function(node){
2572         var multi = false;
2573         if(node instanceof Array){
2574             multi = node;
2575         }else if(arguments.length > 1){
2576             multi = arguments;
2577         }
2578         
2579         // if passed an array or multiple args do them one by one
2580         if(multi){
2581             for(var i = 0, len = multi.length; i < len; i++) {
2582                 this.appendChild(multi[i]);
2583             }
2584         }else{
2585             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2586                 return false;
2587             }
2588             var index = this.childNodes.length;
2589             var oldParent = node.parentNode;
2590             // it's a move, make sure we move it cleanly
2591             if(oldParent){
2592                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2593                     return false;
2594                 }
2595                 oldParent.removeChild(node);
2596             }
2597             
2598             index = this.childNodes.length;
2599             if(index == 0){
2600                 this.setFirstChild(node);
2601             }
2602             this.childNodes.push(node);
2603             node.parentNode = this;
2604             var ps = this.childNodes[index-1];
2605             if(ps){
2606                 node.previousSibling = ps;
2607                 ps.nextSibling = node;
2608             }else{
2609                 node.previousSibling = null;
2610             }
2611             node.nextSibling = null;
2612             this.setLastChild(node);
2613             node.setOwnerTree(this.getOwnerTree());
2614             this.fireEvent("append", this.ownerTree, this, node, index);
2615             if(this.ownerTree) {
2616                 this.ownerTree.fireEvent("appendnode", this, node, index);
2617             }
2618             if(oldParent){
2619                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2620             }
2621             return node;
2622         }
2623     },
2624
2625     /**
2626      * Removes a child node from this node.
2627      * @param {Node} node The node to remove
2628      * @return {Node} The removed node
2629      */
2630     removeChild : function(node){
2631         var index = this.childNodes.indexOf(node);
2632         if(index == -1){
2633             return false;
2634         }
2635         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2636             return false;
2637         }
2638
2639         // remove it from childNodes collection
2640         this.childNodes.splice(index, 1);
2641
2642         // update siblings
2643         if(node.previousSibling){
2644             node.previousSibling.nextSibling = node.nextSibling;
2645         }
2646         if(node.nextSibling){
2647             node.nextSibling.previousSibling = node.previousSibling;
2648         }
2649
2650         // update child refs
2651         if(this.firstChild == node){
2652             this.setFirstChild(node.nextSibling);
2653         }
2654         if(this.lastChild == node){
2655             this.setLastChild(node.previousSibling);
2656         }
2657
2658         node.setOwnerTree(null);
2659         // clear any references from the node
2660         node.parentNode = null;
2661         node.previousSibling = null;
2662         node.nextSibling = null;
2663         this.fireEvent("remove", this.ownerTree, this, node);
2664         return node;
2665     },
2666
2667     /**
2668      * Inserts the first node before the second node in this nodes childNodes collection.
2669      * @param {Node} node The node to insert
2670      * @param {Node} refNode The node to insert before (if null the node is appended)
2671      * @return {Node} The inserted node
2672      */
2673     insertBefore : function(node, refNode){
2674         if(!refNode){ // like standard Dom, refNode can be null for append
2675             return this.appendChild(node);
2676         }
2677         // nothing to do
2678         if(node == refNode){
2679             return false;
2680         }
2681
2682         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2683             return false;
2684         }
2685         var index = this.childNodes.indexOf(refNode);
2686         var oldParent = node.parentNode;
2687         var refIndex = index;
2688
2689         // when moving internally, indexes will change after remove
2690         if(oldParent == this && this.childNodes.indexOf(node) < index){
2691             refIndex--;
2692         }
2693
2694         // it's a move, make sure we move it cleanly
2695         if(oldParent){
2696             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2697                 return false;
2698             }
2699             oldParent.removeChild(node);
2700         }
2701         if(refIndex == 0){
2702             this.setFirstChild(node);
2703         }
2704         this.childNodes.splice(refIndex, 0, node);
2705         node.parentNode = this;
2706         var ps = this.childNodes[refIndex-1];
2707         if(ps){
2708             node.previousSibling = ps;
2709             ps.nextSibling = node;
2710         }else{
2711             node.previousSibling = null;
2712         }
2713         node.nextSibling = refNode;
2714         refNode.previousSibling = node;
2715         node.setOwnerTree(this.getOwnerTree());
2716         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2717         if(oldParent){
2718             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2719         }
2720         return node;
2721     },
2722
2723     /**
2724      * Returns the child node at the specified index.
2725      * @param {Number} index
2726      * @return {Node}
2727      */
2728     item : function(index){
2729         return this.childNodes[index];
2730     },
2731
2732     /**
2733      * Replaces one child node in this node with another.
2734      * @param {Node} newChild The replacement node
2735      * @param {Node} oldChild The node to replace
2736      * @return {Node} The replaced node
2737      */
2738     replaceChild : function(newChild, oldChild){
2739         this.insertBefore(newChild, oldChild);
2740         this.removeChild(oldChild);
2741         return oldChild;
2742     },
2743
2744     /**
2745      * Returns the index of a child node
2746      * @param {Node} node
2747      * @return {Number} The index of the node or -1 if it was not found
2748      */
2749     indexOf : function(child){
2750         return this.childNodes.indexOf(child);
2751     },
2752
2753     /**
2754      * Returns the tree this node is in.
2755      * @return {Tree}
2756      */
2757     getOwnerTree : function(){
2758         // if it doesn't have one, look for one
2759         if(!this.ownerTree){
2760             var p = this;
2761             while(p){
2762                 if(p.ownerTree){
2763                     this.ownerTree = p.ownerTree;
2764                     break;
2765                 }
2766                 p = p.parentNode;
2767             }
2768         }
2769         return this.ownerTree;
2770     },
2771
2772     /**
2773      * Returns depth of this node (the root node has a depth of 0)
2774      * @return {Number}
2775      */
2776     getDepth : function(){
2777         var depth = 0;
2778         var p = this;
2779         while(p.parentNode){
2780             ++depth;
2781             p = p.parentNode;
2782         }
2783         return depth;
2784     },
2785
2786     // private
2787     setOwnerTree : function(tree){
2788         // if it's move, we need to update everyone
2789         if(tree != this.ownerTree){
2790             if(this.ownerTree){
2791                 this.ownerTree.unregisterNode(this);
2792             }
2793             this.ownerTree = tree;
2794             var cs = this.childNodes;
2795             for(var i = 0, len = cs.length; i < len; i++) {
2796                 cs[i].setOwnerTree(tree);
2797             }
2798             if(tree){
2799                 tree.registerNode(this);
2800             }
2801         }
2802     },
2803
2804     /**
2805      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2806      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2807      * @return {String} The path
2808      */
2809     getPath : function(attr){
2810         attr = attr || "id";
2811         var p = this.parentNode;
2812         var b = [this.attributes[attr]];
2813         while(p){
2814             b.unshift(p.attributes[attr]);
2815             p = p.parentNode;
2816         }
2817         var sep = this.getOwnerTree().pathSeparator;
2818         return sep + b.join(sep);
2819     },
2820
2821     /**
2822      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2823      * function call will be the scope provided or the current node. The arguments to the function
2824      * will be the args provided or the current node. If the function returns false at any point,
2825      * the bubble is stopped.
2826      * @param {Function} fn The function to call
2827      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2828      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2829      */
2830     bubble : function(fn, scope, args){
2831         var p = this;
2832         while(p){
2833             if(fn.call(scope || p, args || p) === false){
2834                 break;
2835             }
2836             p = p.parentNode;
2837         }
2838     },
2839
2840     /**
2841      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2842      * function call will be the scope provided or the current node. The arguments to the function
2843      * will be the args provided or the current node. If the function returns false at any point,
2844      * the cascade is stopped on that branch.
2845      * @param {Function} fn The function to call
2846      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2847      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2848      */
2849     cascade : function(fn, scope, args){
2850         if(fn.call(scope || this, args || this) !== false){
2851             var cs = this.childNodes;
2852             for(var i = 0, len = cs.length; i < len; i++) {
2853                 cs[i].cascade(fn, scope, args);
2854             }
2855         }
2856     },
2857
2858     /**
2859      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2860      * function call will be the scope provided or the current node. The arguments to the function
2861      * will be the args provided or the current node. If the function returns false at any point,
2862      * the iteration stops.
2863      * @param {Function} fn The function to call
2864      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2865      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2866      */
2867     eachChild : function(fn, scope, args){
2868         var cs = this.childNodes;
2869         for(var i = 0, len = cs.length; i < len; i++) {
2870                 if(fn.call(scope || this, args || cs[i]) === false){
2871                     break;
2872                 }
2873         }
2874     },
2875
2876     /**
2877      * Finds the first child that has the attribute with the specified value.
2878      * @param {String} attribute The attribute name
2879      * @param {Mixed} value The value to search for
2880      * @return {Node} The found child or null if none was found
2881      */
2882     findChild : function(attribute, value){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(cs[i].attributes[attribute] == value){
2886                     return cs[i];
2887                 }
2888         }
2889         return null;
2890     },
2891
2892     /**
2893      * Finds the first child by a custom function. The child matches if the function passed
2894      * returns true.
2895      * @param {Function} fn
2896      * @param {Object} scope (optional)
2897      * @return {Node} The found child or null if none was found
2898      */
2899     findChildBy : function(fn, scope){
2900         var cs = this.childNodes;
2901         for(var i = 0, len = cs.length; i < len; i++) {
2902                 if(fn.call(scope||cs[i], cs[i]) === true){
2903                     return cs[i];
2904                 }
2905         }
2906         return null;
2907     },
2908
2909     /**
2910      * Sorts this nodes children using the supplied sort function
2911      * @param {Function} fn
2912      * @param {Object} scope (optional)
2913      */
2914     sort : function(fn, scope){
2915         var cs = this.childNodes;
2916         var len = cs.length;
2917         if(len > 0){
2918             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2919             cs.sort(sortFn);
2920             for(var i = 0; i < len; i++){
2921                 var n = cs[i];
2922                 n.previousSibling = cs[i-1];
2923                 n.nextSibling = cs[i+1];
2924                 if(i == 0){
2925                     this.setFirstChild(n);
2926                 }
2927                 if(i == len-1){
2928                     this.setLastChild(n);
2929                 }
2930             }
2931         }
2932     },
2933
2934     /**
2935      * Returns true if this node is an ancestor (at any point) of the passed node.
2936      * @param {Node} node
2937      * @return {Boolean}
2938      */
2939     contains : function(node){
2940         return node.isAncestor(this);
2941     },
2942
2943     /**
2944      * Returns true if the passed node is an ancestor (at any point) of this node.
2945      * @param {Node} node
2946      * @return {Boolean}
2947      */
2948     isAncestor : function(node){
2949         var p = this.parentNode;
2950         while(p){
2951             if(p == node){
2952                 return true;
2953             }
2954             p = p.parentNode;
2955         }
2956         return false;
2957     },
2958
2959     toString : function(){
2960         return "[Node"+(this.id?" "+this.id:"")+"]";
2961     }
2962 });/*
2963  * Based on:
2964  * Ext JS Library 1.1.1
2965  * Copyright(c) 2006-2007, Ext JS, LLC.
2966  *
2967  * Originally Released Under LGPL - original licence link has changed is not relivant.
2968  *
2969  * Fork - LGPL
2970  * <script type="text/javascript">
2971  */
2972  (function(){ 
2973 /**
2974  * @class Roo.Layer
2975  * @extends Roo.Element
2976  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2977  * automatic maintaining of shadow/shim positions.
2978  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2979  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2980  * you can pass a string with a CSS class name. False turns off the shadow.
2981  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2982  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2983  * @cfg {String} cls CSS class to add to the element
2984  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2985  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2986  * @constructor
2987  * @param {Object} config An object with config options.
2988  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2989  */
2990
2991 Roo.Layer = function(config, existingEl){
2992     config = config || {};
2993     var dh = Roo.DomHelper;
2994     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2995     if(existingEl){
2996         this.dom = Roo.getDom(existingEl);
2997     }
2998     if(!this.dom){
2999         var o = config.dh || {tag: "div", cls: "x-layer"};
3000         this.dom = dh.append(pel, o);
3001     }
3002     if(config.cls){
3003         this.addClass(config.cls);
3004     }
3005     this.constrain = config.constrain !== false;
3006     this.visibilityMode = Roo.Element.VISIBILITY;
3007     if(config.id){
3008         this.id = this.dom.id = config.id;
3009     }else{
3010         this.id = Roo.id(this.dom);
3011     }
3012     this.zindex = config.zindex || this.getZIndex();
3013     this.position("absolute", this.zindex);
3014     if(config.shadow){
3015         this.shadowOffset = config.shadowOffset || 4;
3016         this.shadow = new Roo.Shadow({
3017             offset : this.shadowOffset,
3018             mode : config.shadow
3019         });
3020     }else{
3021         this.shadowOffset = 0;
3022     }
3023     this.useShim = config.shim !== false && Roo.useShims;
3024     this.useDisplay = config.useDisplay;
3025     this.hide();
3026 };
3027
3028 var supr = Roo.Element.prototype;
3029
3030 // shims are shared among layer to keep from having 100 iframes
3031 var shims = [];
3032
3033 Roo.extend(Roo.Layer, Roo.Element, {
3034
3035     getZIndex : function(){
3036         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3037     },
3038
3039     getShim : function(){
3040         if(!this.useShim){
3041             return null;
3042         }
3043         if(this.shim){
3044             return this.shim;
3045         }
3046         var shim = shims.shift();
3047         if(!shim){
3048             shim = this.createShim();
3049             shim.enableDisplayMode('block');
3050             shim.dom.style.display = 'none';
3051             shim.dom.style.visibility = 'visible';
3052         }
3053         var pn = this.dom.parentNode;
3054         if(shim.dom.parentNode != pn){
3055             pn.insertBefore(shim.dom, this.dom);
3056         }
3057         shim.setStyle('z-index', this.getZIndex()-2);
3058         this.shim = shim;
3059         return shim;
3060     },
3061
3062     hideShim : function(){
3063         if(this.shim){
3064             this.shim.setDisplayed(false);
3065             shims.push(this.shim);
3066             delete this.shim;
3067         }
3068     },
3069
3070     disableShadow : function(){
3071         if(this.shadow){
3072             this.shadowDisabled = true;
3073             this.shadow.hide();
3074             this.lastShadowOffset = this.shadowOffset;
3075             this.shadowOffset = 0;
3076         }
3077     },
3078
3079     enableShadow : function(show){
3080         if(this.shadow){
3081             this.shadowDisabled = false;
3082             this.shadowOffset = this.lastShadowOffset;
3083             delete this.lastShadowOffset;
3084             if(show){
3085                 this.sync(true);
3086             }
3087         }
3088     },
3089
3090     // private
3091     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3092     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3093     sync : function(doShow){
3094         var sw = this.shadow;
3095         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3096             var sh = this.getShim();
3097
3098             var w = this.getWidth(),
3099                 h = this.getHeight();
3100
3101             var l = this.getLeft(true),
3102                 t = this.getTop(true);
3103
3104             if(sw && !this.shadowDisabled){
3105                 if(doShow && !sw.isVisible()){
3106                     sw.show(this);
3107                 }else{
3108                     sw.realign(l, t, w, h);
3109                 }
3110                 if(sh){
3111                     if(doShow){
3112                        sh.show();
3113                     }
3114                     // fit the shim behind the shadow, so it is shimmed too
3115                     var a = sw.adjusts, s = sh.dom.style;
3116                     s.left = (Math.min(l, l+a.l))+"px";
3117                     s.top = (Math.min(t, t+a.t))+"px";
3118                     s.width = (w+a.w)+"px";
3119                     s.height = (h+a.h)+"px";
3120                 }
3121             }else if(sh){
3122                 if(doShow){
3123                    sh.show();
3124                 }
3125                 sh.setSize(w, h);
3126                 sh.setLeftTop(l, t);
3127             }
3128             
3129         }
3130     },
3131
3132     // private
3133     destroy : function(){
3134         this.hideShim();
3135         if(this.shadow){
3136             this.shadow.hide();
3137         }
3138         this.removeAllListeners();
3139         var pn = this.dom.parentNode;
3140         if(pn){
3141             pn.removeChild(this.dom);
3142         }
3143         Roo.Element.uncache(this.id);
3144     },
3145
3146     remove : function(){
3147         this.destroy();
3148     },
3149
3150     // private
3151     beginUpdate : function(){
3152         this.updating = true;
3153     },
3154
3155     // private
3156     endUpdate : function(){
3157         this.updating = false;
3158         this.sync(true);
3159     },
3160
3161     // private
3162     hideUnders : function(negOffset){
3163         if(this.shadow){
3164             this.shadow.hide();
3165         }
3166         this.hideShim();
3167     },
3168
3169     // private
3170     constrainXY : function(){
3171         if(this.constrain){
3172             var vw = Roo.lib.Dom.getViewWidth(),
3173                 vh = Roo.lib.Dom.getViewHeight();
3174             var s = Roo.get(document).getScroll();
3175
3176             var xy = this.getXY();
3177             var x = xy[0], y = xy[1];   
3178             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3179             // only move it if it needs it
3180             var moved = false;
3181             // first validate right/bottom
3182             if((x + w) > vw+s.left){
3183                 x = vw - w - this.shadowOffset;
3184                 moved = true;
3185             }
3186             if((y + h) > vh+s.top){
3187                 y = vh - h - this.shadowOffset;
3188                 moved = true;
3189             }
3190             // then make sure top/left isn't negative
3191             if(x < s.left){
3192                 x = s.left;
3193                 moved = true;
3194             }
3195             if(y < s.top){
3196                 y = s.top;
3197                 moved = true;
3198             }
3199             if(moved){
3200                 if(this.avoidY){
3201                     var ay = this.avoidY;
3202                     if(y <= ay && (y+h) >= ay){
3203                         y = ay-h-5;   
3204                     }
3205                 }
3206                 xy = [x, y];
3207                 this.storeXY(xy);
3208                 supr.setXY.call(this, xy);
3209                 this.sync();
3210             }
3211         }
3212     },
3213
3214     isVisible : function(){
3215         return this.visible;    
3216     },
3217
3218     // private
3219     showAction : function(){
3220         this.visible = true; // track visibility to prevent getStyle calls
3221         if(this.useDisplay === true){
3222             this.setDisplayed("");
3223         }else if(this.lastXY){
3224             supr.setXY.call(this, this.lastXY);
3225         }else if(this.lastLT){
3226             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3227         }
3228     },
3229
3230     // private
3231     hideAction : function(){
3232         this.visible = false;
3233         if(this.useDisplay === true){
3234             this.setDisplayed(false);
3235         }else{
3236             this.setLeftTop(-10000,-10000);
3237         }
3238     },
3239
3240     // overridden Element method
3241     setVisible : function(v, a, d, c, e){
3242         if(v){
3243             this.showAction();
3244         }
3245         if(a && v){
3246             var cb = function(){
3247                 this.sync(true);
3248                 if(c){
3249                     c();
3250                 }
3251             }.createDelegate(this);
3252             supr.setVisible.call(this, true, true, d, cb, e);
3253         }else{
3254             if(!v){
3255                 this.hideUnders(true);
3256             }
3257             var cb = c;
3258             if(a){
3259                 cb = function(){
3260                     this.hideAction();
3261                     if(c){
3262                         c();
3263                     }
3264                 }.createDelegate(this);
3265             }
3266             supr.setVisible.call(this, v, a, d, cb, e);
3267             if(v){
3268                 this.sync(true);
3269             }else if(!a){
3270                 this.hideAction();
3271             }
3272         }
3273     },
3274
3275     storeXY : function(xy){
3276         delete this.lastLT;
3277         this.lastXY = xy;
3278     },
3279
3280     storeLeftTop : function(left, top){
3281         delete this.lastXY;
3282         this.lastLT = [left, top];
3283     },
3284
3285     // private
3286     beforeFx : function(){
3287         this.beforeAction();
3288         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3289     },
3290
3291     // private
3292     afterFx : function(){
3293         Roo.Layer.superclass.afterFx.apply(this, arguments);
3294         this.sync(this.isVisible());
3295     },
3296
3297     // private
3298     beforeAction : function(){
3299         if(!this.updating && this.shadow){
3300             this.shadow.hide();
3301         }
3302     },
3303
3304     // overridden Element method
3305     setLeft : function(left){
3306         this.storeLeftTop(left, this.getTop(true));
3307         supr.setLeft.apply(this, arguments);
3308         this.sync();
3309     },
3310
3311     setTop : function(top){
3312         this.storeLeftTop(this.getLeft(true), top);
3313         supr.setTop.apply(this, arguments);
3314         this.sync();
3315     },
3316
3317     setLeftTop : function(left, top){
3318         this.storeLeftTop(left, top);
3319         supr.setLeftTop.apply(this, arguments);
3320         this.sync();
3321     },
3322
3323     setXY : function(xy, a, d, c, e){
3324         this.fixDisplay();
3325         this.beforeAction();
3326         this.storeXY(xy);
3327         var cb = this.createCB(c);
3328         supr.setXY.call(this, xy, a, d, cb, e);
3329         if(!a){
3330             cb();
3331         }
3332     },
3333
3334     // private
3335     createCB : function(c){
3336         var el = this;
3337         return function(){
3338             el.constrainXY();
3339             el.sync(true);
3340             if(c){
3341                 c();
3342             }
3343         };
3344     },
3345
3346     // overridden Element method
3347     setX : function(x, a, d, c, e){
3348         this.setXY([x, this.getY()], a, d, c, e);
3349     },
3350
3351     // overridden Element method
3352     setY : function(y, a, d, c, e){
3353         this.setXY([this.getX(), y], a, d, c, e);
3354     },
3355
3356     // overridden Element method
3357     setSize : function(w, h, a, d, c, e){
3358         this.beforeAction();
3359         var cb = this.createCB(c);
3360         supr.setSize.call(this, w, h, a, d, cb, e);
3361         if(!a){
3362             cb();
3363         }
3364     },
3365
3366     // overridden Element method
3367     setWidth : function(w, a, d, c, e){
3368         this.beforeAction();
3369         var cb = this.createCB(c);
3370         supr.setWidth.call(this, w, a, d, cb, e);
3371         if(!a){
3372             cb();
3373         }
3374     },
3375
3376     // overridden Element method
3377     setHeight : function(h, a, d, c, e){
3378         this.beforeAction();
3379         var cb = this.createCB(c);
3380         supr.setHeight.call(this, h, a, d, cb, e);
3381         if(!a){
3382             cb();
3383         }
3384     },
3385
3386     // overridden Element method
3387     setBounds : function(x, y, w, h, a, d, c, e){
3388         this.beforeAction();
3389         var cb = this.createCB(c);
3390         if(!a){
3391             this.storeXY([x, y]);
3392             supr.setXY.call(this, [x, y]);
3393             supr.setSize.call(this, w, h, a, d, cb, e);
3394             cb();
3395         }else{
3396             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3397         }
3398         return this;
3399     },
3400     
3401     /**
3402      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3403      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3404      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3405      * @param {Number} zindex The new z-index to set
3406      * @return {this} The Layer
3407      */
3408     setZIndex : function(zindex){
3409         this.zindex = zindex;
3410         this.setStyle("z-index", zindex + 2);
3411         if(this.shadow){
3412             this.shadow.setZIndex(zindex + 1);
3413         }
3414         if(this.shim){
3415             this.shim.setStyle("z-index", zindex);
3416         }
3417     }
3418 });
3419 })();/*
3420  * Based on:
3421  * Ext JS Library 1.1.1
3422  * Copyright(c) 2006-2007, Ext JS, LLC.
3423  *
3424  * Originally Released Under LGPL - original licence link has changed is not relivant.
3425  *
3426  * Fork - LGPL
3427  * <script type="text/javascript">
3428  */
3429
3430
3431 /**
3432  * @class Roo.Shadow
3433  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3434  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3435  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3436  * @constructor
3437  * Create a new Shadow
3438  * @param {Object} config The config object
3439  */
3440 Roo.Shadow = function(config){
3441     Roo.apply(this, config);
3442     if(typeof this.mode != "string"){
3443         this.mode = this.defaultMode;
3444     }
3445     var o = this.offset, a = {h: 0};
3446     var rad = Math.floor(this.offset/2);
3447     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3448         case "drop":
3449             a.w = 0;
3450             a.l = a.t = o;
3451             a.t -= 1;
3452             if(Roo.isIE){
3453                 a.l -= this.offset + rad;
3454                 a.t -= this.offset + rad;
3455                 a.w -= rad;
3456                 a.h -= rad;
3457                 a.t += 1;
3458             }
3459         break;
3460         case "sides":
3461             a.w = (o*2);
3462             a.l = -o;
3463             a.t = o-1;
3464             if(Roo.isIE){
3465                 a.l -= (this.offset - rad);
3466                 a.t -= this.offset + rad;
3467                 a.l += 1;
3468                 a.w -= (this.offset - rad)*2;
3469                 a.w -= rad + 1;
3470                 a.h -= 1;
3471             }
3472         break;
3473         case "frame":
3474             a.w = a.h = (o*2);
3475             a.l = a.t = -o;
3476             a.t += 1;
3477             a.h -= 2;
3478             if(Roo.isIE){
3479                 a.l -= (this.offset - rad);
3480                 a.t -= (this.offset - rad);
3481                 a.l += 1;
3482                 a.w -= (this.offset + rad + 1);
3483                 a.h -= (this.offset + rad);
3484                 a.h += 1;
3485             }
3486         break;
3487     };
3488
3489     this.adjusts = a;
3490 };
3491
3492 Roo.Shadow.prototype = {
3493     /**
3494      * @cfg {String} mode
3495      * The shadow display mode.  Supports the following options:<br />
3496      * sides: Shadow displays on both sides and bottom only<br />
3497      * frame: Shadow displays equally on all four sides<br />
3498      * drop: Traditional bottom-right drop shadow (default)
3499      */
3500     /**
3501      * @cfg {String} offset
3502      * The number of pixels to offset the shadow from the element (defaults to 4)
3503      */
3504     offset: 4,
3505
3506     // private
3507     defaultMode: "drop",
3508
3509     /**
3510      * Displays the shadow under the target element
3511      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3512      */
3513     show : function(target){
3514         target = Roo.get(target);
3515         if(!this.el){
3516             this.el = Roo.Shadow.Pool.pull();
3517             if(this.el.dom.nextSibling != target.dom){
3518                 this.el.insertBefore(target);
3519             }
3520         }
3521         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3522         if(Roo.isIE){
3523             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3524         }
3525         this.realign(
3526             target.getLeft(true),
3527             target.getTop(true),
3528             target.getWidth(),
3529             target.getHeight()
3530         );
3531         this.el.dom.style.display = "block";
3532     },
3533
3534     /**
3535      * Returns true if the shadow is visible, else false
3536      */
3537     isVisible : function(){
3538         return this.el ? true : false;  
3539     },
3540
3541     /**
3542      * Direct alignment when values are already available. Show must be called at least once before
3543      * calling this method to ensure it is initialized.
3544      * @param {Number} left The target element left position
3545      * @param {Number} top The target element top position
3546      * @param {Number} width The target element width
3547      * @param {Number} height The target element height
3548      */
3549     realign : function(l, t, w, h){
3550         if(!this.el){
3551             return;
3552         }
3553         var a = this.adjusts, d = this.el.dom, s = d.style;
3554         var iea = 0;
3555         s.left = (l+a.l)+"px";
3556         s.top = (t+a.t)+"px";
3557         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3558  
3559         if(s.width != sws || s.height != shs){
3560             s.width = sws;
3561             s.height = shs;
3562             if(!Roo.isIE){
3563                 var cn = d.childNodes;
3564                 var sww = Math.max(0, (sw-12))+"px";
3565                 cn[0].childNodes[1].style.width = sww;
3566                 cn[1].childNodes[1].style.width = sww;
3567                 cn[2].childNodes[1].style.width = sww;
3568                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3569             }
3570         }
3571     },
3572
3573     /**
3574      * Hides this shadow
3575      */
3576     hide : function(){
3577         if(this.el){
3578             this.el.dom.style.display = "none";
3579             Roo.Shadow.Pool.push(this.el);
3580             delete this.el;
3581         }
3582     },
3583
3584     /**
3585      * Adjust the z-index of this shadow
3586      * @param {Number} zindex The new z-index
3587      */
3588     setZIndex : function(z){
3589         this.zIndex = z;
3590         if(this.el){
3591             this.el.setStyle("z-index", z);
3592         }
3593     }
3594 };
3595
3596 // Private utility class that manages the internal Shadow cache
3597 Roo.Shadow.Pool = function(){
3598     var p = [];
3599     var markup = Roo.isIE ?
3600                  '<div class="x-ie-shadow"></div>' :
3601                  '<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>';
3602     return {
3603         pull : function(){
3604             var sh = p.shift();
3605             if(!sh){
3606                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3607                 sh.autoBoxAdjust = false;
3608             }
3609             return sh;
3610         },
3611
3612         push : function(sh){
3613             p.push(sh);
3614         }
3615     };
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 /**
3629  * @class Roo.SplitBar
3630  * @extends Roo.util.Observable
3631  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3632  * <br><br>
3633  * Usage:
3634  * <pre><code>
3635 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3636                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3637 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3638 split.minSize = 100;
3639 split.maxSize = 600;
3640 split.animate = true;
3641 split.on('moved', splitterMoved);
3642 </code></pre>
3643  * @constructor
3644  * Create a new SplitBar
3645  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3646  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3647  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3648  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3649                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3650                         position of the SplitBar).
3651  */
3652 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3653     
3654     /** @private */
3655     this.el = Roo.get(dragElement, true);
3656     this.el.dom.unselectable = "on";
3657     /** @private */
3658     this.resizingEl = Roo.get(resizingElement, true);
3659
3660     /**
3661      * @private
3662      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3663      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3664      * @type Number
3665      */
3666     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3667     
3668     /**
3669      * The minimum size of the resizing element. (Defaults to 0)
3670      * @type Number
3671      */
3672     this.minSize = 0;
3673     
3674     /**
3675      * The maximum size of the resizing element. (Defaults to 2000)
3676      * @type Number
3677      */
3678     this.maxSize = 2000;
3679     
3680     /**
3681      * Whether to animate the transition to the new size
3682      * @type Boolean
3683      */
3684     this.animate = false;
3685     
3686     /**
3687      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3688      * @type Boolean
3689      */
3690     this.useShim = false;
3691     
3692     /** @private */
3693     this.shim = null;
3694     
3695     if(!existingProxy){
3696         /** @private */
3697         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3698     }else{
3699         this.proxy = Roo.get(existingProxy).dom;
3700     }
3701     /** @private */
3702     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3703     
3704     /** @private */
3705     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3706     
3707     /** @private */
3708     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3709     
3710     /** @private */
3711     this.dragSpecs = {};
3712     
3713     /**
3714      * @private The adapter to use to positon and resize elements
3715      */
3716     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3717     this.adapter.init(this);
3718     
3719     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3720         /** @private */
3721         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3722         this.el.addClass("x-splitbar-h");
3723     }else{
3724         /** @private */
3725         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3726         this.el.addClass("x-splitbar-v");
3727     }
3728     
3729     this.addEvents({
3730         /**
3731          * @event resize
3732          * Fires when the splitter is moved (alias for {@link #event-moved})
3733          * @param {Roo.SplitBar} this
3734          * @param {Number} newSize the new width or height
3735          */
3736         "resize" : true,
3737         /**
3738          * @event moved
3739          * Fires when the splitter is moved
3740          * @param {Roo.SplitBar} this
3741          * @param {Number} newSize the new width or height
3742          */
3743         "moved" : true,
3744         /**
3745          * @event beforeresize
3746          * Fires before the splitter is dragged
3747          * @param {Roo.SplitBar} this
3748          */
3749         "beforeresize" : true,
3750
3751         "beforeapply" : true
3752     });
3753
3754     Roo.util.Observable.call(this);
3755 };
3756
3757 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3758     onStartProxyDrag : function(x, y){
3759         this.fireEvent("beforeresize", this);
3760         if(!this.overlay){
3761             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3762             o.unselectable();
3763             o.enableDisplayMode("block");
3764             // all splitbars share the same overlay
3765             Roo.SplitBar.prototype.overlay = o;
3766         }
3767         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3768         this.overlay.show();
3769         Roo.get(this.proxy).setDisplayed("block");
3770         var size = this.adapter.getElementSize(this);
3771         this.activeMinSize = this.getMinimumSize();;
3772         this.activeMaxSize = this.getMaximumSize();;
3773         var c1 = size - this.activeMinSize;
3774         var c2 = Math.max(this.activeMaxSize - size, 0);
3775         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3776             this.dd.resetConstraints();
3777             this.dd.setXConstraint(
3778                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3779                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3780             );
3781             this.dd.setYConstraint(0, 0);
3782         }else{
3783             this.dd.resetConstraints();
3784             this.dd.setXConstraint(0, 0);
3785             this.dd.setYConstraint(
3786                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3787                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3788             );
3789          }
3790         this.dragSpecs.startSize = size;
3791         this.dragSpecs.startPoint = [x, y];
3792         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3793     },
3794     
3795     /** 
3796      * @private Called after the drag operation by the DDProxy
3797      */
3798     onEndProxyDrag : function(e){
3799         Roo.get(this.proxy).setDisplayed(false);
3800         var endPoint = Roo.lib.Event.getXY(e);
3801         if(this.overlay){
3802             this.overlay.hide();
3803         }
3804         var newSize;
3805         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3806             newSize = this.dragSpecs.startSize + 
3807                 (this.placement == Roo.SplitBar.LEFT ?
3808                     endPoint[0] - this.dragSpecs.startPoint[0] :
3809                     this.dragSpecs.startPoint[0] - endPoint[0]
3810                 );
3811         }else{
3812             newSize = this.dragSpecs.startSize + 
3813                 (this.placement == Roo.SplitBar.TOP ?
3814                     endPoint[1] - this.dragSpecs.startPoint[1] :
3815                     this.dragSpecs.startPoint[1] - endPoint[1]
3816                 );
3817         }
3818         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3819         if(newSize != this.dragSpecs.startSize){
3820             if(this.fireEvent('beforeapply', this, newSize) !== false){
3821                 this.adapter.setElementSize(this, newSize);
3822                 this.fireEvent("moved", this, newSize);
3823                 this.fireEvent("resize", this, newSize);
3824             }
3825         }
3826     },
3827     
3828     /**
3829      * Get the adapter this SplitBar uses
3830      * @return The adapter object
3831      */
3832     getAdapter : function(){
3833         return this.adapter;
3834     },
3835     
3836     /**
3837      * Set the adapter this SplitBar uses
3838      * @param {Object} adapter A SplitBar adapter object
3839      */
3840     setAdapter : function(adapter){
3841         this.adapter = adapter;
3842         this.adapter.init(this);
3843     },
3844     
3845     /**
3846      * Gets the minimum size for the resizing element
3847      * @return {Number} The minimum size
3848      */
3849     getMinimumSize : function(){
3850         return this.minSize;
3851     },
3852     
3853     /**
3854      * Sets the minimum size for the resizing element
3855      * @param {Number} minSize The minimum size
3856      */
3857     setMinimumSize : function(minSize){
3858         this.minSize = minSize;
3859     },
3860     
3861     /**
3862      * Gets the maximum size for the resizing element
3863      * @return {Number} The maximum size
3864      */
3865     getMaximumSize : function(){
3866         return this.maxSize;
3867     },
3868     
3869     /**
3870      * Sets the maximum size for the resizing element
3871      * @param {Number} maxSize The maximum size
3872      */
3873     setMaximumSize : function(maxSize){
3874         this.maxSize = maxSize;
3875     },
3876     
3877     /**
3878      * Sets the initialize size for the resizing element
3879      * @param {Number} size The initial size
3880      */
3881     setCurrentSize : function(size){
3882         var oldAnimate = this.animate;
3883         this.animate = false;
3884         this.adapter.setElementSize(this, size);
3885         this.animate = oldAnimate;
3886     },
3887     
3888     /**
3889      * Destroy this splitbar. 
3890      * @param {Boolean} removeEl True to remove the element
3891      */
3892     destroy : function(removeEl){
3893         if(this.shim){
3894             this.shim.remove();
3895         }
3896         this.dd.unreg();
3897         this.proxy.parentNode.removeChild(this.proxy);
3898         if(removeEl){
3899             this.el.remove();
3900         }
3901     }
3902 });
3903
3904 /**
3905  * @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.
3906  */
3907 Roo.SplitBar.createProxy = function(dir){
3908     var proxy = new Roo.Element(document.createElement("div"));
3909     proxy.unselectable();
3910     var cls = 'x-splitbar-proxy';
3911     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3912     document.body.appendChild(proxy.dom);
3913     return proxy.dom;
3914 };
3915
3916 /** 
3917  * @class Roo.SplitBar.BasicLayoutAdapter
3918  * Default Adapter. It assumes the splitter and resizing element are not positioned
3919  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3920  */
3921 Roo.SplitBar.BasicLayoutAdapter = function(){
3922 };
3923
3924 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3925     // do nothing for now
3926     init : function(s){
3927     
3928     },
3929     /**
3930      * Called before drag operations to get the current size of the resizing element. 
3931      * @param {Roo.SplitBar} s The SplitBar using this adapter
3932      */
3933      getElementSize : function(s){
3934         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3935             return s.resizingEl.getWidth();
3936         }else{
3937             return s.resizingEl.getHeight();
3938         }
3939     },
3940     
3941     /**
3942      * Called after drag operations to set the size of the resizing element.
3943      * @param {Roo.SplitBar} s The SplitBar using this adapter
3944      * @param {Number} newSize The new size to set
3945      * @param {Function} onComplete A function to be invoked when resizing is complete
3946      */
3947     setElementSize : function(s, newSize, onComplete){
3948         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3949             if(!s.animate){
3950                 s.resizingEl.setWidth(newSize);
3951                 if(onComplete){
3952                     onComplete(s, newSize);
3953                 }
3954             }else{
3955                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3956             }
3957         }else{
3958             
3959             if(!s.animate){
3960                 s.resizingEl.setHeight(newSize);
3961                 if(onComplete){
3962                     onComplete(s, newSize);
3963                 }
3964             }else{
3965                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3966             }
3967         }
3968     }
3969 };
3970
3971 /** 
3972  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3973  * @extends Roo.SplitBar.BasicLayoutAdapter
3974  * Adapter that  moves the splitter element to align with the resized sizing element. 
3975  * Used with an absolute positioned SplitBar.
3976  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3977  * document.body, make sure you assign an id to the body element.
3978  */
3979 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3980     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3981     this.container = Roo.get(container);
3982 };
3983
3984 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3985     init : function(s){
3986         this.basic.init(s);
3987     },
3988     
3989     getElementSize : function(s){
3990         return this.basic.getElementSize(s);
3991     },
3992     
3993     setElementSize : function(s, newSize, onComplete){
3994         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3995     },
3996     
3997     moveSplitter : function(s){
3998         var yes = Roo.SplitBar;
3999         switch(s.placement){
4000             case yes.LEFT:
4001                 s.el.setX(s.resizingEl.getRight());
4002                 break;
4003             case yes.RIGHT:
4004                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4005                 break;
4006             case yes.TOP:
4007                 s.el.setY(s.resizingEl.getBottom());
4008                 break;
4009             case yes.BOTTOM:
4010                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4011                 break;
4012         }
4013     }
4014 };
4015
4016 /**
4017  * Orientation constant - Create a vertical SplitBar
4018  * @static
4019  * @type Number
4020  */
4021 Roo.SplitBar.VERTICAL = 1;
4022
4023 /**
4024  * Orientation constant - Create a horizontal SplitBar
4025  * @static
4026  * @type Number
4027  */
4028 Roo.SplitBar.HORIZONTAL = 2;
4029
4030 /**
4031  * Placement constant - The resizing element is to the left of the splitter element
4032  * @static
4033  * @type Number
4034  */
4035 Roo.SplitBar.LEFT = 1;
4036
4037 /**
4038  * Placement constant - The resizing element is to the right of the splitter element
4039  * @static
4040  * @type Number
4041  */
4042 Roo.SplitBar.RIGHT = 2;
4043
4044 /**
4045  * Placement constant - The resizing element is positioned above the splitter element
4046  * @static
4047  * @type Number
4048  */
4049 Roo.SplitBar.TOP = 3;
4050
4051 /**
4052  * Placement constant - The resizing element is positioned under splitter element
4053  * @static
4054  * @type Number
4055  */
4056 Roo.SplitBar.BOTTOM = 4;
4057 /*
4058  * Based on:
4059  * Ext JS Library 1.1.1
4060  * Copyright(c) 2006-2007, Ext JS, LLC.
4061  *
4062  * Originally Released Under LGPL - original licence link has changed is not relivant.
4063  *
4064  * Fork - LGPL
4065  * <script type="text/javascript">
4066  */
4067
4068 /**
4069  * @class Roo.View
4070  * @extends Roo.util.Observable
4071  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4072  * This class also supports single and multi selection modes. <br>
4073  * Create a data model bound view:
4074  <pre><code>
4075  var store = new Roo.data.Store(...);
4076
4077  var view = new Roo.View({
4078     el : "my-element",
4079     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4080  
4081     singleSelect: true,
4082     selectedClass: "ydataview-selected",
4083     store: store
4084  });
4085
4086  // listen for node click?
4087  view.on("click", function(vw, index, node, e){
4088  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4089  });
4090
4091  // load XML data
4092  dataModel.load("foobar.xml");
4093  </code></pre>
4094  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4095  * <br><br>
4096  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4097  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4098  * 
4099  * Note: old style constructor is still suported (container, template, config)
4100  * 
4101  * @constructor
4102  * Create a new View
4103  * @param {Object} config The config object
4104  * 
4105  */
4106 Roo.View = function(config, depreciated_tpl, depreciated_config){
4107     
4108     this.parent = false;
4109     
4110     if (typeof(depreciated_tpl) == 'undefined') {
4111         // new way.. - universal constructor.
4112         Roo.apply(this, config);
4113         this.el  = Roo.get(this.el);
4114     } else {
4115         // old format..
4116         this.el  = Roo.get(config);
4117         this.tpl = depreciated_tpl;
4118         Roo.apply(this, depreciated_config);
4119     }
4120     this.wrapEl  = this.el.wrap().wrap();
4121     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4122     
4123     
4124     if(typeof(this.tpl) == "string"){
4125         this.tpl = new Roo.Template(this.tpl);
4126     } else {
4127         // support xtype ctors..
4128         this.tpl = new Roo.factory(this.tpl, Roo);
4129     }
4130     
4131     
4132     this.tpl.compile();
4133     
4134     /** @private */
4135     this.addEvents({
4136         /**
4137          * @event beforeclick
4138          * Fires before a click is processed. Returns false to cancel the default action.
4139          * @param {Roo.View} this
4140          * @param {Number} index The index of the target node
4141          * @param {HTMLElement} node The target node
4142          * @param {Roo.EventObject} e The raw event object
4143          */
4144             "beforeclick" : true,
4145         /**
4146          * @event click
4147          * Fires when a template node is clicked.
4148          * @param {Roo.View} this
4149          * @param {Number} index The index of the target node
4150          * @param {HTMLElement} node The target node
4151          * @param {Roo.EventObject} e The raw event object
4152          */
4153             "click" : true,
4154         /**
4155          * @event dblclick
4156          * Fires when a template node is double clicked.
4157          * @param {Roo.View} this
4158          * @param {Number} index The index of the target node
4159          * @param {HTMLElement} node The target node
4160          * @param {Roo.EventObject} e The raw event object
4161          */
4162             "dblclick" : true,
4163         /**
4164          * @event contextmenu
4165          * Fires when a template node is right clicked.
4166          * @param {Roo.View} this
4167          * @param {Number} index The index of the target node
4168          * @param {HTMLElement} node The target node
4169          * @param {Roo.EventObject} e The raw event object
4170          */
4171             "contextmenu" : true,
4172         /**
4173          * @event selectionchange
4174          * Fires when the selected nodes change.
4175          * @param {Roo.View} this
4176          * @param {Array} selections Array of the selected nodes
4177          */
4178             "selectionchange" : true,
4179     
4180         /**
4181          * @event beforeselect
4182          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4183          * @param {Roo.View} this
4184          * @param {HTMLElement} node The node to be selected
4185          * @param {Array} selections Array of currently selected nodes
4186          */
4187             "beforeselect" : true,
4188         /**
4189          * @event preparedata
4190          * Fires on every row to render, to allow you to change the data.
4191          * @param {Roo.View} this
4192          * @param {Object} data to be rendered (change this)
4193          */
4194           "preparedata" : true
4195           
4196           
4197         });
4198
4199
4200
4201     this.el.on({
4202         "click": this.onClick,
4203         "dblclick": this.onDblClick,
4204         "contextmenu": this.onContextMenu,
4205         scope:this
4206     });
4207
4208     this.selections = [];
4209     this.nodes = [];
4210     this.cmp = new Roo.CompositeElementLite([]);
4211     if(this.store){
4212         this.store = Roo.factory(this.store, Roo.data);
4213         this.setStore(this.store, true);
4214     }
4215     
4216     if ( this.footer && this.footer.xtype) {
4217            
4218          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4219         
4220         this.footer.dataSource = this.store;
4221         this.footer.container = fctr;
4222         this.footer = Roo.factory(this.footer, Roo);
4223         fctr.insertFirst(this.el);
4224         
4225         // this is a bit insane - as the paging toolbar seems to detach the el..
4226 //        dom.parentNode.parentNode.parentNode
4227          // they get detached?
4228     }
4229     
4230     
4231     Roo.View.superclass.constructor.call(this);
4232     
4233     
4234 };
4235
4236 Roo.extend(Roo.View, Roo.util.Observable, {
4237     
4238      /**
4239      * @cfg {Roo.data.Store} store Data store to load data from.
4240      */
4241     store : false,
4242     
4243     /**
4244      * @cfg {String|Roo.Element} el The container element.
4245      */
4246     el : '',
4247     
4248     /**
4249      * @cfg {String|Roo.Template} tpl The template used by this View 
4250      */
4251     tpl : false,
4252     /**
4253      * @cfg {String} dataName the named area of the template to use as the data area
4254      *                          Works with domtemplates roo-name="name"
4255      */
4256     dataName: false,
4257     /**
4258      * @cfg {String} selectedClass The css class to add to selected nodes
4259      */
4260     selectedClass : "x-view-selected",
4261      /**
4262      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4263      */
4264     emptyText : "",
4265     
4266     /**
4267      * @cfg {String} text to display on mask (default Loading)
4268      */
4269     mask : false,
4270     /**
4271      * @cfg {Boolean} multiSelect Allow multiple selection
4272      */
4273     multiSelect : false,
4274     /**
4275      * @cfg {Boolean} singleSelect Allow single selection
4276      */
4277     singleSelect:  false,
4278     
4279     /**
4280      * @cfg {Boolean} toggleSelect - selecting 
4281      */
4282     toggleSelect : false,
4283     
4284     /**
4285      * @cfg {Boolean} tickable - selecting 
4286      */
4287     tickable : false,
4288     
4289     /**
4290      * Returns the element this view is bound to.
4291      * @return {Roo.Element}
4292      */
4293     getEl : function(){
4294         return this.wrapEl;
4295     },
4296     
4297     
4298
4299     /**
4300      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4301      */
4302     refresh : function(){
4303         //Roo.log('refresh');
4304         var t = this.tpl;
4305         
4306         // if we are using something like 'domtemplate', then
4307         // the what gets used is:
4308         // t.applySubtemplate(NAME, data, wrapping data..)
4309         // the outer template then get' applied with
4310         //     the store 'extra data'
4311         // and the body get's added to the
4312         //      roo-name="data" node?
4313         //      <span class='roo-tpl-{name}'></span> ?????
4314         
4315         
4316         
4317         this.clearSelections();
4318         this.el.update("");
4319         var html = [];
4320         var records = this.store.getRange();
4321         if(records.length < 1) {
4322             
4323             // is this valid??  = should it render a template??
4324             
4325             this.el.update(this.emptyText);
4326             return;
4327         }
4328         var el = this.el;
4329         if (this.dataName) {
4330             this.el.update(t.apply(this.store.meta)); //????
4331             el = this.el.child('.roo-tpl-' + this.dataName);
4332         }
4333         
4334         for(var i = 0, len = records.length; i < len; i++){
4335             var data = this.prepareData(records[i].data, i, records[i]);
4336             this.fireEvent("preparedata", this, data, i, records[i]);
4337             
4338             var d = Roo.apply({}, data);
4339             
4340             if(this.tickable){
4341                 Roo.apply(d, {'roo-id' : Roo.id()});
4342                 
4343                 var _this = this;
4344             
4345                 Roo.each(this.parent.item, function(item){
4346                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4347                         return;
4348                     }
4349                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4350                 });
4351             }
4352             
4353             html[html.length] = Roo.util.Format.trim(
4354                 this.dataName ?
4355                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4356                     t.apply(d)
4357             );
4358         }
4359         
4360         
4361         
4362         el.update(html.join(""));
4363         this.nodes = el.dom.childNodes;
4364         this.updateIndexes(0);
4365     },
4366     
4367
4368     /**
4369      * Function to override to reformat the data that is sent to
4370      * the template for each node.
4371      * DEPRICATED - use the preparedata event handler.
4372      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4373      * a JSON object for an UpdateManager bound view).
4374      */
4375     prepareData : function(data, index, record)
4376     {
4377         this.fireEvent("preparedata", this, data, index, record);
4378         return data;
4379     },
4380
4381     onUpdate : function(ds, record){
4382         // Roo.log('on update');   
4383         this.clearSelections();
4384         var index = this.store.indexOf(record);
4385         var n = this.nodes[index];
4386         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4387         n.parentNode.removeChild(n);
4388         this.updateIndexes(index, index);
4389     },
4390
4391     
4392     
4393 // --------- FIXME     
4394     onAdd : function(ds, records, index)
4395     {
4396         //Roo.log(['on Add', ds, records, index] );        
4397         this.clearSelections();
4398         if(this.nodes.length == 0){
4399             this.refresh();
4400             return;
4401         }
4402         var n = this.nodes[index];
4403         for(var i = 0, len = records.length; i < len; i++){
4404             var d = this.prepareData(records[i].data, i, records[i]);
4405             if(n){
4406                 this.tpl.insertBefore(n, d);
4407             }else{
4408                 
4409                 this.tpl.append(this.el, d);
4410             }
4411         }
4412         this.updateIndexes(index);
4413     },
4414
4415     onRemove : function(ds, record, index){
4416        // Roo.log('onRemove');
4417         this.clearSelections();
4418         var el = this.dataName  ?
4419             this.el.child('.roo-tpl-' + this.dataName) :
4420             this.el; 
4421         
4422         el.dom.removeChild(this.nodes[index]);
4423         this.updateIndexes(index);
4424     },
4425
4426     /**
4427      * Refresh an individual node.
4428      * @param {Number} index
4429      */
4430     refreshNode : function(index){
4431         this.onUpdate(this.store, this.store.getAt(index));
4432     },
4433
4434     updateIndexes : function(startIndex, endIndex){
4435         var ns = this.nodes;
4436         startIndex = startIndex || 0;
4437         endIndex = endIndex || ns.length - 1;
4438         for(var i = startIndex; i <= endIndex; i++){
4439             ns[i].nodeIndex = i;
4440         }
4441     },
4442
4443     /**
4444      * Changes the data store this view uses and refresh the view.
4445      * @param {Store} store
4446      */
4447     setStore : function(store, initial){
4448         if(!initial && this.store){
4449             this.store.un("datachanged", this.refresh);
4450             this.store.un("add", this.onAdd);
4451             this.store.un("remove", this.onRemove);
4452             this.store.un("update", this.onUpdate);
4453             this.store.un("clear", this.refresh);
4454             this.store.un("beforeload", this.onBeforeLoad);
4455             this.store.un("load", this.onLoad);
4456             this.store.un("loadexception", this.onLoad);
4457         }
4458         if(store){
4459           
4460             store.on("datachanged", this.refresh, this);
4461             store.on("add", this.onAdd, this);
4462             store.on("remove", this.onRemove, this);
4463             store.on("update", this.onUpdate, this);
4464             store.on("clear", this.refresh, this);
4465             store.on("beforeload", this.onBeforeLoad, this);
4466             store.on("load", this.onLoad, this);
4467             store.on("loadexception", this.onLoad, this);
4468         }
4469         
4470         if(store){
4471             this.refresh();
4472         }
4473     },
4474     /**
4475      * onbeforeLoad - masks the loading area.
4476      *
4477      */
4478     onBeforeLoad : function(store,opts)
4479     {
4480          //Roo.log('onBeforeLoad');   
4481         if (!opts.add) {
4482             this.el.update("");
4483         }
4484         this.el.mask(this.mask ? this.mask : "Loading" ); 
4485     },
4486     onLoad : function ()
4487     {
4488         this.el.unmask();
4489     },
4490     
4491
4492     /**
4493      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4494      * @param {HTMLElement} node
4495      * @return {HTMLElement} The template node
4496      */
4497     findItemFromChild : function(node){
4498         var el = this.dataName  ?
4499             this.el.child('.roo-tpl-' + this.dataName,true) :
4500             this.el.dom; 
4501         
4502         if(!node || node.parentNode == el){
4503                     return node;
4504             }
4505             var p = node.parentNode;
4506             while(p && p != el){
4507             if(p.parentNode == el){
4508                 return p;
4509             }
4510             p = p.parentNode;
4511         }
4512             return null;
4513     },
4514
4515     /** @ignore */
4516     onClick : function(e){
4517         var item = this.findItemFromChild(e.getTarget());
4518         if(item){
4519             var index = this.indexOf(item);
4520             if(this.onItemClick(item, index, e) !== false){
4521                 this.fireEvent("click", this, index, item, e);
4522             }
4523         }else{
4524             this.clearSelections();
4525         }
4526     },
4527
4528     /** @ignore */
4529     onContextMenu : function(e){
4530         var item = this.findItemFromChild(e.getTarget());
4531         if(item){
4532             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4533         }
4534     },
4535
4536     /** @ignore */
4537     onDblClick : function(e){
4538         var item = this.findItemFromChild(e.getTarget());
4539         if(item){
4540             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4541         }
4542     },
4543
4544     onItemClick : function(item, index, e)
4545     {
4546         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4547             return false;
4548         }
4549         if (this.toggleSelect) {
4550             var m = this.isSelected(item) ? 'unselect' : 'select';
4551             //Roo.log(m);
4552             var _t = this;
4553             _t[m](item, true, false);
4554             return true;
4555         }
4556         if(this.multiSelect || this.singleSelect){
4557             if(this.multiSelect && e.shiftKey && this.lastSelection){
4558                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4559             }else{
4560                 this.select(item, this.multiSelect && e.ctrlKey);
4561                 this.lastSelection = item;
4562             }
4563             
4564             if(!this.tickable){
4565                 e.preventDefault();
4566             }
4567             
4568         }
4569         return true;
4570     },
4571
4572     /**
4573      * Get the number of selected nodes.
4574      * @return {Number}
4575      */
4576     getSelectionCount : function(){
4577         return this.selections.length;
4578     },
4579
4580     /**
4581      * Get the currently selected nodes.
4582      * @return {Array} An array of HTMLElements
4583      */
4584     getSelectedNodes : function(){
4585         return this.selections;
4586     },
4587
4588     /**
4589      * Get the indexes of the selected nodes.
4590      * @return {Array}
4591      */
4592     getSelectedIndexes : function(){
4593         var indexes = [], s = this.selections;
4594         for(var i = 0, len = s.length; i < len; i++){
4595             indexes.push(s[i].nodeIndex);
4596         }
4597         return indexes;
4598     },
4599
4600     /**
4601      * Clear all selections
4602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4603      */
4604     clearSelections : function(suppressEvent){
4605         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4606             this.cmp.elements = this.selections;
4607             this.cmp.removeClass(this.selectedClass);
4608             this.selections = [];
4609             if(!suppressEvent){
4610                 this.fireEvent("selectionchange", this, this.selections);
4611             }
4612         }
4613     },
4614
4615     /**
4616      * Returns true if the passed node is selected
4617      * @param {HTMLElement/Number} node The node or node index
4618      * @return {Boolean}
4619      */
4620     isSelected : function(node){
4621         var s = this.selections;
4622         if(s.length < 1){
4623             return false;
4624         }
4625         node = this.getNode(node);
4626         return s.indexOf(node) !== -1;
4627     },
4628
4629     /**
4630      * Selects nodes.
4631      * @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
4632      * @param {Boolean} keepExisting (optional) true to keep existing selections
4633      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4634      */
4635     select : function(nodeInfo, keepExisting, suppressEvent){
4636         if(nodeInfo instanceof Array){
4637             if(!keepExisting){
4638                 this.clearSelections(true);
4639             }
4640             for(var i = 0, len = nodeInfo.length; i < len; i++){
4641                 this.select(nodeInfo[i], true, true);
4642             }
4643             return;
4644         } 
4645         var node = this.getNode(nodeInfo);
4646         if(!node || this.isSelected(node)){
4647             return; // already selected.
4648         }
4649         if(!keepExisting){
4650             this.clearSelections(true);
4651         }
4652         
4653         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4654             Roo.fly(node).addClass(this.selectedClass);
4655             this.selections.push(node);
4656             if(!suppressEvent){
4657                 this.fireEvent("selectionchange", this, this.selections);
4658             }
4659         }
4660         
4661         
4662     },
4663       /**
4664      * Unselects nodes.
4665      * @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
4666      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4667      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4668      */
4669     unselect : function(nodeInfo, keepExisting, suppressEvent)
4670     {
4671         if(nodeInfo instanceof Array){
4672             Roo.each(this.selections, function(s) {
4673                 this.unselect(s, nodeInfo);
4674             }, this);
4675             return;
4676         }
4677         var node = this.getNode(nodeInfo);
4678         if(!node || !this.isSelected(node)){
4679             //Roo.log("not selected");
4680             return; // not selected.
4681         }
4682         // fireevent???
4683         var ns = [];
4684         Roo.each(this.selections, function(s) {
4685             if (s == node ) {
4686                 Roo.fly(node).removeClass(this.selectedClass);
4687
4688                 return;
4689             }
4690             ns.push(s);
4691         },this);
4692         
4693         this.selections= ns;
4694         this.fireEvent("selectionchange", this, this.selections);
4695     },
4696
4697     /**
4698      * Gets a template node.
4699      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4700      * @return {HTMLElement} The node or null if it wasn't found
4701      */
4702     getNode : function(nodeInfo){
4703         if(typeof nodeInfo == "string"){
4704             return document.getElementById(nodeInfo);
4705         }else if(typeof nodeInfo == "number"){
4706             return this.nodes[nodeInfo];
4707         }
4708         return nodeInfo;
4709     },
4710
4711     /**
4712      * Gets a range template nodes.
4713      * @param {Number} startIndex
4714      * @param {Number} endIndex
4715      * @return {Array} An array of nodes
4716      */
4717     getNodes : function(start, end){
4718         var ns = this.nodes;
4719         start = start || 0;
4720         end = typeof end == "undefined" ? ns.length - 1 : end;
4721         var nodes = [];
4722         if(start <= end){
4723             for(var i = start; i <= end; i++){
4724                 nodes.push(ns[i]);
4725             }
4726         } else{
4727             for(var i = start; i >= end; i--){
4728                 nodes.push(ns[i]);
4729             }
4730         }
4731         return nodes;
4732     },
4733
4734     /**
4735      * Finds the index of the passed node
4736      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4737      * @return {Number} The index of the node or -1
4738      */
4739     indexOf : function(node){
4740         node = this.getNode(node);
4741         if(typeof node.nodeIndex == "number"){
4742             return node.nodeIndex;
4743         }
4744         var ns = this.nodes;
4745         for(var i = 0, len = ns.length; i < len; i++){
4746             if(ns[i] == node){
4747                 return i;
4748             }
4749         }
4750         return -1;
4751     }
4752 });
4753 /*
4754  * Based on:
4755  * Ext JS Library 1.1.1
4756  * Copyright(c) 2006-2007, Ext JS, LLC.
4757  *
4758  * Originally Released Under LGPL - original licence link has changed is not relivant.
4759  *
4760  * Fork - LGPL
4761  * <script type="text/javascript">
4762  */
4763
4764 /**
4765  * @class Roo.JsonView
4766  * @extends Roo.View
4767  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4768 <pre><code>
4769 var view = new Roo.JsonView({
4770     container: "my-element",
4771     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4772     multiSelect: true, 
4773     jsonRoot: "data" 
4774 });
4775
4776 // listen for node click?
4777 view.on("click", function(vw, index, node, e){
4778     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4779 });
4780
4781 // direct load of JSON data
4782 view.load("foobar.php");
4783
4784 // Example from my blog list
4785 var tpl = new Roo.Template(
4786     '&lt;div class="entry"&gt;' +
4787     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4788     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4789     "&lt;/div&gt;&lt;hr /&gt;"
4790 );
4791
4792 var moreView = new Roo.JsonView({
4793     container :  "entry-list", 
4794     template : tpl,
4795     jsonRoot: "posts"
4796 });
4797 moreView.on("beforerender", this.sortEntries, this);
4798 moreView.load({
4799     url: "/blog/get-posts.php",
4800     params: "allposts=true",
4801     text: "Loading Blog Entries..."
4802 });
4803 </code></pre>
4804
4805 * Note: old code is supported with arguments : (container, template, config)
4806
4807
4808  * @constructor
4809  * Create a new JsonView
4810  * 
4811  * @param {Object} config The config object
4812  * 
4813  */
4814 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4815     
4816     
4817     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4818
4819     var um = this.el.getUpdateManager();
4820     um.setRenderer(this);
4821     um.on("update", this.onLoad, this);
4822     um.on("failure", this.onLoadException, this);
4823
4824     /**
4825      * @event beforerender
4826      * Fires before rendering of the downloaded JSON data.
4827      * @param {Roo.JsonView} this
4828      * @param {Object} data The JSON data loaded
4829      */
4830     /**
4831      * @event load
4832      * Fires when data is loaded.
4833      * @param {Roo.JsonView} this
4834      * @param {Object} data The JSON data loaded
4835      * @param {Object} response The raw Connect response object
4836      */
4837     /**
4838      * @event loadexception
4839      * Fires when loading fails.
4840      * @param {Roo.JsonView} this
4841      * @param {Object} response The raw Connect response object
4842      */
4843     this.addEvents({
4844         'beforerender' : true,
4845         'load' : true,
4846         'loadexception' : true
4847     });
4848 };
4849 Roo.extend(Roo.JsonView, Roo.View, {
4850     /**
4851      * @type {String} The root property in the loaded JSON object that contains the data
4852      */
4853     jsonRoot : "",
4854
4855     /**
4856      * Refreshes the view.
4857      */
4858     refresh : function(){
4859         this.clearSelections();
4860         this.el.update("");
4861         var html = [];
4862         var o = this.jsonData;
4863         if(o && o.length > 0){
4864             for(var i = 0, len = o.length; i < len; i++){
4865                 var data = this.prepareData(o[i], i, o);
4866                 html[html.length] = this.tpl.apply(data);
4867             }
4868         }else{
4869             html.push(this.emptyText);
4870         }
4871         this.el.update(html.join(""));
4872         this.nodes = this.el.dom.childNodes;
4873         this.updateIndexes(0);
4874     },
4875
4876     /**
4877      * 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.
4878      * @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:
4879      <pre><code>
4880      view.load({
4881          url: "your-url.php",
4882          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4883          callback: yourFunction,
4884          scope: yourObject, //(optional scope)
4885          discardUrl: false,
4886          nocache: false,
4887          text: "Loading...",
4888          timeout: 30,
4889          scripts: false
4890      });
4891      </code></pre>
4892      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4893      * 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.
4894      * @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}
4895      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4896      * @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.
4897      */
4898     load : function(){
4899         var um = this.el.getUpdateManager();
4900         um.update.apply(um, arguments);
4901     },
4902
4903     // note - render is a standard framework call...
4904     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4905     render : function(el, response){
4906         
4907         this.clearSelections();
4908         this.el.update("");
4909         var o;
4910         try{
4911             if (response != '') {
4912                 o = Roo.util.JSON.decode(response.responseText);
4913                 if(this.jsonRoot){
4914                     
4915                     o = o[this.jsonRoot];
4916                 }
4917             }
4918         } catch(e){
4919         }
4920         /**
4921          * The current JSON data or null
4922          */
4923         this.jsonData = o;
4924         this.beforeRender();
4925         this.refresh();
4926     },
4927
4928 /**
4929  * Get the number of records in the current JSON dataset
4930  * @return {Number}
4931  */
4932     getCount : function(){
4933         return this.jsonData ? this.jsonData.length : 0;
4934     },
4935
4936 /**
4937  * Returns the JSON object for the specified node(s)
4938  * @param {HTMLElement/Array} node The node or an array of nodes
4939  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4940  * you get the JSON object for the node
4941  */
4942     getNodeData : function(node){
4943         if(node instanceof Array){
4944             var data = [];
4945             for(var i = 0, len = node.length; i < len; i++){
4946                 data.push(this.getNodeData(node[i]));
4947             }
4948             return data;
4949         }
4950         return this.jsonData[this.indexOf(node)] || null;
4951     },
4952
4953     beforeRender : function(){
4954         this.snapshot = this.jsonData;
4955         if(this.sortInfo){
4956             this.sort.apply(this, this.sortInfo);
4957         }
4958         this.fireEvent("beforerender", this, this.jsonData);
4959     },
4960
4961     onLoad : function(el, o){
4962         this.fireEvent("load", this, this.jsonData, o);
4963     },
4964
4965     onLoadException : function(el, o){
4966         this.fireEvent("loadexception", this, o);
4967     },
4968
4969 /**
4970  * Filter the data by a specific property.
4971  * @param {String} property A property on your JSON objects
4972  * @param {String/RegExp} value Either string that the property values
4973  * should start with, or a RegExp to test against the property
4974  */
4975     filter : function(property, value){
4976         if(this.jsonData){
4977             var data = [];
4978             var ss = this.snapshot;
4979             if(typeof value == "string"){
4980                 var vlen = value.length;
4981                 if(vlen == 0){
4982                     this.clearFilter();
4983                     return;
4984                 }
4985                 value = value.toLowerCase();
4986                 for(var i = 0, len = ss.length; i < len; i++){
4987                     var o = ss[i];
4988                     if(o[property].substr(0, vlen).toLowerCase() == value){
4989                         data.push(o);
4990                     }
4991                 }
4992             } else if(value.exec){ // regex?
4993                 for(var i = 0, len = ss.length; i < len; i++){
4994                     var o = ss[i];
4995                     if(value.test(o[property])){
4996                         data.push(o);
4997                     }
4998                 }
4999             } else{
5000                 return;
5001             }
5002             this.jsonData = data;
5003             this.refresh();
5004         }
5005     },
5006
5007 /**
5008  * Filter by a function. The passed function will be called with each
5009  * object in the current dataset. If the function returns true the value is kept,
5010  * otherwise it is filtered.
5011  * @param {Function} fn
5012  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5013  */
5014     filterBy : function(fn, scope){
5015         if(this.jsonData){
5016             var data = [];
5017             var ss = this.snapshot;
5018             for(var i = 0, len = ss.length; i < len; i++){
5019                 var o = ss[i];
5020                 if(fn.call(scope || this, o)){
5021                     data.push(o);
5022                 }
5023             }
5024             this.jsonData = data;
5025             this.refresh();
5026         }
5027     },
5028
5029 /**
5030  * Clears the current filter.
5031  */
5032     clearFilter : function(){
5033         if(this.snapshot && this.jsonData != this.snapshot){
5034             this.jsonData = this.snapshot;
5035             this.refresh();
5036         }
5037     },
5038
5039
5040 /**
5041  * Sorts the data for this view and refreshes it.
5042  * @param {String} property A property on your JSON objects to sort on
5043  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5044  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5045  */
5046     sort : function(property, dir, sortType){
5047         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5048         if(this.jsonData){
5049             var p = property;
5050             var dsc = dir && dir.toLowerCase() == "desc";
5051             var f = function(o1, o2){
5052                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5053                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5054                 ;
5055                 if(v1 < v2){
5056                     return dsc ? +1 : -1;
5057                 } else if(v1 > v2){
5058                     return dsc ? -1 : +1;
5059                 } else{
5060                     return 0;
5061                 }
5062             };
5063             this.jsonData.sort(f);
5064             this.refresh();
5065             if(this.jsonData != this.snapshot){
5066                 this.snapshot.sort(f);
5067             }
5068         }
5069     }
5070 });/*
5071  * Based on:
5072  * Ext JS Library 1.1.1
5073  * Copyright(c) 2006-2007, Ext JS, LLC.
5074  *
5075  * Originally Released Under LGPL - original licence link has changed is not relivant.
5076  *
5077  * Fork - LGPL
5078  * <script type="text/javascript">
5079  */
5080  
5081
5082 /**
5083  * @class Roo.ColorPalette
5084  * @extends Roo.Component
5085  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5086  * Here's an example of typical usage:
5087  * <pre><code>
5088 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5089 cp.render('my-div');
5090
5091 cp.on('select', function(palette, selColor){
5092     // do something with selColor
5093 });
5094 </code></pre>
5095  * @constructor
5096  * Create a new ColorPalette
5097  * @param {Object} config The config object
5098  */
5099 Roo.ColorPalette = function(config){
5100     Roo.ColorPalette.superclass.constructor.call(this, config);
5101     this.addEvents({
5102         /**
5103              * @event select
5104              * Fires when a color is selected
5105              * @param {ColorPalette} this
5106              * @param {String} color The 6-digit color hex code (without the # symbol)
5107              */
5108         select: true
5109     });
5110
5111     if(this.handler){
5112         this.on("select", this.handler, this.scope, true);
5113     }
5114 };
5115 Roo.extend(Roo.ColorPalette, Roo.Component, {
5116     /**
5117      * @cfg {String} itemCls
5118      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5119      */
5120     itemCls : "x-color-palette",
5121     /**
5122      * @cfg {String} value
5123      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5124      * the hex codes are case-sensitive.
5125      */
5126     value : null,
5127     clickEvent:'click',
5128     // private
5129     ctype: "Roo.ColorPalette",
5130
5131     /**
5132      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5133      */
5134     allowReselect : false,
5135
5136     /**
5137      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5138      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5139      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5140      * of colors with the width setting until the box is symmetrical.</p>
5141      * <p>You can override individual colors if needed:</p>
5142      * <pre><code>
5143 var cp = new Roo.ColorPalette();
5144 cp.colors[0] = "FF0000";  // change the first box to red
5145 </code></pre>
5146
5147 Or you can provide a custom array of your own for complete control:
5148 <pre><code>
5149 var cp = new Roo.ColorPalette();
5150 cp.colors = ["000000", "993300", "333300"];
5151 </code></pre>
5152      * @type Array
5153      */
5154     colors : [
5155         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5156         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5157         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5158         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5159         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5160     ],
5161
5162     // private
5163     onRender : function(container, position){
5164         var t = new Roo.MasterTemplate(
5165             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5166         );
5167         var c = this.colors;
5168         for(var i = 0, len = c.length; i < len; i++){
5169             t.add([c[i]]);
5170         }
5171         var el = document.createElement("div");
5172         el.className = this.itemCls;
5173         t.overwrite(el);
5174         container.dom.insertBefore(el, position);
5175         this.el = Roo.get(el);
5176         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5177         if(this.clickEvent != 'click'){
5178             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5179         }
5180     },
5181
5182     // private
5183     afterRender : function(){
5184         Roo.ColorPalette.superclass.afterRender.call(this);
5185         if(this.value){
5186             var s = this.value;
5187             this.value = null;
5188             this.select(s);
5189         }
5190     },
5191
5192     // private
5193     handleClick : function(e, t){
5194         e.preventDefault();
5195         if(!this.disabled){
5196             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5197             this.select(c.toUpperCase());
5198         }
5199     },
5200
5201     /**
5202      * Selects the specified color in the palette (fires the select event)
5203      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5204      */
5205     select : function(color){
5206         color = color.replace("#", "");
5207         if(color != this.value || this.allowReselect){
5208             var el = this.el;
5209             if(this.value){
5210                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5211             }
5212             el.child("a.color-"+color).addClass("x-color-palette-sel");
5213             this.value = color;
5214             this.fireEvent("select", this, color);
5215         }
5216     }
5217 });/*
5218  * Based on:
5219  * Ext JS Library 1.1.1
5220  * Copyright(c) 2006-2007, Ext JS, LLC.
5221  *
5222  * Originally Released Under LGPL - original licence link has changed is not relivant.
5223  *
5224  * Fork - LGPL
5225  * <script type="text/javascript">
5226  */
5227  
5228 /**
5229  * @class Roo.DatePicker
5230  * @extends Roo.Component
5231  * Simple date picker class.
5232  * @constructor
5233  * Create a new DatePicker
5234  * @param {Object} config The config object
5235  */
5236 Roo.DatePicker = function(config){
5237     Roo.DatePicker.superclass.constructor.call(this, config);
5238
5239     this.value = config && config.value ?
5240                  config.value.clearTime() : new Date().clearTime();
5241
5242     this.addEvents({
5243         /**
5244              * @event select
5245              * Fires when a date is selected
5246              * @param {DatePicker} this
5247              * @param {Date} date The selected date
5248              */
5249         'select': true,
5250         /**
5251              * @event monthchange
5252              * Fires when the displayed month changes 
5253              * @param {DatePicker} this
5254              * @param {Date} date The selected month
5255              */
5256         'monthchange': true
5257     });
5258
5259     if(this.handler){
5260         this.on("select", this.handler,  this.scope || this);
5261     }
5262     // build the disabledDatesRE
5263     if(!this.disabledDatesRE && this.disabledDates){
5264         var dd = this.disabledDates;
5265         var re = "(?:";
5266         for(var i = 0; i < dd.length; i++){
5267             re += dd[i];
5268             if(i != dd.length-1) {
5269                 re += "|";
5270             }
5271         }
5272         this.disabledDatesRE = new RegExp(re + ")");
5273     }
5274 };
5275
5276 Roo.extend(Roo.DatePicker, Roo.Component, {
5277     /**
5278      * @cfg {String} todayText
5279      * The text to display on the button that selects the current date (defaults to "Today")
5280      */
5281     todayText : "Today",
5282     /**
5283      * @cfg {String} okText
5284      * The text to display on the ok button
5285      */
5286     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5287     /**
5288      * @cfg {String} cancelText
5289      * The text to display on the cancel button
5290      */
5291     cancelText : "Cancel",
5292     /**
5293      * @cfg {String} todayTip
5294      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5295      */
5296     todayTip : "{0} (Spacebar)",
5297     /**
5298      * @cfg {Date} minDate
5299      * Minimum allowable date (JavaScript date object, defaults to null)
5300      */
5301     minDate : null,
5302     /**
5303      * @cfg {Date} maxDate
5304      * Maximum allowable date (JavaScript date object, defaults to null)
5305      */
5306     maxDate : null,
5307     /**
5308      * @cfg {String} minText
5309      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5310      */
5311     minText : "This date is before the minimum date",
5312     /**
5313      * @cfg {String} maxText
5314      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5315      */
5316     maxText : "This date is after the maximum date",
5317     /**
5318      * @cfg {String} format
5319      * The default date format string which can be overriden for localization support.  The format must be
5320      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5321      */
5322     format : "m/d/y",
5323     /**
5324      * @cfg {Array} disabledDays
5325      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5326      */
5327     disabledDays : null,
5328     /**
5329      * @cfg {String} disabledDaysText
5330      * The tooltip to display when the date falls on a disabled day (defaults to "")
5331      */
5332     disabledDaysText : "",
5333     /**
5334      * @cfg {RegExp} disabledDatesRE
5335      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5336      */
5337     disabledDatesRE : null,
5338     /**
5339      * @cfg {String} disabledDatesText
5340      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5341      */
5342     disabledDatesText : "",
5343     /**
5344      * @cfg {Boolean} constrainToViewport
5345      * True to constrain the date picker to the viewport (defaults to true)
5346      */
5347     constrainToViewport : true,
5348     /**
5349      * @cfg {Array} monthNames
5350      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5351      */
5352     monthNames : Date.monthNames,
5353     /**
5354      * @cfg {Array} dayNames
5355      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5356      */
5357     dayNames : Date.dayNames,
5358     /**
5359      * @cfg {String} nextText
5360      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5361      */
5362     nextText: 'Next Month (Control+Right)',
5363     /**
5364      * @cfg {String} prevText
5365      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5366      */
5367     prevText: 'Previous Month (Control+Left)',
5368     /**
5369      * @cfg {String} monthYearText
5370      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5371      */
5372     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5373     /**
5374      * @cfg {Number} startDay
5375      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5376      */
5377     startDay : 0,
5378     /**
5379      * @cfg {Bool} showClear
5380      * Show a clear button (usefull for date form elements that can be blank.)
5381      */
5382     
5383     showClear: false,
5384     
5385     /**
5386      * Sets the value of the date field
5387      * @param {Date} value The date to set
5388      */
5389     setValue : function(value){
5390         var old = this.value;
5391         
5392         if (typeof(value) == 'string') {
5393          
5394             value = Date.parseDate(value, this.format);
5395         }
5396         if (!value) {
5397             value = new Date();
5398         }
5399         
5400         this.value = value.clearTime(true);
5401         if(this.el){
5402             this.update(this.value);
5403         }
5404     },
5405
5406     /**
5407      * Gets the current selected value of the date field
5408      * @return {Date} The selected date
5409      */
5410     getValue : function(){
5411         return this.value;
5412     },
5413
5414     // private
5415     focus : function(){
5416         if(this.el){
5417             this.update(this.activeDate);
5418         }
5419     },
5420
5421     // privateval
5422     onRender : function(container, position){
5423         
5424         var m = [
5425              '<table cellspacing="0">',
5426                 '<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>',
5427                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5428         var dn = this.dayNames;
5429         for(var i = 0; i < 7; i++){
5430             var d = this.startDay+i;
5431             if(d > 6){
5432                 d = d-7;
5433             }
5434             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5435         }
5436         m[m.length] = "</tr></thead><tbody><tr>";
5437         for(var i = 0; i < 42; i++) {
5438             if(i % 7 == 0 && i != 0){
5439                 m[m.length] = "</tr><tr>";
5440             }
5441             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5442         }
5443         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5444             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5445
5446         var el = document.createElement("div");
5447         el.className = "x-date-picker";
5448         el.innerHTML = m.join("");
5449
5450         container.dom.insertBefore(el, position);
5451
5452         this.el = Roo.get(el);
5453         this.eventEl = Roo.get(el.firstChild);
5454
5455         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5456             handler: this.showPrevMonth,
5457             scope: this,
5458             preventDefault:true,
5459             stopDefault:true
5460         });
5461
5462         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5463             handler: this.showNextMonth,
5464             scope: this,
5465             preventDefault:true,
5466             stopDefault:true
5467         });
5468
5469         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5470
5471         this.monthPicker = this.el.down('div.x-date-mp');
5472         this.monthPicker.enableDisplayMode('block');
5473         
5474         var kn = new Roo.KeyNav(this.eventEl, {
5475             "left" : function(e){
5476                 e.ctrlKey ?
5477                     this.showPrevMonth() :
5478                     this.update(this.activeDate.add("d", -1));
5479             },
5480
5481             "right" : function(e){
5482                 e.ctrlKey ?
5483                     this.showNextMonth() :
5484                     this.update(this.activeDate.add("d", 1));
5485             },
5486
5487             "up" : function(e){
5488                 e.ctrlKey ?
5489                     this.showNextYear() :
5490                     this.update(this.activeDate.add("d", -7));
5491             },
5492
5493             "down" : function(e){
5494                 e.ctrlKey ?
5495                     this.showPrevYear() :
5496                     this.update(this.activeDate.add("d", 7));
5497             },
5498
5499             "pageUp" : function(e){
5500                 this.showNextMonth();
5501             },
5502
5503             "pageDown" : function(e){
5504                 this.showPrevMonth();
5505             },
5506
5507             "enter" : function(e){
5508                 e.stopPropagation();
5509                 return true;
5510             },
5511
5512             scope : this
5513         });
5514
5515         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5516
5517         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5518
5519         this.el.unselectable();
5520         
5521         this.cells = this.el.select("table.x-date-inner tbody td");
5522         this.textNodes = this.el.query("table.x-date-inner tbody span");
5523
5524         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5525             text: "&#160;",
5526             tooltip: this.monthYearText
5527         });
5528
5529         this.mbtn.on('click', this.showMonthPicker, this);
5530         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5531
5532
5533         var today = (new Date()).dateFormat(this.format);
5534         
5535         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5536         if (this.showClear) {
5537             baseTb.add( new Roo.Toolbar.Fill());
5538         }
5539         baseTb.add({
5540             text: String.format(this.todayText, today),
5541             tooltip: String.format(this.todayTip, today),
5542             handler: this.selectToday,
5543             scope: this
5544         });
5545         
5546         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5547             
5548         //});
5549         if (this.showClear) {
5550             
5551             baseTb.add( new Roo.Toolbar.Fill());
5552             baseTb.add({
5553                 text: '&#160;',
5554                 cls: 'x-btn-icon x-btn-clear',
5555                 handler: function() {
5556                     //this.value = '';
5557                     this.fireEvent("select", this, '');
5558                 },
5559                 scope: this
5560             });
5561         }
5562         
5563         
5564         if(Roo.isIE){
5565             this.el.repaint();
5566         }
5567         this.update(this.value);
5568     },
5569
5570     createMonthPicker : function(){
5571         if(!this.monthPicker.dom.firstChild){
5572             var buf = ['<table border="0" cellspacing="0">'];
5573             for(var i = 0; i < 6; i++){
5574                 buf.push(
5575                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5576                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5577                     i == 0 ?
5578                     '<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>' :
5579                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5580                 );
5581             }
5582             buf.push(
5583                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5584                     this.okText,
5585                     '</button><button type="button" class="x-date-mp-cancel">',
5586                     this.cancelText,
5587                     '</button></td></tr>',
5588                 '</table>'
5589             );
5590             this.monthPicker.update(buf.join(''));
5591             this.monthPicker.on('click', this.onMonthClick, this);
5592             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5593
5594             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5595             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5596
5597             this.mpMonths.each(function(m, a, i){
5598                 i += 1;
5599                 if((i%2) == 0){
5600                     m.dom.xmonth = 5 + Math.round(i * .5);
5601                 }else{
5602                     m.dom.xmonth = Math.round((i-1) * .5);
5603                 }
5604             });
5605         }
5606     },
5607
5608     showMonthPicker : function(){
5609         this.createMonthPicker();
5610         var size = this.el.getSize();
5611         this.monthPicker.setSize(size);
5612         this.monthPicker.child('table').setSize(size);
5613
5614         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5615         this.updateMPMonth(this.mpSelMonth);
5616         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5617         this.updateMPYear(this.mpSelYear);
5618
5619         this.monthPicker.slideIn('t', {duration:.2});
5620     },
5621
5622     updateMPYear : function(y){
5623         this.mpyear = y;
5624         var ys = this.mpYears.elements;
5625         for(var i = 1; i <= 10; i++){
5626             var td = ys[i-1], y2;
5627             if((i%2) == 0){
5628                 y2 = y + Math.round(i * .5);
5629                 td.firstChild.innerHTML = y2;
5630                 td.xyear = y2;
5631             }else{
5632                 y2 = y - (5-Math.round(i * .5));
5633                 td.firstChild.innerHTML = y2;
5634                 td.xyear = y2;
5635             }
5636             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5637         }
5638     },
5639
5640     updateMPMonth : function(sm){
5641         this.mpMonths.each(function(m, a, i){
5642             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5643         });
5644     },
5645
5646     selectMPMonth: function(m){
5647         
5648     },
5649
5650     onMonthClick : function(e, t){
5651         e.stopEvent();
5652         var el = new Roo.Element(t), pn;
5653         if(el.is('button.x-date-mp-cancel')){
5654             this.hideMonthPicker();
5655         }
5656         else if(el.is('button.x-date-mp-ok')){
5657             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660         else if(pn = el.up('td.x-date-mp-month', 2)){
5661             this.mpMonths.removeClass('x-date-mp-sel');
5662             pn.addClass('x-date-mp-sel');
5663             this.mpSelMonth = pn.dom.xmonth;
5664         }
5665         else if(pn = el.up('td.x-date-mp-year', 2)){
5666             this.mpYears.removeClass('x-date-mp-sel');
5667             pn.addClass('x-date-mp-sel');
5668             this.mpSelYear = pn.dom.xyear;
5669         }
5670         else if(el.is('a.x-date-mp-prev')){
5671             this.updateMPYear(this.mpyear-10);
5672         }
5673         else if(el.is('a.x-date-mp-next')){
5674             this.updateMPYear(this.mpyear+10);
5675         }
5676     },
5677
5678     onMonthDblClick : function(e, t){
5679         e.stopEvent();
5680         var el = new Roo.Element(t), pn;
5681         if(pn = el.up('td.x-date-mp-month', 2)){
5682             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5683             this.hideMonthPicker();
5684         }
5685         else if(pn = el.up('td.x-date-mp-year', 2)){
5686             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5687             this.hideMonthPicker();
5688         }
5689     },
5690
5691     hideMonthPicker : function(disableAnim){
5692         if(this.monthPicker){
5693             if(disableAnim === true){
5694                 this.monthPicker.hide();
5695             }else{
5696                 this.monthPicker.slideOut('t', {duration:.2});
5697             }
5698         }
5699     },
5700
5701     // private
5702     showPrevMonth : function(e){
5703         this.update(this.activeDate.add("mo", -1));
5704     },
5705
5706     // private
5707     showNextMonth : function(e){
5708         this.update(this.activeDate.add("mo", 1));
5709     },
5710
5711     // private
5712     showPrevYear : function(){
5713         this.update(this.activeDate.add("y", -1));
5714     },
5715
5716     // private
5717     showNextYear : function(){
5718         this.update(this.activeDate.add("y", 1));
5719     },
5720
5721     // private
5722     handleMouseWheel : function(e){
5723         var delta = e.getWheelDelta();
5724         if(delta > 0){
5725             this.showPrevMonth();
5726             e.stopEvent();
5727         } else if(delta < 0){
5728             this.showNextMonth();
5729             e.stopEvent();
5730         }
5731     },
5732
5733     // private
5734     handleDateClick : function(e, t){
5735         e.stopEvent();
5736         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5737             this.setValue(new Date(t.dateValue));
5738             this.fireEvent("select", this, this.value);
5739         }
5740     },
5741
5742     // private
5743     selectToday : function(){
5744         this.setValue(new Date().clearTime());
5745         this.fireEvent("select", this, this.value);
5746     },
5747
5748     // private
5749     update : function(date)
5750     {
5751         var vd = this.activeDate;
5752         this.activeDate = date;
5753         if(vd && this.el){
5754             var t = date.getTime();
5755             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5756                 this.cells.removeClass("x-date-selected");
5757                 this.cells.each(function(c){
5758                    if(c.dom.firstChild.dateValue == t){
5759                        c.addClass("x-date-selected");
5760                        setTimeout(function(){
5761                             try{c.dom.firstChild.focus();}catch(e){}
5762                        }, 50);
5763                        return false;
5764                    }
5765                 });
5766                 return;
5767             }
5768         }
5769         
5770         var days = date.getDaysInMonth();
5771         var firstOfMonth = date.getFirstDateOfMonth();
5772         var startingPos = firstOfMonth.getDay()-this.startDay;
5773
5774         if(startingPos <= this.startDay){
5775             startingPos += 7;
5776         }
5777
5778         var pm = date.add("mo", -1);
5779         var prevStart = pm.getDaysInMonth()-startingPos;
5780
5781         var cells = this.cells.elements;
5782         var textEls = this.textNodes;
5783         days += startingPos;
5784
5785         // convert everything to numbers so it's fast
5786         var day = 86400000;
5787         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5788         var today = new Date().clearTime().getTime();
5789         var sel = date.clearTime().getTime();
5790         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5791         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5792         var ddMatch = this.disabledDatesRE;
5793         var ddText = this.disabledDatesText;
5794         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5795         var ddaysText = this.disabledDaysText;
5796         var format = this.format;
5797
5798         var setCellClass = function(cal, cell){
5799             cell.title = "";
5800             var t = d.getTime();
5801             cell.firstChild.dateValue = t;
5802             if(t == today){
5803                 cell.className += " x-date-today";
5804                 cell.title = cal.todayText;
5805             }
5806             if(t == sel){
5807                 cell.className += " x-date-selected";
5808                 setTimeout(function(){
5809                     try{cell.firstChild.focus();}catch(e){}
5810                 }, 50);
5811             }
5812             // disabling
5813             if(t < min) {
5814                 cell.className = " x-date-disabled";
5815                 cell.title = cal.minText;
5816                 return;
5817             }
5818             if(t > max) {
5819                 cell.className = " x-date-disabled";
5820                 cell.title = cal.maxText;
5821                 return;
5822             }
5823             if(ddays){
5824                 if(ddays.indexOf(d.getDay()) != -1){
5825                     cell.title = ddaysText;
5826                     cell.className = " x-date-disabled";
5827                 }
5828             }
5829             if(ddMatch && format){
5830                 var fvalue = d.dateFormat(format);
5831                 if(ddMatch.test(fvalue)){
5832                     cell.title = ddText.replace("%0", fvalue);
5833                     cell.className = " x-date-disabled";
5834                 }
5835             }
5836         };
5837
5838         var i = 0;
5839         for(; i < startingPos; i++) {
5840             textEls[i].innerHTML = (++prevStart);
5841             d.setDate(d.getDate()+1);
5842             cells[i].className = "x-date-prevday";
5843             setCellClass(this, cells[i]);
5844         }
5845         for(; i < days; i++){
5846             intDay = i - startingPos + 1;
5847             textEls[i].innerHTML = (intDay);
5848             d.setDate(d.getDate()+1);
5849             cells[i].className = "x-date-active";
5850             setCellClass(this, cells[i]);
5851         }
5852         var extraDays = 0;
5853         for(; i < 42; i++) {
5854              textEls[i].innerHTML = (++extraDays);
5855              d.setDate(d.getDate()+1);
5856              cells[i].className = "x-date-nextday";
5857              setCellClass(this, cells[i]);
5858         }
5859
5860         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5861         this.fireEvent('monthchange', this, date);
5862         
5863         if(!this.internalRender){
5864             var main = this.el.dom.firstChild;
5865             var w = main.offsetWidth;
5866             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5867             Roo.fly(main).setWidth(w);
5868             this.internalRender = true;
5869             // opera does not respect the auto grow header center column
5870             // then, after it gets a width opera refuses to recalculate
5871             // without a second pass
5872             if(Roo.isOpera && !this.secondPass){
5873                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5874                 this.secondPass = true;
5875                 this.update.defer(10, this, [date]);
5876             }
5877         }
5878         
5879         
5880     }
5881 });        /*
5882  * Based on:
5883  * Ext JS Library 1.1.1
5884  * Copyright(c) 2006-2007, Ext JS, LLC.
5885  *
5886  * Originally Released Under LGPL - original licence link has changed is not relivant.
5887  *
5888  * Fork - LGPL
5889  * <script type="text/javascript">
5890  */
5891 /**
5892  * @class Roo.TabPanel
5893  * @extends Roo.util.Observable
5894  * A lightweight tab container.
5895  * <br><br>
5896  * Usage:
5897  * <pre><code>
5898 // basic tabs 1, built from existing content
5899 var tabs = new Roo.TabPanel("tabs1");
5900 tabs.addTab("script", "View Script");
5901 tabs.addTab("markup", "View Markup");
5902 tabs.activate("script");
5903
5904 // more advanced tabs, built from javascript
5905 var jtabs = new Roo.TabPanel("jtabs");
5906 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5907
5908 // set up the UpdateManager
5909 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5910 var updater = tab2.getUpdateManager();
5911 updater.setDefaultUrl("ajax1.htm");
5912 tab2.on('activate', updater.refresh, updater, true);
5913
5914 // Use setUrl for Ajax loading
5915 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5916 tab3.setUrl("ajax2.htm", null, true);
5917
5918 // Disabled tab
5919 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5920 tab4.disable();
5921
5922 jtabs.activate("jtabs-1");
5923  * </code></pre>
5924  * @constructor
5925  * Create a new TabPanel.
5926  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5927  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5928  */
5929 Roo.TabPanel = function(container, config){
5930     /**
5931     * The container element for this TabPanel.
5932     * @type Roo.Element
5933     */
5934     this.el = Roo.get(container, true);
5935     if(config){
5936         if(typeof config == "boolean"){
5937             this.tabPosition = config ? "bottom" : "top";
5938         }else{
5939             Roo.apply(this, config);
5940         }
5941     }
5942     if(this.tabPosition == "bottom"){
5943         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5944         this.el.addClass("x-tabs-bottom");
5945     }
5946     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5947     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5948     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5949     if(Roo.isIE){
5950         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5951     }
5952     if(this.tabPosition != "bottom"){
5953         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5954          * @type Roo.Element
5955          */
5956         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5957         this.el.addClass("x-tabs-top");
5958     }
5959     this.items = [];
5960
5961     this.bodyEl.setStyle("position", "relative");
5962
5963     this.active = null;
5964     this.activateDelegate = this.activate.createDelegate(this);
5965
5966     this.addEvents({
5967         /**
5968          * @event tabchange
5969          * Fires when the active tab changes
5970          * @param {Roo.TabPanel} this
5971          * @param {Roo.TabPanelItem} activePanel The new active tab
5972          */
5973         "tabchange": true,
5974         /**
5975          * @event beforetabchange
5976          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5977          * @param {Roo.TabPanel} this
5978          * @param {Object} e Set cancel to true on this object to cancel the tab change
5979          * @param {Roo.TabPanelItem} tab The tab being changed to
5980          */
5981         "beforetabchange" : true
5982     });
5983
5984     Roo.EventManager.onWindowResize(this.onResize, this);
5985     this.cpad = this.el.getPadding("lr");
5986     this.hiddenCount = 0;
5987
5988
5989     // toolbar on the tabbar support...
5990     if (this.toolbar) {
5991         var tcfg = this.toolbar;
5992         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5993         this.toolbar = new Roo.Toolbar(tcfg);
5994         if (Roo.isSafari) {
5995             var tbl = tcfg.container.child('table', true);
5996             tbl.setAttribute('width', '100%');
5997         }
5998         
5999     }
6000    
6001
6002
6003     Roo.TabPanel.superclass.constructor.call(this);
6004 };
6005
6006 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6007     /*
6008      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6009      */
6010     tabPosition : "top",
6011     /*
6012      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6013      */
6014     currentTabWidth : 0,
6015     /*
6016      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6017      */
6018     minTabWidth : 40,
6019     /*
6020      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6021      */
6022     maxTabWidth : 250,
6023     /*
6024      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6025      */
6026     preferredTabWidth : 175,
6027     /*
6028      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6029      */
6030     resizeTabs : false,
6031     /*
6032      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6033      */
6034     monitorResize : true,
6035     /*
6036      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6037      */
6038     toolbar : false,
6039
6040     /**
6041      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6042      * @param {String} id The id of the div to use <b>or create</b>
6043      * @param {String} text The text for the tab
6044      * @param {String} content (optional) Content to put in the TabPanelItem body
6045      * @param {Boolean} closable (optional) True to create a close icon on the tab
6046      * @return {Roo.TabPanelItem} The created TabPanelItem
6047      */
6048     addTab : function(id, text, content, closable){
6049         var item = new Roo.TabPanelItem(this, id, text, closable);
6050         this.addTabItem(item);
6051         if(content){
6052             item.setContent(content);
6053         }
6054         return item;
6055     },
6056
6057     /**
6058      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6059      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6060      * @return {Roo.TabPanelItem}
6061      */
6062     getTab : function(id){
6063         return this.items[id];
6064     },
6065
6066     /**
6067      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6068      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6069      */
6070     hideTab : function(id){
6071         var t = this.items[id];
6072         if(!t.isHidden()){
6073            t.setHidden(true);
6074            this.hiddenCount++;
6075            this.autoSizeTabs();
6076         }
6077     },
6078
6079     /**
6080      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6081      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6082      */
6083     unhideTab : function(id){
6084         var t = this.items[id];
6085         if(t.isHidden()){
6086            t.setHidden(false);
6087            this.hiddenCount--;
6088            this.autoSizeTabs();
6089         }
6090     },
6091
6092     /**
6093      * Adds an existing {@link Roo.TabPanelItem}.
6094      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6095      */
6096     addTabItem : function(item){
6097         this.items[item.id] = item;
6098         this.items.push(item);
6099         if(this.resizeTabs){
6100            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6101            this.autoSizeTabs();
6102         }else{
6103             item.autoSize();
6104         }
6105     },
6106
6107     /**
6108      * Removes a {@link Roo.TabPanelItem}.
6109      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6110      */
6111     removeTab : function(id){
6112         var items = this.items;
6113         var tab = items[id];
6114         if(!tab) { return; }
6115         var index = items.indexOf(tab);
6116         if(this.active == tab && items.length > 1){
6117             var newTab = this.getNextAvailable(index);
6118             if(newTab) {
6119                 newTab.activate();
6120             }
6121         }
6122         this.stripEl.dom.removeChild(tab.pnode.dom);
6123         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6124             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6125         }
6126         items.splice(index, 1);
6127         delete this.items[tab.id];
6128         tab.fireEvent("close", tab);
6129         tab.purgeListeners();
6130         this.autoSizeTabs();
6131     },
6132
6133     getNextAvailable : function(start){
6134         var items = this.items;
6135         var index = start;
6136         // look for a next tab that will slide over to
6137         // replace the one being removed
6138         while(index < items.length){
6139             var item = items[++index];
6140             if(item && !item.isHidden()){
6141                 return item;
6142             }
6143         }
6144         // if one isn't found select the previous tab (on the left)
6145         index = start;
6146         while(index >= 0){
6147             var item = items[--index];
6148             if(item && !item.isHidden()){
6149                 return item;
6150             }
6151         }
6152         return null;
6153     },
6154
6155     /**
6156      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6157      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6158      */
6159     disableTab : function(id){
6160         var tab = this.items[id];
6161         if(tab && this.active != tab){
6162             tab.disable();
6163         }
6164     },
6165
6166     /**
6167      * Enables a {@link Roo.TabPanelItem} that is disabled.
6168      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6169      */
6170     enableTab : function(id){
6171         var tab = this.items[id];
6172         tab.enable();
6173     },
6174
6175     /**
6176      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6177      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6178      * @return {Roo.TabPanelItem} The TabPanelItem.
6179      */
6180     activate : function(id){
6181         var tab = this.items[id];
6182         if(!tab){
6183             return null;
6184         }
6185         if(tab == this.active || tab.disabled){
6186             return tab;
6187         }
6188         var e = {};
6189         this.fireEvent("beforetabchange", this, e, tab);
6190         if(e.cancel !== true && !tab.disabled){
6191             if(this.active){
6192                 this.active.hide();
6193             }
6194             this.active = this.items[id];
6195             this.active.show();
6196             this.fireEvent("tabchange", this, this.active);
6197         }
6198         return tab;
6199     },
6200
6201     /**
6202      * Gets the active {@link Roo.TabPanelItem}.
6203      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6204      */
6205     getActiveTab : function(){
6206         return this.active;
6207     },
6208
6209     /**
6210      * Updates the tab body element to fit the height of the container element
6211      * for overflow scrolling
6212      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6213      */
6214     syncHeight : function(targetHeight){
6215         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6216         var bm = this.bodyEl.getMargins();
6217         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6218         this.bodyEl.setHeight(newHeight);
6219         return newHeight;
6220     },
6221
6222     onResize : function(){
6223         if(this.monitorResize){
6224             this.autoSizeTabs();
6225         }
6226     },
6227
6228     /**
6229      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6230      */
6231     beginUpdate : function(){
6232         this.updating = true;
6233     },
6234
6235     /**
6236      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6237      */
6238     endUpdate : function(){
6239         this.updating = false;
6240         this.autoSizeTabs();
6241     },
6242
6243     /**
6244      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6245      */
6246     autoSizeTabs : function(){
6247         var count = this.items.length;
6248         var vcount = count - this.hiddenCount;
6249         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6250             return;
6251         }
6252         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6253         var availWidth = Math.floor(w / vcount);
6254         var b = this.stripBody;
6255         if(b.getWidth() > w){
6256             var tabs = this.items;
6257             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6258             if(availWidth < this.minTabWidth){
6259                 /*if(!this.sleft){    // incomplete scrolling code
6260                     this.createScrollButtons();
6261                 }
6262                 this.showScroll();
6263                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6264             }
6265         }else{
6266             if(this.currentTabWidth < this.preferredTabWidth){
6267                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6268             }
6269         }
6270     },
6271
6272     /**
6273      * Returns the number of tabs in this TabPanel.
6274      * @return {Number}
6275      */
6276      getCount : function(){
6277          return this.items.length;
6278      },
6279
6280     /**
6281      * Resizes all the tabs to the passed width
6282      * @param {Number} The new width
6283      */
6284     setTabWidth : function(width){
6285         this.currentTabWidth = width;
6286         for(var i = 0, len = this.items.length; i < len; i++) {
6287                 if(!this.items[i].isHidden()) {
6288                 this.items[i].setWidth(width);
6289             }
6290         }
6291     },
6292
6293     /**
6294      * Destroys this TabPanel
6295      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6296      */
6297     destroy : function(removeEl){
6298         Roo.EventManager.removeResizeListener(this.onResize, this);
6299         for(var i = 0, len = this.items.length; i < len; i++){
6300             this.items[i].purgeListeners();
6301         }
6302         if(removeEl === true){
6303             this.el.update("");
6304             this.el.remove();
6305         }
6306     }
6307 });
6308
6309 /**
6310  * @class Roo.TabPanelItem
6311  * @extends Roo.util.Observable
6312  * Represents an individual item (tab plus body) in a TabPanel.
6313  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6314  * @param {String} id The id of this TabPanelItem
6315  * @param {String} text The text for the tab of this TabPanelItem
6316  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6317  */
6318 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6319     /**
6320      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6321      * @type Roo.TabPanel
6322      */
6323     this.tabPanel = tabPanel;
6324     /**
6325      * The id for this TabPanelItem
6326      * @type String
6327      */
6328     this.id = id;
6329     /** @private */
6330     this.disabled = false;
6331     /** @private */
6332     this.text = text;
6333     /** @private */
6334     this.loaded = false;
6335     this.closable = closable;
6336
6337     /**
6338      * The body element for this TabPanelItem.
6339      * @type Roo.Element
6340      */
6341     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6342     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6343     this.bodyEl.setStyle("display", "block");
6344     this.bodyEl.setStyle("zoom", "1");
6345     this.hideAction();
6346
6347     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6348     /** @private */
6349     this.el = Roo.get(els.el, true);
6350     this.inner = Roo.get(els.inner, true);
6351     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6352     this.pnode = Roo.get(els.el.parentNode, true);
6353     this.el.on("mousedown", this.onTabMouseDown, this);
6354     this.el.on("click", this.onTabClick, this);
6355     /** @private */
6356     if(closable){
6357         var c = Roo.get(els.close, true);
6358         c.dom.title = this.closeText;
6359         c.addClassOnOver("close-over");
6360         c.on("click", this.closeClick, this);
6361      }
6362
6363     this.addEvents({
6364          /**
6365          * @event activate
6366          * Fires when this tab becomes the active tab.
6367          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6368          * @param {Roo.TabPanelItem} this
6369          */
6370         "activate": true,
6371         /**
6372          * @event beforeclose
6373          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6374          * @param {Roo.TabPanelItem} this
6375          * @param {Object} e Set cancel to true on this object to cancel the close.
6376          */
6377         "beforeclose": true,
6378         /**
6379          * @event close
6380          * Fires when this tab is closed.
6381          * @param {Roo.TabPanelItem} this
6382          */
6383          "close": true,
6384         /**
6385          * @event deactivate
6386          * Fires when this tab is no longer the active tab.
6387          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6388          * @param {Roo.TabPanelItem} this
6389          */
6390          "deactivate" : true
6391     });
6392     this.hidden = false;
6393
6394     Roo.TabPanelItem.superclass.constructor.call(this);
6395 };
6396
6397 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6398     purgeListeners : function(){
6399        Roo.util.Observable.prototype.purgeListeners.call(this);
6400        this.el.removeAllListeners();
6401     },
6402     /**
6403      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6404      */
6405     show : function(){
6406         this.pnode.addClass("on");
6407         this.showAction();
6408         if(Roo.isOpera){
6409             this.tabPanel.stripWrap.repaint();
6410         }
6411         this.fireEvent("activate", this.tabPanel, this);
6412     },
6413
6414     /**
6415      * Returns true if this tab is the active tab.
6416      * @return {Boolean}
6417      */
6418     isActive : function(){
6419         return this.tabPanel.getActiveTab() == this;
6420     },
6421
6422     /**
6423      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6424      */
6425     hide : function(){
6426         this.pnode.removeClass("on");
6427         this.hideAction();
6428         this.fireEvent("deactivate", this.tabPanel, this);
6429     },
6430
6431     hideAction : function(){
6432         this.bodyEl.hide();
6433         this.bodyEl.setStyle("position", "absolute");
6434         this.bodyEl.setLeft("-20000px");
6435         this.bodyEl.setTop("-20000px");
6436     },
6437
6438     showAction : function(){
6439         this.bodyEl.setStyle("position", "relative");
6440         this.bodyEl.setTop("");
6441         this.bodyEl.setLeft("");
6442         this.bodyEl.show();
6443     },
6444
6445     /**
6446      * Set the tooltip for the tab.
6447      * @param {String} tooltip The tab's tooltip
6448      */
6449     setTooltip : function(text){
6450         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6451             this.textEl.dom.qtip = text;
6452             this.textEl.dom.removeAttribute('title');
6453         }else{
6454             this.textEl.dom.title = text;
6455         }
6456     },
6457
6458     onTabClick : function(e){
6459         e.preventDefault();
6460         this.tabPanel.activate(this.id);
6461     },
6462
6463     onTabMouseDown : function(e){
6464         e.preventDefault();
6465         this.tabPanel.activate(this.id);
6466     },
6467
6468     getWidth : function(){
6469         return this.inner.getWidth();
6470     },
6471
6472     setWidth : function(width){
6473         var iwidth = width - this.pnode.getPadding("lr");
6474         this.inner.setWidth(iwidth);
6475         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6476         this.pnode.setWidth(width);
6477     },
6478
6479     /**
6480      * Show or hide the tab
6481      * @param {Boolean} hidden True to hide or false to show.
6482      */
6483     setHidden : function(hidden){
6484         this.hidden = hidden;
6485         this.pnode.setStyle("display", hidden ? "none" : "");
6486     },
6487
6488     /**
6489      * Returns true if this tab is "hidden"
6490      * @return {Boolean}
6491      */
6492     isHidden : function(){
6493         return this.hidden;
6494     },
6495
6496     /**
6497      * Returns the text for this tab
6498      * @return {String}
6499      */
6500     getText : function(){
6501         return this.text;
6502     },
6503
6504     autoSize : function(){
6505         //this.el.beginMeasure();
6506         this.textEl.setWidth(1);
6507         /*
6508          *  #2804 [new] Tabs in Roojs
6509          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6510          */
6511         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6512         //this.el.endMeasure();
6513     },
6514
6515     /**
6516      * Sets the text for the tab (Note: this also sets the tooltip text)
6517      * @param {String} text The tab's text and tooltip
6518      */
6519     setText : function(text){
6520         this.text = text;
6521         this.textEl.update(text);
6522         this.setTooltip(text);
6523         if(!this.tabPanel.resizeTabs){
6524             this.autoSize();
6525         }
6526     },
6527     /**
6528      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6529      */
6530     activate : function(){
6531         this.tabPanel.activate(this.id);
6532     },
6533
6534     /**
6535      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6536      */
6537     disable : function(){
6538         if(this.tabPanel.active != this){
6539             this.disabled = true;
6540             this.pnode.addClass("disabled");
6541         }
6542     },
6543
6544     /**
6545      * Enables this TabPanelItem if it was previously disabled.
6546      */
6547     enable : function(){
6548         this.disabled = false;
6549         this.pnode.removeClass("disabled");
6550     },
6551
6552     /**
6553      * Sets the content for this TabPanelItem.
6554      * @param {String} content The content
6555      * @param {Boolean} loadScripts true to look for and load scripts
6556      */
6557     setContent : function(content, loadScripts){
6558         this.bodyEl.update(content, loadScripts);
6559     },
6560
6561     /**
6562      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6563      * @return {Roo.UpdateManager} The UpdateManager
6564      */
6565     getUpdateManager : function(){
6566         return this.bodyEl.getUpdateManager();
6567     },
6568
6569     /**
6570      * Set a URL to be used to load the content for this TabPanelItem.
6571      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6572      * @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)
6573      * @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)
6574      * @return {Roo.UpdateManager} The UpdateManager
6575      */
6576     setUrl : function(url, params, loadOnce){
6577         if(this.refreshDelegate){
6578             this.un('activate', this.refreshDelegate);
6579         }
6580         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6581         this.on("activate", this.refreshDelegate);
6582         return this.bodyEl.getUpdateManager();
6583     },
6584
6585     /** @private */
6586     _handleRefresh : function(url, params, loadOnce){
6587         if(!loadOnce || !this.loaded){
6588             var updater = this.bodyEl.getUpdateManager();
6589             updater.update(url, params, this._setLoaded.createDelegate(this));
6590         }
6591     },
6592
6593     /**
6594      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6595      *   Will fail silently if the setUrl method has not been called.
6596      *   This does not activate the panel, just updates its content.
6597      */
6598     refresh : function(){
6599         if(this.refreshDelegate){
6600            this.loaded = false;
6601            this.refreshDelegate();
6602         }
6603     },
6604
6605     /** @private */
6606     _setLoaded : function(){
6607         this.loaded = true;
6608     },
6609
6610     /** @private */
6611     closeClick : function(e){
6612         var o = {};
6613         e.stopEvent();
6614         this.fireEvent("beforeclose", this, o);
6615         if(o.cancel !== true){
6616             this.tabPanel.removeTab(this.id);
6617         }
6618     },
6619     /**
6620      * The text displayed in the tooltip for the close icon.
6621      * @type String
6622      */
6623     closeText : "Close this tab"
6624 });
6625
6626 /** @private */
6627 Roo.TabPanel.prototype.createStrip = function(container){
6628     var strip = document.createElement("div");
6629     strip.className = "x-tabs-wrap";
6630     container.appendChild(strip);
6631     return strip;
6632 };
6633 /** @private */
6634 Roo.TabPanel.prototype.createStripList = function(strip){
6635     // div wrapper for retard IE
6636     // returns the "tr" element.
6637     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6638         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6639         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6640     return strip.firstChild.firstChild.firstChild.firstChild;
6641 };
6642 /** @private */
6643 Roo.TabPanel.prototype.createBody = function(container){
6644     var body = document.createElement("div");
6645     Roo.id(body, "tab-body");
6646     Roo.fly(body).addClass("x-tabs-body");
6647     container.appendChild(body);
6648     return body;
6649 };
6650 /** @private */
6651 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6652     var body = Roo.getDom(id);
6653     if(!body){
6654         body = document.createElement("div");
6655         body.id = id;
6656     }
6657     Roo.fly(body).addClass("x-tabs-item-body");
6658     bodyEl.insertBefore(body, bodyEl.firstChild);
6659     return body;
6660 };
6661 /** @private */
6662 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6663     var td = document.createElement("td");
6664     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6665     //stripEl.appendChild(td);
6666     if(closable){
6667         td.className = "x-tabs-closable";
6668         if(!this.closeTpl){
6669             this.closeTpl = new Roo.Template(
6670                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6671                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6672                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6673             );
6674         }
6675         var el = this.closeTpl.overwrite(td, {"text": text});
6676         var close = el.getElementsByTagName("div")[0];
6677         var inner = el.getElementsByTagName("em")[0];
6678         return {"el": el, "close": close, "inner": inner};
6679     } else {
6680         if(!this.tabTpl){
6681             this.tabTpl = new Roo.Template(
6682                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6683                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6684             );
6685         }
6686         var el = this.tabTpl.overwrite(td, {"text": text});
6687         var inner = el.getElementsByTagName("em")[0];
6688         return {"el": el, "inner": inner};
6689     }
6690 };/*
6691  * Based on:
6692  * Ext JS Library 1.1.1
6693  * Copyright(c) 2006-2007, Ext JS, LLC.
6694  *
6695  * Originally Released Under LGPL - original licence link has changed is not relivant.
6696  *
6697  * Fork - LGPL
6698  * <script type="text/javascript">
6699  */
6700
6701 /**
6702  * @class Roo.Button
6703  * @extends Roo.util.Observable
6704  * Simple Button class
6705  * @cfg {String} text The button text
6706  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6707  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6708  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6709  * @cfg {Object} scope The scope of the handler
6710  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6711  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6712  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6713  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6714  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6715  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6716    applies if enableToggle = true)
6717  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6718  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6719   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6720  * @constructor
6721  * Create a new button
6722  * @param {Object} config The config object
6723  */
6724 Roo.Button = function(renderTo, config)
6725 {
6726     if (!config) {
6727         config = renderTo;
6728         renderTo = config.renderTo || false;
6729     }
6730     
6731     Roo.apply(this, config);
6732     this.addEvents({
6733         /**
6734              * @event click
6735              * Fires when this button is clicked
6736              * @param {Button} this
6737              * @param {EventObject} e The click event
6738              */
6739             "click" : true,
6740         /**
6741              * @event toggle
6742              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6743              * @param {Button} this
6744              * @param {Boolean} pressed
6745              */
6746             "toggle" : true,
6747         /**
6748              * @event mouseover
6749              * Fires when the mouse hovers over the button
6750              * @param {Button} this
6751              * @param {Event} e The event object
6752              */
6753         'mouseover' : true,
6754         /**
6755              * @event mouseout
6756              * Fires when the mouse exits the button
6757              * @param {Button} this
6758              * @param {Event} e The event object
6759              */
6760         'mouseout': true,
6761          /**
6762              * @event render
6763              * Fires when the button is rendered
6764              * @param {Button} this
6765              */
6766         'render': true
6767     });
6768     if(this.menu){
6769         this.menu = Roo.menu.MenuMgr.get(this.menu);
6770     }
6771     // register listeners first!!  - so render can be captured..
6772     Roo.util.Observable.call(this);
6773     if(renderTo){
6774         this.render(renderTo);
6775     }
6776     
6777   
6778 };
6779
6780 Roo.extend(Roo.Button, Roo.util.Observable, {
6781     /**
6782      * 
6783      */
6784     
6785     /**
6786      * Read-only. True if this button is hidden
6787      * @type Boolean
6788      */
6789     hidden : false,
6790     /**
6791      * Read-only. True if this button is disabled
6792      * @type Boolean
6793      */
6794     disabled : false,
6795     /**
6796      * Read-only. True if this button is pressed (only if enableToggle = true)
6797      * @type Boolean
6798      */
6799     pressed : false,
6800
6801     /**
6802      * @cfg {Number} tabIndex 
6803      * The DOM tabIndex for this button (defaults to undefined)
6804      */
6805     tabIndex : undefined,
6806
6807     /**
6808      * @cfg {Boolean} enableToggle
6809      * True to enable pressed/not pressed toggling (defaults to false)
6810      */
6811     enableToggle: false,
6812     /**
6813      * @cfg {Mixed} menu
6814      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6815      */
6816     menu : undefined,
6817     /**
6818      * @cfg {String} menuAlign
6819      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6820      */
6821     menuAlign : "tl-bl?",
6822
6823     /**
6824      * @cfg {String} iconCls
6825      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6826      */
6827     iconCls : undefined,
6828     /**
6829      * @cfg {String} type
6830      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6831      */
6832     type : 'button',
6833
6834     // private
6835     menuClassTarget: 'tr',
6836
6837     /**
6838      * @cfg {String} clickEvent
6839      * The type of event to map to the button's event handler (defaults to 'click')
6840      */
6841     clickEvent : 'click',
6842
6843     /**
6844      * @cfg {Boolean} handleMouseEvents
6845      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6846      */
6847     handleMouseEvents : true,
6848
6849     /**
6850      * @cfg {String} tooltipType
6851      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6852      */
6853     tooltipType : 'qtip',
6854
6855     /**
6856      * @cfg {String} cls
6857      * A CSS class to apply to the button's main element.
6858      */
6859     
6860     /**
6861      * @cfg {Roo.Template} template (Optional)
6862      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6863      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6864      * require code modifications if required elements (e.g. a button) aren't present.
6865      */
6866
6867     // private
6868     render : function(renderTo){
6869         var btn;
6870         if(this.hideParent){
6871             this.parentEl = Roo.get(renderTo);
6872         }
6873         if(!this.dhconfig){
6874             if(!this.template){
6875                 if(!Roo.Button.buttonTemplate){
6876                     // hideous table template
6877                     Roo.Button.buttonTemplate = new Roo.Template(
6878                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6879                         '<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>',
6880                         "</tr></tbody></table>");
6881                 }
6882                 this.template = Roo.Button.buttonTemplate;
6883             }
6884             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6885             var btnEl = btn.child("button:first");
6886             btnEl.on('focus', this.onFocus, this);
6887             btnEl.on('blur', this.onBlur, this);
6888             if(this.cls){
6889                 btn.addClass(this.cls);
6890             }
6891             if(this.icon){
6892                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6893             }
6894             if(this.iconCls){
6895                 btnEl.addClass(this.iconCls);
6896                 if(!this.cls){
6897                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6898                 }
6899             }
6900             if(this.tabIndex !== undefined){
6901                 btnEl.dom.tabIndex = this.tabIndex;
6902             }
6903             if(this.tooltip){
6904                 if(typeof this.tooltip == 'object'){
6905                     Roo.QuickTips.tips(Roo.apply({
6906                           target: btnEl.id
6907                     }, this.tooltip));
6908                 } else {
6909                     btnEl.dom[this.tooltipType] = this.tooltip;
6910                 }
6911             }
6912         }else{
6913             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6914         }
6915         this.el = btn;
6916         if(this.id){
6917             this.el.dom.id = this.el.id = this.id;
6918         }
6919         if(this.menu){
6920             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6921             this.menu.on("show", this.onMenuShow, this);
6922             this.menu.on("hide", this.onMenuHide, this);
6923         }
6924         btn.addClass("x-btn");
6925         if(Roo.isIE && !Roo.isIE7){
6926             this.autoWidth.defer(1, this);
6927         }else{
6928             this.autoWidth();
6929         }
6930         if(this.handleMouseEvents){
6931             btn.on("mouseover", this.onMouseOver, this);
6932             btn.on("mouseout", this.onMouseOut, this);
6933             btn.on("mousedown", this.onMouseDown, this);
6934         }
6935         btn.on(this.clickEvent, this.onClick, this);
6936         //btn.on("mouseup", this.onMouseUp, this);
6937         if(this.hidden){
6938             this.hide();
6939         }
6940         if(this.disabled){
6941             this.disable();
6942         }
6943         Roo.ButtonToggleMgr.register(this);
6944         if(this.pressed){
6945             this.el.addClass("x-btn-pressed");
6946         }
6947         if(this.repeat){
6948             var repeater = new Roo.util.ClickRepeater(btn,
6949                 typeof this.repeat == "object" ? this.repeat : {}
6950             );
6951             repeater.on("click", this.onClick,  this);
6952         }
6953         
6954         this.fireEvent('render', this);
6955         
6956     },
6957     /**
6958      * Returns the button's underlying element
6959      * @return {Roo.Element} The element
6960      */
6961     getEl : function(){
6962         return this.el;  
6963     },
6964     
6965     /**
6966      * Destroys this Button and removes any listeners.
6967      */
6968     destroy : function(){
6969         Roo.ButtonToggleMgr.unregister(this);
6970         this.el.removeAllListeners();
6971         this.purgeListeners();
6972         this.el.remove();
6973     },
6974
6975     // private
6976     autoWidth : function(){
6977         if(this.el){
6978             this.el.setWidth("auto");
6979             if(Roo.isIE7 && Roo.isStrict){
6980                 var ib = this.el.child('button');
6981                 if(ib && ib.getWidth() > 20){
6982                     ib.clip();
6983                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6984                 }
6985             }
6986             if(this.minWidth){
6987                 if(this.hidden){
6988                     this.el.beginMeasure();
6989                 }
6990                 if(this.el.getWidth() < this.minWidth){
6991                     this.el.setWidth(this.minWidth);
6992                 }
6993                 if(this.hidden){
6994                     this.el.endMeasure();
6995                 }
6996             }
6997         }
6998     },
6999
7000     /**
7001      * Assigns this button's click handler
7002      * @param {Function} handler The function to call when the button is clicked
7003      * @param {Object} scope (optional) Scope for the function passed in
7004      */
7005     setHandler : function(handler, scope){
7006         this.handler = handler;
7007         this.scope = scope;  
7008     },
7009     
7010     /**
7011      * Sets this button's text
7012      * @param {String} text The button text
7013      */
7014     setText : function(text){
7015         this.text = text;
7016         if(this.el){
7017             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7018         }
7019         this.autoWidth();
7020     },
7021     
7022     /**
7023      * Gets the text for this button
7024      * @return {String} The button text
7025      */
7026     getText : function(){
7027         return this.text;  
7028     },
7029     
7030     /**
7031      * Show this button
7032      */
7033     show: function(){
7034         this.hidden = false;
7035         if(this.el){
7036             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7037         }
7038     },
7039     
7040     /**
7041      * Hide this button
7042      */
7043     hide: function(){
7044         this.hidden = true;
7045         if(this.el){
7046             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7047         }
7048     },
7049     
7050     /**
7051      * Convenience function for boolean show/hide
7052      * @param {Boolean} visible True to show, false to hide
7053      */
7054     setVisible: function(visible){
7055         if(visible) {
7056             this.show();
7057         }else{
7058             this.hide();
7059         }
7060     },
7061     
7062     /**
7063      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7064      * @param {Boolean} state (optional) Force a particular state
7065      */
7066     toggle : function(state){
7067         state = state === undefined ? !this.pressed : state;
7068         if(state != this.pressed){
7069             if(state){
7070                 this.el.addClass("x-btn-pressed");
7071                 this.pressed = true;
7072                 this.fireEvent("toggle", this, true);
7073             }else{
7074                 this.el.removeClass("x-btn-pressed");
7075                 this.pressed = false;
7076                 this.fireEvent("toggle", this, false);
7077             }
7078             if(this.toggleHandler){
7079                 this.toggleHandler.call(this.scope || this, this, state);
7080             }
7081         }
7082     },
7083     
7084     /**
7085      * Focus the button
7086      */
7087     focus : function(){
7088         this.el.child('button:first').focus();
7089     },
7090     
7091     /**
7092      * Disable this button
7093      */
7094     disable : function(){
7095         if(this.el){
7096             this.el.addClass("x-btn-disabled");
7097         }
7098         this.disabled = true;
7099     },
7100     
7101     /**
7102      * Enable this button
7103      */
7104     enable : function(){
7105         if(this.el){
7106             this.el.removeClass("x-btn-disabled");
7107         }
7108         this.disabled = false;
7109     },
7110
7111     /**
7112      * Convenience function for boolean enable/disable
7113      * @param {Boolean} enabled True to enable, false to disable
7114      */
7115     setDisabled : function(v){
7116         this[v !== true ? "enable" : "disable"]();
7117     },
7118
7119     // private
7120     onClick : function(e)
7121     {
7122         if(e){
7123             e.preventDefault();
7124         }
7125         if(e.button != 0){
7126             return;
7127         }
7128         if(!this.disabled){
7129             if(this.enableToggle){
7130                 this.toggle();
7131             }
7132             if(this.menu && !this.menu.isVisible()){
7133                 this.menu.show(this.el, this.menuAlign);
7134             }
7135             this.fireEvent("click", this, e);
7136             if(this.handler){
7137                 this.el.removeClass("x-btn-over");
7138                 this.handler.call(this.scope || this, this, e);
7139             }
7140         }
7141     },
7142     // private
7143     onMouseOver : function(e){
7144         if(!this.disabled){
7145             this.el.addClass("x-btn-over");
7146             this.fireEvent('mouseover', this, e);
7147         }
7148     },
7149     // private
7150     onMouseOut : function(e){
7151         if(!e.within(this.el,  true)){
7152             this.el.removeClass("x-btn-over");
7153             this.fireEvent('mouseout', this, e);
7154         }
7155     },
7156     // private
7157     onFocus : function(e){
7158         if(!this.disabled){
7159             this.el.addClass("x-btn-focus");
7160         }
7161     },
7162     // private
7163     onBlur : function(e){
7164         this.el.removeClass("x-btn-focus");
7165     },
7166     // private
7167     onMouseDown : function(e){
7168         if(!this.disabled && e.button == 0){
7169             this.el.addClass("x-btn-click");
7170             Roo.get(document).on('mouseup', this.onMouseUp, this);
7171         }
7172     },
7173     // private
7174     onMouseUp : function(e){
7175         if(e.button == 0){
7176             this.el.removeClass("x-btn-click");
7177             Roo.get(document).un('mouseup', this.onMouseUp, this);
7178         }
7179     },
7180     // private
7181     onMenuShow : function(e){
7182         this.el.addClass("x-btn-menu-active");
7183     },
7184     // private
7185     onMenuHide : function(e){
7186         this.el.removeClass("x-btn-menu-active");
7187     }   
7188 });
7189
7190 // Private utility class used by Button
7191 Roo.ButtonToggleMgr = function(){
7192    var groups = {};
7193    
7194    function toggleGroup(btn, state){
7195        if(state){
7196            var g = groups[btn.toggleGroup];
7197            for(var i = 0, l = g.length; i < l; i++){
7198                if(g[i] != btn){
7199                    g[i].toggle(false);
7200                }
7201            }
7202        }
7203    }
7204    
7205    return {
7206        register : function(btn){
7207            if(!btn.toggleGroup){
7208                return;
7209            }
7210            var g = groups[btn.toggleGroup];
7211            if(!g){
7212                g = groups[btn.toggleGroup] = [];
7213            }
7214            g.push(btn);
7215            btn.on("toggle", toggleGroup);
7216        },
7217        
7218        unregister : function(btn){
7219            if(!btn.toggleGroup){
7220                return;
7221            }
7222            var g = groups[btn.toggleGroup];
7223            if(g){
7224                g.remove(btn);
7225                btn.un("toggle", toggleGroup);
7226            }
7227        }
7228    };
7229 }();/*
7230  * Based on:
7231  * Ext JS Library 1.1.1
7232  * Copyright(c) 2006-2007, Ext JS, LLC.
7233  *
7234  * Originally Released Under LGPL - original licence link has changed is not relivant.
7235  *
7236  * Fork - LGPL
7237  * <script type="text/javascript">
7238  */
7239  
7240 /**
7241  * @class Roo.SplitButton
7242  * @extends Roo.Button
7243  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7244  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7245  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7246  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7247  * @cfg {String} arrowTooltip The title attribute of the arrow
7248  * @constructor
7249  * Create a new menu button
7250  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7251  * @param {Object} config The config object
7252  */
7253 Roo.SplitButton = function(renderTo, config){
7254     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7255     /**
7256      * @event arrowclick
7257      * Fires when this button's arrow is clicked
7258      * @param {SplitButton} this
7259      * @param {EventObject} e The click event
7260      */
7261     this.addEvents({"arrowclick":true});
7262 };
7263
7264 Roo.extend(Roo.SplitButton, Roo.Button, {
7265     render : function(renderTo){
7266         // this is one sweet looking template!
7267         var tpl = new Roo.Template(
7268             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7269             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7270             '<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>',
7271             "</tbody></table></td><td>",
7272             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7273             '<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>',
7274             "</tbody></table></td></tr></table>"
7275         );
7276         var btn = tpl.append(renderTo, [this.text, this.type], true);
7277         var btnEl = btn.child("button");
7278         if(this.cls){
7279             btn.addClass(this.cls);
7280         }
7281         if(this.icon){
7282             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7283         }
7284         if(this.iconCls){
7285             btnEl.addClass(this.iconCls);
7286             if(!this.cls){
7287                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7288             }
7289         }
7290         this.el = btn;
7291         if(this.handleMouseEvents){
7292             btn.on("mouseover", this.onMouseOver, this);
7293             btn.on("mouseout", this.onMouseOut, this);
7294             btn.on("mousedown", this.onMouseDown, this);
7295             btn.on("mouseup", this.onMouseUp, this);
7296         }
7297         btn.on(this.clickEvent, this.onClick, this);
7298         if(this.tooltip){
7299             if(typeof this.tooltip == 'object'){
7300                 Roo.QuickTips.tips(Roo.apply({
7301                       target: btnEl.id
7302                 }, this.tooltip));
7303             } else {
7304                 btnEl.dom[this.tooltipType] = this.tooltip;
7305             }
7306         }
7307         if(this.arrowTooltip){
7308             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7309         }
7310         if(this.hidden){
7311             this.hide();
7312         }
7313         if(this.disabled){
7314             this.disable();
7315         }
7316         if(this.pressed){
7317             this.el.addClass("x-btn-pressed");
7318         }
7319         if(Roo.isIE && !Roo.isIE7){
7320             this.autoWidth.defer(1, this);
7321         }else{
7322             this.autoWidth();
7323         }
7324         if(this.menu){
7325             this.menu.on("show", this.onMenuShow, this);
7326             this.menu.on("hide", this.onMenuHide, this);
7327         }
7328         this.fireEvent('render', this);
7329     },
7330
7331     // private
7332     autoWidth : function(){
7333         if(this.el){
7334             var tbl = this.el.child("table:first");
7335             var tbl2 = this.el.child("table:last");
7336             this.el.setWidth("auto");
7337             tbl.setWidth("auto");
7338             if(Roo.isIE7 && Roo.isStrict){
7339                 var ib = this.el.child('button:first');
7340                 if(ib && ib.getWidth() > 20){
7341                     ib.clip();
7342                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7343                 }
7344             }
7345             if(this.minWidth){
7346                 if(this.hidden){
7347                     this.el.beginMeasure();
7348                 }
7349                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7350                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7351                 }
7352                 if(this.hidden){
7353                     this.el.endMeasure();
7354                 }
7355             }
7356             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7357         } 
7358     },
7359     /**
7360      * Sets this button's click handler
7361      * @param {Function} handler The function to call when the button is clicked
7362      * @param {Object} scope (optional) Scope for the function passed above
7363      */
7364     setHandler : function(handler, scope){
7365         this.handler = handler;
7366         this.scope = scope;  
7367     },
7368     
7369     /**
7370      * Sets this button's arrow click handler
7371      * @param {Function} handler The function to call when the arrow is clicked
7372      * @param {Object} scope (optional) Scope for the function passed above
7373      */
7374     setArrowHandler : function(handler, scope){
7375         this.arrowHandler = handler;
7376         this.scope = scope;  
7377     },
7378     
7379     /**
7380      * Focus the button
7381      */
7382     focus : function(){
7383         if(this.el){
7384             this.el.child("button:first").focus();
7385         }
7386     },
7387
7388     // private
7389     onClick : function(e){
7390         e.preventDefault();
7391         if(!this.disabled){
7392             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7393                 if(this.menu && !this.menu.isVisible()){
7394                     this.menu.show(this.el, this.menuAlign);
7395                 }
7396                 this.fireEvent("arrowclick", this, e);
7397                 if(this.arrowHandler){
7398                     this.arrowHandler.call(this.scope || this, this, e);
7399                 }
7400             }else{
7401                 this.fireEvent("click", this, e);
7402                 if(this.handler){
7403                     this.handler.call(this.scope || this, this, e);
7404                 }
7405             }
7406         }
7407     },
7408     // private
7409     onMouseDown : function(e){
7410         if(!this.disabled){
7411             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7412         }
7413     },
7414     // private
7415     onMouseUp : function(e){
7416         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7417     }   
7418 });
7419
7420
7421 // backwards compat
7422 Roo.MenuButton = Roo.SplitButton;/*
7423  * Based on:
7424  * Ext JS Library 1.1.1
7425  * Copyright(c) 2006-2007, Ext JS, LLC.
7426  *
7427  * Originally Released Under LGPL - original licence link has changed is not relivant.
7428  *
7429  * Fork - LGPL
7430  * <script type="text/javascript">
7431  */
7432
7433 /**
7434  * @class Roo.Toolbar
7435  * Basic Toolbar class.
7436  * @constructor
7437  * Creates a new Toolbar
7438  * @param {Object} container The config object
7439  */ 
7440 Roo.Toolbar = function(container, buttons, config)
7441 {
7442     /// old consturctor format still supported..
7443     if(container instanceof Array){ // omit the container for later rendering
7444         buttons = container;
7445         config = buttons;
7446         container = null;
7447     }
7448     if (typeof(container) == 'object' && container.xtype) {
7449         config = container;
7450         container = config.container;
7451         buttons = config.buttons || []; // not really - use items!!
7452     }
7453     var xitems = [];
7454     if (config && config.items) {
7455         xitems = config.items;
7456         delete config.items;
7457     }
7458     Roo.apply(this, config);
7459     this.buttons = buttons;
7460     
7461     if(container){
7462         this.render(container);
7463     }
7464     this.xitems = xitems;
7465     Roo.each(xitems, function(b) {
7466         this.add(b);
7467     }, this);
7468     
7469 };
7470
7471 Roo.Toolbar.prototype = {
7472     /**
7473      * @cfg {Array} items
7474      * array of button configs or elements to add (will be converted to a MixedCollection)
7475      */
7476     
7477     /**
7478      * @cfg {String/HTMLElement/Element} container
7479      * The id or element that will contain the toolbar
7480      */
7481     // private
7482     render : function(ct){
7483         this.el = Roo.get(ct);
7484         if(this.cls){
7485             this.el.addClass(this.cls);
7486         }
7487         // using a table allows for vertical alignment
7488         // 100% width is needed by Safari...
7489         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7490         this.tr = this.el.child("tr", true);
7491         var autoId = 0;
7492         this.items = new Roo.util.MixedCollection(false, function(o){
7493             return o.id || ("item" + (++autoId));
7494         });
7495         if(this.buttons){
7496             this.add.apply(this, this.buttons);
7497             delete this.buttons;
7498         }
7499     },
7500
7501     /**
7502      * Adds element(s) to the toolbar -- this function takes a variable number of 
7503      * arguments of mixed type and adds them to the toolbar.
7504      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7505      * <ul>
7506      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7507      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7508      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7509      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7510      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7511      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7512      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7513      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7514      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7515      * </ul>
7516      * @param {Mixed} arg2
7517      * @param {Mixed} etc.
7518      */
7519     add : function(){
7520         var a = arguments, l = a.length;
7521         for(var i = 0; i < l; i++){
7522             this._add(a[i]);
7523         }
7524     },
7525     // private..
7526     _add : function(el) {
7527         
7528         if (el.xtype) {
7529             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7530         }
7531         
7532         if (el.applyTo){ // some kind of form field
7533             return this.addField(el);
7534         } 
7535         if (el.render){ // some kind of Toolbar.Item
7536             return this.addItem(el);
7537         }
7538         if (typeof el == "string"){ // string
7539             if(el == "separator" || el == "-"){
7540                 return this.addSeparator();
7541             }
7542             if (el == " "){
7543                 return this.addSpacer();
7544             }
7545             if(el == "->"){
7546                 return this.addFill();
7547             }
7548             return this.addText(el);
7549             
7550         }
7551         if(el.tagName){ // element
7552             return this.addElement(el);
7553         }
7554         if(typeof el == "object"){ // must be button config?
7555             return this.addButton(el);
7556         }
7557         // and now what?!?!
7558         return false;
7559         
7560     },
7561     
7562     /**
7563      * Add an Xtype element
7564      * @param {Object} xtype Xtype Object
7565      * @return {Object} created Object
7566      */
7567     addxtype : function(e){
7568         return this.add(e);  
7569     },
7570     
7571     /**
7572      * Returns the Element for this toolbar.
7573      * @return {Roo.Element}
7574      */
7575     getEl : function(){
7576         return this.el;  
7577     },
7578     
7579     /**
7580      * Adds a separator
7581      * @return {Roo.Toolbar.Item} The separator item
7582      */
7583     addSeparator : function(){
7584         return this.addItem(new Roo.Toolbar.Separator());
7585     },
7586
7587     /**
7588      * Adds a spacer element
7589      * @return {Roo.Toolbar.Spacer} The spacer item
7590      */
7591     addSpacer : function(){
7592         return this.addItem(new Roo.Toolbar.Spacer());
7593     },
7594
7595     /**
7596      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7597      * @return {Roo.Toolbar.Fill} The fill item
7598      */
7599     addFill : function(){
7600         return this.addItem(new Roo.Toolbar.Fill());
7601     },
7602
7603     /**
7604      * Adds any standard HTML element to the toolbar
7605      * @param {String/HTMLElement/Element} el The element or id of the element to add
7606      * @return {Roo.Toolbar.Item} The element's item
7607      */
7608     addElement : function(el){
7609         return this.addItem(new Roo.Toolbar.Item(el));
7610     },
7611     /**
7612      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7613      * @type Roo.util.MixedCollection  
7614      */
7615     items : false,
7616      
7617     /**
7618      * Adds any Toolbar.Item or subclass
7619      * @param {Roo.Toolbar.Item} item
7620      * @return {Roo.Toolbar.Item} The item
7621      */
7622     addItem : function(item){
7623         var td = this.nextBlock();
7624         item.render(td);
7625         this.items.add(item);
7626         return item;
7627     },
7628     
7629     /**
7630      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7631      * @param {Object/Array} config A button config or array of configs
7632      * @return {Roo.Toolbar.Button/Array}
7633      */
7634     addButton : function(config){
7635         if(config instanceof Array){
7636             var buttons = [];
7637             for(var i = 0, len = config.length; i < len; i++) {
7638                 buttons.push(this.addButton(config[i]));
7639             }
7640             return buttons;
7641         }
7642         var b = config;
7643         if(!(config instanceof Roo.Toolbar.Button)){
7644             b = config.split ?
7645                 new Roo.Toolbar.SplitButton(config) :
7646                 new Roo.Toolbar.Button(config);
7647         }
7648         var td = this.nextBlock();
7649         b.render(td);
7650         this.items.add(b);
7651         return b;
7652     },
7653     
7654     /**
7655      * Adds text to the toolbar
7656      * @param {String} text The text to add
7657      * @return {Roo.Toolbar.Item} The element's item
7658      */
7659     addText : function(text){
7660         return this.addItem(new Roo.Toolbar.TextItem(text));
7661     },
7662     
7663     /**
7664      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7665      * @param {Number} index The index where the item is to be inserted
7666      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7667      * @return {Roo.Toolbar.Button/Item}
7668      */
7669     insertButton : function(index, item){
7670         if(item instanceof Array){
7671             var buttons = [];
7672             for(var i = 0, len = item.length; i < len; i++) {
7673                buttons.push(this.insertButton(index + i, item[i]));
7674             }
7675             return buttons;
7676         }
7677         if (!(item instanceof Roo.Toolbar.Button)){
7678            item = new Roo.Toolbar.Button(item);
7679         }
7680         var td = document.createElement("td");
7681         this.tr.insertBefore(td, this.tr.childNodes[index]);
7682         item.render(td);
7683         this.items.insert(index, item);
7684         return item;
7685     },
7686     
7687     /**
7688      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7689      * @param {Object} config
7690      * @return {Roo.Toolbar.Item} The element's item
7691      */
7692     addDom : function(config, returnEl){
7693         var td = this.nextBlock();
7694         Roo.DomHelper.overwrite(td, config);
7695         var ti = new Roo.Toolbar.Item(td.firstChild);
7696         ti.render(td);
7697         this.items.add(ti);
7698         return ti;
7699     },
7700
7701     /**
7702      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7703      * @type Roo.util.MixedCollection  
7704      */
7705     fields : false,
7706     
7707     /**
7708      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7709      * Note: the field should not have been rendered yet. For a field that has already been
7710      * rendered, use {@link #addElement}.
7711      * @param {Roo.form.Field} field
7712      * @return {Roo.ToolbarItem}
7713      */
7714      
7715       
7716     addField : function(field) {
7717         if (!this.fields) {
7718             var autoId = 0;
7719             this.fields = new Roo.util.MixedCollection(false, function(o){
7720                 return o.id || ("item" + (++autoId));
7721             });
7722
7723         }
7724         
7725         var td = this.nextBlock();
7726         field.render(td);
7727         var ti = new Roo.Toolbar.Item(td.firstChild);
7728         ti.render(td);
7729         this.items.add(ti);
7730         this.fields.add(field);
7731         return ti;
7732     },
7733     /**
7734      * Hide the toolbar
7735      * @method hide
7736      */
7737      
7738       
7739     hide : function()
7740     {
7741         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7742         this.el.child('div').hide();
7743     },
7744     /**
7745      * Show the toolbar
7746      * @method show
7747      */
7748     show : function()
7749     {
7750         this.el.child('div').show();
7751     },
7752       
7753     // private
7754     nextBlock : function(){
7755         var td = document.createElement("td");
7756         this.tr.appendChild(td);
7757         return td;
7758     },
7759
7760     // private
7761     destroy : function(){
7762         if(this.items){ // rendered?
7763             Roo.destroy.apply(Roo, this.items.items);
7764         }
7765         if(this.fields){ // rendered?
7766             Roo.destroy.apply(Roo, this.fields.items);
7767         }
7768         Roo.Element.uncache(this.el, this.tr);
7769     }
7770 };
7771
7772 /**
7773  * @class Roo.Toolbar.Item
7774  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7775  * @constructor
7776  * Creates a new Item
7777  * @param {HTMLElement} el 
7778  */
7779 Roo.Toolbar.Item = function(el){
7780     var cfg = {};
7781     if (typeof (el.xtype) != 'undefined') {
7782         cfg = el;
7783         el = cfg.el;
7784     }
7785     
7786     this.el = Roo.getDom(el);
7787     this.id = Roo.id(this.el);
7788     this.hidden = false;
7789     
7790     this.addEvents({
7791          /**
7792              * @event render
7793              * Fires when the button is rendered
7794              * @param {Button} this
7795              */
7796         'render': true
7797     });
7798     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7799 };
7800 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7801 //Roo.Toolbar.Item.prototype = {
7802     
7803     /**
7804      * Get this item's HTML Element
7805      * @return {HTMLElement}
7806      */
7807     getEl : function(){
7808        return this.el;  
7809     },
7810
7811     // private
7812     render : function(td){
7813         
7814          this.td = td;
7815         td.appendChild(this.el);
7816         
7817         this.fireEvent('render', this);
7818     },
7819     
7820     /**
7821      * Removes and destroys this item.
7822      */
7823     destroy : function(){
7824         this.td.parentNode.removeChild(this.td);
7825     },
7826     
7827     /**
7828      * Shows this item.
7829      */
7830     show: function(){
7831         this.hidden = false;
7832         this.td.style.display = "";
7833     },
7834     
7835     /**
7836      * Hides this item.
7837      */
7838     hide: function(){
7839         this.hidden = true;
7840         this.td.style.display = "none";
7841     },
7842     
7843     /**
7844      * Convenience function for boolean show/hide.
7845      * @param {Boolean} visible true to show/false to hide
7846      */
7847     setVisible: function(visible){
7848         if(visible) {
7849             this.show();
7850         }else{
7851             this.hide();
7852         }
7853     },
7854     
7855     /**
7856      * Try to focus this item.
7857      */
7858     focus : function(){
7859         Roo.fly(this.el).focus();
7860     },
7861     
7862     /**
7863      * Disables this item.
7864      */
7865     disable : function(){
7866         Roo.fly(this.td).addClass("x-item-disabled");
7867         this.disabled = true;
7868         this.el.disabled = true;
7869     },
7870     
7871     /**
7872      * Enables this item.
7873      */
7874     enable : function(){
7875         Roo.fly(this.td).removeClass("x-item-disabled");
7876         this.disabled = false;
7877         this.el.disabled = false;
7878     }
7879 });
7880
7881
7882 /**
7883  * @class Roo.Toolbar.Separator
7884  * @extends Roo.Toolbar.Item
7885  * A simple toolbar separator class
7886  * @constructor
7887  * Creates a new Separator
7888  */
7889 Roo.Toolbar.Separator = function(cfg){
7890     
7891     var s = document.createElement("span");
7892     s.className = "ytb-sep";
7893     if (cfg) {
7894         cfg.el = s;
7895     }
7896     
7897     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7898 };
7899 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7900     enable:Roo.emptyFn,
7901     disable:Roo.emptyFn,
7902     focus:Roo.emptyFn
7903 });
7904
7905 /**
7906  * @class Roo.Toolbar.Spacer
7907  * @extends Roo.Toolbar.Item
7908  * A simple element that adds extra horizontal space to a toolbar.
7909  * @constructor
7910  * Creates a new Spacer
7911  */
7912 Roo.Toolbar.Spacer = function(cfg){
7913     var s = document.createElement("div");
7914     s.className = "ytb-spacer";
7915     if (cfg) {
7916         cfg.el = s;
7917     }
7918     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7919 };
7920 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7921     enable:Roo.emptyFn,
7922     disable:Roo.emptyFn,
7923     focus:Roo.emptyFn
7924 });
7925
7926 /**
7927  * @class Roo.Toolbar.Fill
7928  * @extends Roo.Toolbar.Spacer
7929  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7930  * @constructor
7931  * Creates a new Spacer
7932  */
7933 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7934     // private
7935     render : function(td){
7936         td.style.width = '100%';
7937         Roo.Toolbar.Fill.superclass.render.call(this, td);
7938     }
7939 });
7940
7941 /**
7942  * @class Roo.Toolbar.TextItem
7943  * @extends Roo.Toolbar.Item
7944  * A simple class that renders text directly into a toolbar.
7945  * @constructor
7946  * Creates a new TextItem
7947  * @param {String} text
7948  */
7949 Roo.Toolbar.TextItem = function(cfg){
7950     var  text = cfg || "";
7951     if (typeof(cfg) == 'object') {
7952         text = cfg.text || "";
7953     }  else {
7954         cfg = null;
7955     }
7956     var s = document.createElement("span");
7957     s.className = "ytb-text";
7958     s.innerHTML = text;
7959     if (cfg) {
7960         cfg.el  = s;
7961     }
7962     
7963     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7964 };
7965 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7966     
7967      
7968     enable:Roo.emptyFn,
7969     disable:Roo.emptyFn,
7970     focus:Roo.emptyFn
7971 });
7972
7973 /**
7974  * @class Roo.Toolbar.Button
7975  * @extends Roo.Button
7976  * A button that renders into a toolbar.
7977  * @constructor
7978  * Creates a new Button
7979  * @param {Object} config A standard {@link Roo.Button} config object
7980  */
7981 Roo.Toolbar.Button = function(config){
7982     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7983 };
7984 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7985     render : function(td){
7986         this.td = td;
7987         Roo.Toolbar.Button.superclass.render.call(this, td);
7988     },
7989     
7990     /**
7991      * Removes and destroys this button
7992      */
7993     destroy : function(){
7994         Roo.Toolbar.Button.superclass.destroy.call(this);
7995         this.td.parentNode.removeChild(this.td);
7996     },
7997     
7998     /**
7999      * Shows this button
8000      */
8001     show: function(){
8002         this.hidden = false;
8003         this.td.style.display = "";
8004     },
8005     
8006     /**
8007      * Hides this button
8008      */
8009     hide: function(){
8010         this.hidden = true;
8011         this.td.style.display = "none";
8012     },
8013
8014     /**
8015      * Disables this item
8016      */
8017     disable : function(){
8018         Roo.fly(this.td).addClass("x-item-disabled");
8019         this.disabled = true;
8020     },
8021
8022     /**
8023      * Enables this item
8024      */
8025     enable : function(){
8026         Roo.fly(this.td).removeClass("x-item-disabled");
8027         this.disabled = false;
8028     }
8029 });
8030 // backwards compat
8031 Roo.ToolbarButton = Roo.Toolbar.Button;
8032
8033 /**
8034  * @class Roo.Toolbar.SplitButton
8035  * @extends Roo.SplitButton
8036  * A menu button that renders into a toolbar.
8037  * @constructor
8038  * Creates a new SplitButton
8039  * @param {Object} config A standard {@link Roo.SplitButton} config object
8040  */
8041 Roo.Toolbar.SplitButton = function(config){
8042     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8043 };
8044 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8045     render : function(td){
8046         this.td = td;
8047         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8048     },
8049     
8050     /**
8051      * Removes and destroys this button
8052      */
8053     destroy : function(){
8054         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8055         this.td.parentNode.removeChild(this.td);
8056     },
8057     
8058     /**
8059      * Shows this button
8060      */
8061     show: function(){
8062         this.hidden = false;
8063         this.td.style.display = "";
8064     },
8065     
8066     /**
8067      * Hides this button
8068      */
8069     hide: function(){
8070         this.hidden = true;
8071         this.td.style.display = "none";
8072     }
8073 });
8074
8075 // backwards compat
8076 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8077  * Based on:
8078  * Ext JS Library 1.1.1
8079  * Copyright(c) 2006-2007, Ext JS, LLC.
8080  *
8081  * Originally Released Under LGPL - original licence link has changed is not relivant.
8082  *
8083  * Fork - LGPL
8084  * <script type="text/javascript">
8085  */
8086  
8087 /**
8088  * @class Roo.PagingToolbar
8089  * @extends Roo.Toolbar
8090  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8091  * @constructor
8092  * Create a new PagingToolbar
8093  * @param {Object} config The config object
8094  */
8095 Roo.PagingToolbar = function(el, ds, config)
8096 {
8097     // old args format still supported... - xtype is prefered..
8098     if (typeof(el) == 'object' && el.xtype) {
8099         // created from xtype...
8100         config = el;
8101         ds = el.dataSource;
8102         el = config.container;
8103     }
8104     var items = [];
8105     if (config.items) {
8106         items = config.items;
8107         config.items = [];
8108     }
8109     
8110     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8111     this.ds = ds;
8112     this.cursor = 0;
8113     this.renderButtons(this.el);
8114     this.bind(ds);
8115     
8116     // supprot items array.
8117    
8118     Roo.each(items, function(e) {
8119         this.add(Roo.factory(e));
8120     },this);
8121     
8122 };
8123
8124 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8125     /**
8126      * @cfg {Roo.data.Store} dataSource
8127      * The underlying data store providing the paged data
8128      */
8129     /**
8130      * @cfg {String/HTMLElement/Element} container
8131      * container The id or element that will contain the toolbar
8132      */
8133     /**
8134      * @cfg {Boolean} displayInfo
8135      * True to display the displayMsg (defaults to false)
8136      */
8137     /**
8138      * @cfg {Number} pageSize
8139      * The number of records to display per page (defaults to 20)
8140      */
8141     pageSize: 20,
8142     /**
8143      * @cfg {String} displayMsg
8144      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8145      */
8146     displayMsg : 'Displaying {0} - {1} of {2}',
8147     /**
8148      * @cfg {String} emptyMsg
8149      * The message to display when no records are found (defaults to "No data to display")
8150      */
8151     emptyMsg : 'No data to display',
8152     /**
8153      * Customizable piece of the default paging text (defaults to "Page")
8154      * @type String
8155      */
8156     beforePageText : "Page",
8157     /**
8158      * Customizable piece of the default paging text (defaults to "of %0")
8159      * @type String
8160      */
8161     afterPageText : "of {0}",
8162     /**
8163      * Customizable piece of the default paging text (defaults to "First Page")
8164      * @type String
8165      */
8166     firstText : "First Page",
8167     /**
8168      * Customizable piece of the default paging text (defaults to "Previous Page")
8169      * @type String
8170      */
8171     prevText : "Previous Page",
8172     /**
8173      * Customizable piece of the default paging text (defaults to "Next Page")
8174      * @type String
8175      */
8176     nextText : "Next Page",
8177     /**
8178      * Customizable piece of the default paging text (defaults to "Last Page")
8179      * @type String
8180      */
8181     lastText : "Last Page",
8182     /**
8183      * Customizable piece of the default paging text (defaults to "Refresh")
8184      * @type String
8185      */
8186     refreshText : "Refresh",
8187
8188     // private
8189     renderButtons : function(el){
8190         Roo.PagingToolbar.superclass.render.call(this, el);
8191         this.first = this.addButton({
8192             tooltip: this.firstText,
8193             cls: "x-btn-icon x-grid-page-first",
8194             disabled: true,
8195             handler: this.onClick.createDelegate(this, ["first"])
8196         });
8197         this.prev = this.addButton({
8198             tooltip: this.prevText,
8199             cls: "x-btn-icon x-grid-page-prev",
8200             disabled: true,
8201             handler: this.onClick.createDelegate(this, ["prev"])
8202         });
8203         //this.addSeparator();
8204         this.add(this.beforePageText);
8205         this.field = Roo.get(this.addDom({
8206            tag: "input",
8207            type: "text",
8208            size: "3",
8209            value: "1",
8210            cls: "x-grid-page-number"
8211         }).el);
8212         this.field.on("keydown", this.onPagingKeydown, this);
8213         this.field.on("focus", function(){this.dom.select();});
8214         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8215         this.field.setHeight(18);
8216         //this.addSeparator();
8217         this.next = this.addButton({
8218             tooltip: this.nextText,
8219             cls: "x-btn-icon x-grid-page-next",
8220             disabled: true,
8221             handler: this.onClick.createDelegate(this, ["next"])
8222         });
8223         this.last = this.addButton({
8224             tooltip: this.lastText,
8225             cls: "x-btn-icon x-grid-page-last",
8226             disabled: true,
8227             handler: this.onClick.createDelegate(this, ["last"])
8228         });
8229         //this.addSeparator();
8230         this.loading = this.addButton({
8231             tooltip: this.refreshText,
8232             cls: "x-btn-icon x-grid-loading",
8233             handler: this.onClick.createDelegate(this, ["refresh"])
8234         });
8235
8236         if(this.displayInfo){
8237             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8238         }
8239     },
8240
8241     // private
8242     updateInfo : function(){
8243         if(this.displayEl){
8244             var count = this.ds.getCount();
8245             var msg = count == 0 ?
8246                 this.emptyMsg :
8247                 String.format(
8248                     this.displayMsg,
8249                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8250                 );
8251             this.displayEl.update(msg);
8252         }
8253     },
8254
8255     // private
8256     onLoad : function(ds, r, o){
8257        this.cursor = o.params ? o.params.start : 0;
8258        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8259
8260        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8261        this.field.dom.value = ap;
8262        this.first.setDisabled(ap == 1);
8263        this.prev.setDisabled(ap == 1);
8264        this.next.setDisabled(ap == ps);
8265        this.last.setDisabled(ap == ps);
8266        this.loading.enable();
8267        this.updateInfo();
8268     },
8269
8270     // private
8271     getPageData : function(){
8272         var total = this.ds.getTotalCount();
8273         return {
8274             total : total,
8275             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8276             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8277         };
8278     },
8279
8280     // private
8281     onLoadError : function(){
8282         this.loading.enable();
8283     },
8284
8285     // private
8286     onPagingKeydown : function(e){
8287         var k = e.getKey();
8288         var d = this.getPageData();
8289         if(k == e.RETURN){
8290             var v = this.field.dom.value, pageNum;
8291             if(!v || isNaN(pageNum = parseInt(v, 10))){
8292                 this.field.dom.value = d.activePage;
8293                 return;
8294             }
8295             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8296             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8297             e.stopEvent();
8298         }
8299         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))
8300         {
8301           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8302           this.field.dom.value = pageNum;
8303           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8304           e.stopEvent();
8305         }
8306         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8307         {
8308           var v = this.field.dom.value, pageNum; 
8309           var increment = (e.shiftKey) ? 10 : 1;
8310           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8311             increment *= -1;
8312           }
8313           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8314             this.field.dom.value = d.activePage;
8315             return;
8316           }
8317           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8318           {
8319             this.field.dom.value = parseInt(v, 10) + increment;
8320             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8321             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8322           }
8323           e.stopEvent();
8324         }
8325     },
8326
8327     // private
8328     beforeLoad : function(){
8329         if(this.loading){
8330             this.loading.disable();
8331         }
8332     },
8333
8334     // private
8335     onClick : function(which){
8336         var ds = this.ds;
8337         switch(which){
8338             case "first":
8339                 ds.load({params:{start: 0, limit: this.pageSize}});
8340             break;
8341             case "prev":
8342                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8343             break;
8344             case "next":
8345                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8346             break;
8347             case "last":
8348                 var total = ds.getTotalCount();
8349                 var extra = total % this.pageSize;
8350                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8351                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8352             break;
8353             case "refresh":
8354                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8355             break;
8356         }
8357     },
8358
8359     /**
8360      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8361      * @param {Roo.data.Store} store The data store to unbind
8362      */
8363     unbind : function(ds){
8364         ds.un("beforeload", this.beforeLoad, this);
8365         ds.un("load", this.onLoad, this);
8366         ds.un("loadexception", this.onLoadError, this);
8367         ds.un("remove", this.updateInfo, this);
8368         ds.un("add", this.updateInfo, this);
8369         this.ds = undefined;
8370     },
8371
8372     /**
8373      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8374      * @param {Roo.data.Store} store The data store to bind
8375      */
8376     bind : function(ds){
8377         ds.on("beforeload", this.beforeLoad, this);
8378         ds.on("load", this.onLoad, this);
8379         ds.on("loadexception", this.onLoadError, this);
8380         ds.on("remove", this.updateInfo, this);
8381         ds.on("add", this.updateInfo, this);
8382         this.ds = ds;
8383     }
8384 });/*
8385  * Based on:
8386  * Ext JS Library 1.1.1
8387  * Copyright(c) 2006-2007, Ext JS, LLC.
8388  *
8389  * Originally Released Under LGPL - original licence link has changed is not relivant.
8390  *
8391  * Fork - LGPL
8392  * <script type="text/javascript">
8393  */
8394
8395 /**
8396  * @class Roo.Resizable
8397  * @extends Roo.util.Observable
8398  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8399  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8400  * 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
8401  * the element will be wrapped for you automatically.</p>
8402  * <p>Here is the list of valid resize handles:</p>
8403  * <pre>
8404 Value   Description
8405 ------  -------------------
8406  'n'     north
8407  's'     south
8408  'e'     east
8409  'w'     west
8410  'nw'    northwest
8411  'sw'    southwest
8412  'se'    southeast
8413  'ne'    northeast
8414  'hd'    horizontal drag
8415  'all'   all
8416 </pre>
8417  * <p>Here's an example showing the creation of a typical Resizable:</p>
8418  * <pre><code>
8419 var resizer = new Roo.Resizable("element-id", {
8420     handles: 'all',
8421     minWidth: 200,
8422     minHeight: 100,
8423     maxWidth: 500,
8424     maxHeight: 400,
8425     pinned: true
8426 });
8427 resizer.on("resize", myHandler);
8428 </code></pre>
8429  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8430  * resizer.east.setDisplayed(false);</p>
8431  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8432  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8433  * resize operation's new size (defaults to [0, 0])
8434  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8435  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8436  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8437  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8438  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8439  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8440  * @cfg {Number} width The width of the element in pixels (defaults to null)
8441  * @cfg {Number} height The height of the element in pixels (defaults to null)
8442  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8443  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8444  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8445  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8446  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8447  * in favor of the handles config option (defaults to false)
8448  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8449  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8450  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8451  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8452  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8453  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8454  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8455  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8456  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8457  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8458  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8459  * @constructor
8460  * Create a new resizable component
8461  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8462  * @param {Object} config configuration options
8463   */
8464 Roo.Resizable = function(el, config)
8465 {
8466     this.el = Roo.get(el);
8467
8468     if(config && config.wrap){
8469         config.resizeChild = this.el;
8470         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8471         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8472         this.el.setStyle("overflow", "hidden");
8473         this.el.setPositioning(config.resizeChild.getPositioning());
8474         config.resizeChild.clearPositioning();
8475         if(!config.width || !config.height){
8476             var csize = config.resizeChild.getSize();
8477             this.el.setSize(csize.width, csize.height);
8478         }
8479         if(config.pinned && !config.adjustments){
8480             config.adjustments = "auto";
8481         }
8482     }
8483
8484     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8485     this.proxy.unselectable();
8486     this.proxy.enableDisplayMode('block');
8487
8488     Roo.apply(this, config);
8489
8490     if(this.pinned){
8491         this.disableTrackOver = true;
8492         this.el.addClass("x-resizable-pinned");
8493     }
8494     // if the element isn't positioned, make it relative
8495     var position = this.el.getStyle("position");
8496     if(position != "absolute" && position != "fixed"){
8497         this.el.setStyle("position", "relative");
8498     }
8499     if(!this.handles){ // no handles passed, must be legacy style
8500         this.handles = 's,e,se';
8501         if(this.multiDirectional){
8502             this.handles += ',n,w';
8503         }
8504     }
8505     if(this.handles == "all"){
8506         this.handles = "n s e w ne nw se sw";
8507     }
8508     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8509     var ps = Roo.Resizable.positions;
8510     for(var i = 0, len = hs.length; i < len; i++){
8511         if(hs[i] && ps[hs[i]]){
8512             var pos = ps[hs[i]];
8513             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8514         }
8515     }
8516     // legacy
8517     this.corner = this.southeast;
8518     
8519     // updateBox = the box can move..
8520     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8521         this.updateBox = true;
8522     }
8523
8524     this.activeHandle = null;
8525
8526     if(this.resizeChild){
8527         if(typeof this.resizeChild == "boolean"){
8528             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8529         }else{
8530             this.resizeChild = Roo.get(this.resizeChild, true);
8531         }
8532     }
8533     
8534     if(this.adjustments == "auto"){
8535         var rc = this.resizeChild;
8536         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8537         if(rc && (hw || hn)){
8538             rc.position("relative");
8539             rc.setLeft(hw ? hw.el.getWidth() : 0);
8540             rc.setTop(hn ? hn.el.getHeight() : 0);
8541         }
8542         this.adjustments = [
8543             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8544             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8545         ];
8546     }
8547
8548     if(this.draggable){
8549         this.dd = this.dynamic ?
8550             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8551         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8552     }
8553
8554     // public events
8555     this.addEvents({
8556         /**
8557          * @event beforeresize
8558          * Fired before resize is allowed. Set enabled to false to cancel resize.
8559          * @param {Roo.Resizable} this
8560          * @param {Roo.EventObject} e The mousedown event
8561          */
8562         "beforeresize" : true,
8563         /**
8564          * @event resizing
8565          * Fired a resizing.
8566          * @param {Roo.Resizable} this
8567          * @param {Number} x The new x position
8568          * @param {Number} y The new y position
8569          * @param {Number} w The new w width
8570          * @param {Number} h The new h hight
8571          * @param {Roo.EventObject} e The mouseup event
8572          */
8573         "resizing" : true,
8574         /**
8575          * @event resize
8576          * Fired after a resize.
8577          * @param {Roo.Resizable} this
8578          * @param {Number} width The new width
8579          * @param {Number} height The new height
8580          * @param {Roo.EventObject} e The mouseup event
8581          */
8582         "resize" : true
8583     });
8584
8585     if(this.width !== null && this.height !== null){
8586         this.resizeTo(this.width, this.height);
8587     }else{
8588         this.updateChildSize();
8589     }
8590     if(Roo.isIE){
8591         this.el.dom.style.zoom = 1;
8592     }
8593     Roo.Resizable.superclass.constructor.call(this);
8594 };
8595
8596 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8597         resizeChild : false,
8598         adjustments : [0, 0],
8599         minWidth : 5,
8600         minHeight : 5,
8601         maxWidth : 10000,
8602         maxHeight : 10000,
8603         enabled : true,
8604         animate : false,
8605         duration : .35,
8606         dynamic : false,
8607         handles : false,
8608         multiDirectional : false,
8609         disableTrackOver : false,
8610         easing : 'easeOutStrong',
8611         widthIncrement : 0,
8612         heightIncrement : 0,
8613         pinned : false,
8614         width : null,
8615         height : null,
8616         preserveRatio : false,
8617         transparent: false,
8618         minX: 0,
8619         minY: 0,
8620         draggable: false,
8621
8622         /**
8623          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8624          */
8625         constrainTo: undefined,
8626         /**
8627          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8628          */
8629         resizeRegion: undefined,
8630
8631
8632     /**
8633      * Perform a manual resize
8634      * @param {Number} width
8635      * @param {Number} height
8636      */
8637     resizeTo : function(width, height){
8638         this.el.setSize(width, height);
8639         this.updateChildSize();
8640         this.fireEvent("resize", this, width, height, null);
8641     },
8642
8643     // private
8644     startSizing : function(e, handle){
8645         this.fireEvent("beforeresize", this, e);
8646         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8647
8648             if(!this.overlay){
8649                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8650                 this.overlay.unselectable();
8651                 this.overlay.enableDisplayMode("block");
8652                 this.overlay.on("mousemove", this.onMouseMove, this);
8653                 this.overlay.on("mouseup", this.onMouseUp, this);
8654             }
8655             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8656
8657             this.resizing = true;
8658             this.startBox = this.el.getBox();
8659             this.startPoint = e.getXY();
8660             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8661                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8662
8663             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8664             this.overlay.show();
8665
8666             if(this.constrainTo) {
8667                 var ct = Roo.get(this.constrainTo);
8668                 this.resizeRegion = ct.getRegion().adjust(
8669                     ct.getFrameWidth('t'),
8670                     ct.getFrameWidth('l'),
8671                     -ct.getFrameWidth('b'),
8672                     -ct.getFrameWidth('r')
8673                 );
8674             }
8675
8676             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8677             this.proxy.show();
8678             this.proxy.setBox(this.startBox);
8679             if(!this.dynamic){
8680                 this.proxy.setStyle('visibility', 'visible');
8681             }
8682         }
8683     },
8684
8685     // private
8686     onMouseDown : function(handle, e){
8687         if(this.enabled){
8688             e.stopEvent();
8689             this.activeHandle = handle;
8690             this.startSizing(e, handle);
8691         }
8692     },
8693
8694     // private
8695     onMouseUp : function(e){
8696         var size = this.resizeElement();
8697         this.resizing = false;
8698         this.handleOut();
8699         this.overlay.hide();
8700         this.proxy.hide();
8701         this.fireEvent("resize", this, size.width, size.height, e);
8702     },
8703
8704     // private
8705     updateChildSize : function(){
8706         
8707         if(this.resizeChild){
8708             var el = this.el;
8709             var child = this.resizeChild;
8710             var adj = this.adjustments;
8711             if(el.dom.offsetWidth){
8712                 var b = el.getSize(true);
8713                 child.setSize(b.width+adj[0], b.height+adj[1]);
8714             }
8715             // Second call here for IE
8716             // The first call enables instant resizing and
8717             // the second call corrects scroll bars if they
8718             // exist
8719             if(Roo.isIE){
8720                 setTimeout(function(){
8721                     if(el.dom.offsetWidth){
8722                         var b = el.getSize(true);
8723                         child.setSize(b.width+adj[0], b.height+adj[1]);
8724                     }
8725                 }, 10);
8726             }
8727         }
8728     },
8729
8730     // private
8731     snap : function(value, inc, min){
8732         if(!inc || !value) {
8733             return value;
8734         }
8735         var newValue = value;
8736         var m = value % inc;
8737         if(m > 0){
8738             if(m > (inc/2)){
8739                 newValue = value + (inc-m);
8740             }else{
8741                 newValue = value - m;
8742             }
8743         }
8744         return Math.max(min, newValue);
8745     },
8746
8747     // private
8748     resizeElement : function(){
8749         var box = this.proxy.getBox();
8750         if(this.updateBox){
8751             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8752         }else{
8753             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8754         }
8755         this.updateChildSize();
8756         if(!this.dynamic){
8757             this.proxy.hide();
8758         }
8759         return box;
8760     },
8761
8762     // private
8763     constrain : function(v, diff, m, mx){
8764         if(v - diff < m){
8765             diff = v - m;
8766         }else if(v - diff > mx){
8767             diff = mx - v;
8768         }
8769         return diff;
8770     },
8771
8772     // private
8773     onMouseMove : function(e){
8774         
8775         if(this.enabled){
8776             try{// try catch so if something goes wrong the user doesn't get hung
8777
8778             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8779                 return;
8780             }
8781
8782             //var curXY = this.startPoint;
8783             var curSize = this.curSize || this.startBox;
8784             var x = this.startBox.x, y = this.startBox.y;
8785             var ox = x, oy = y;
8786             var w = curSize.width, h = curSize.height;
8787             var ow = w, oh = h;
8788             var mw = this.minWidth, mh = this.minHeight;
8789             var mxw = this.maxWidth, mxh = this.maxHeight;
8790             var wi = this.widthIncrement;
8791             var hi = this.heightIncrement;
8792
8793             var eventXY = e.getXY();
8794             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8795             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8796
8797             var pos = this.activeHandle.position;
8798
8799             switch(pos){
8800                 case "east":
8801                     w += diffX;
8802                     w = Math.min(Math.max(mw, w), mxw);
8803                     break;
8804              
8805                 case "south":
8806                     h += diffY;
8807                     h = Math.min(Math.max(mh, h), mxh);
8808                     break;
8809                 case "southeast":
8810                     w += diffX;
8811                     h += diffY;
8812                     w = Math.min(Math.max(mw, w), mxw);
8813                     h = Math.min(Math.max(mh, h), mxh);
8814                     break;
8815                 case "north":
8816                     diffY = this.constrain(h, diffY, mh, mxh);
8817                     y += diffY;
8818                     h -= diffY;
8819                     break;
8820                 case "hdrag":
8821                     
8822                     if (wi) {
8823                         var adiffX = Math.abs(diffX);
8824                         var sub = (adiffX % wi); // how much 
8825                         if (sub > (wi/2)) { // far enough to snap
8826                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8827                         } else {
8828                             // remove difference.. 
8829                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8830                         }
8831                     }
8832                     x += diffX;
8833                     x = Math.max(this.minX, x);
8834                     break;
8835                 case "west":
8836                     diffX = this.constrain(w, diffX, mw, mxw);
8837                     x += diffX;
8838                     w -= diffX;
8839                     break;
8840                 case "northeast":
8841                     w += diffX;
8842                     w = Math.min(Math.max(mw, w), mxw);
8843                     diffY = this.constrain(h, diffY, mh, mxh);
8844                     y += diffY;
8845                     h -= diffY;
8846                     break;
8847                 case "northwest":
8848                     diffX = this.constrain(w, diffX, mw, mxw);
8849                     diffY = this.constrain(h, diffY, mh, mxh);
8850                     y += diffY;
8851                     h -= diffY;
8852                     x += diffX;
8853                     w -= diffX;
8854                     break;
8855                case "southwest":
8856                     diffX = this.constrain(w, diffX, mw, mxw);
8857                     h += diffY;
8858                     h = Math.min(Math.max(mh, h), mxh);
8859                     x += diffX;
8860                     w -= diffX;
8861                     break;
8862             }
8863
8864             var sw = this.snap(w, wi, mw);
8865             var sh = this.snap(h, hi, mh);
8866             if(sw != w || sh != h){
8867                 switch(pos){
8868                     case "northeast":
8869                         y -= sh - h;
8870                     break;
8871                     case "north":
8872                         y -= sh - h;
8873                         break;
8874                     case "southwest":
8875                         x -= sw - w;
8876                     break;
8877                     case "west":
8878                         x -= sw - w;
8879                         break;
8880                     case "northwest":
8881                         x -= sw - w;
8882                         y -= sh - h;
8883                     break;
8884                 }
8885                 w = sw;
8886                 h = sh;
8887             }
8888
8889             if(this.preserveRatio){
8890                 switch(pos){
8891                     case "southeast":
8892                     case "east":
8893                         h = oh * (w/ow);
8894                         h = Math.min(Math.max(mh, h), mxh);
8895                         w = ow * (h/oh);
8896                        break;
8897                     case "south":
8898                         w = ow * (h/oh);
8899                         w = Math.min(Math.max(mw, w), mxw);
8900                         h = oh * (w/ow);
8901                         break;
8902                     case "northeast":
8903                         w = ow * (h/oh);
8904                         w = Math.min(Math.max(mw, w), mxw);
8905                         h = oh * (w/ow);
8906                     break;
8907                     case "north":
8908                         var tw = w;
8909                         w = ow * (h/oh);
8910                         w = Math.min(Math.max(mw, w), mxw);
8911                         h = oh * (w/ow);
8912                         x += (tw - w) / 2;
8913                         break;
8914                     case "southwest":
8915                         h = oh * (w/ow);
8916                         h = Math.min(Math.max(mh, h), mxh);
8917                         var tw = w;
8918                         w = ow * (h/oh);
8919                         x += tw - w;
8920                         break;
8921                     case "west":
8922                         var th = h;
8923                         h = oh * (w/ow);
8924                         h = Math.min(Math.max(mh, h), mxh);
8925                         y += (th - h) / 2;
8926                         var tw = w;
8927                         w = ow * (h/oh);
8928                         x += tw - w;
8929                        break;
8930                     case "northwest":
8931                         var tw = w;
8932                         var th = h;
8933                         h = oh * (w/ow);
8934                         h = Math.min(Math.max(mh, h), mxh);
8935                         w = ow * (h/oh);
8936                         y += th - h;
8937                         x += tw - w;
8938                        break;
8939
8940                 }
8941             }
8942             if (pos == 'hdrag') {
8943                 w = ow;
8944             }
8945             this.proxy.setBounds(x, y, w, h);
8946             if(this.dynamic){
8947                 this.resizeElement();
8948             }
8949             }catch(e){}
8950         }
8951         this.fireEvent("resizing", this, x, y, w, h, e);
8952     },
8953
8954     // private
8955     handleOver : function(){
8956         if(this.enabled){
8957             this.el.addClass("x-resizable-over");
8958         }
8959     },
8960
8961     // private
8962     handleOut : function(){
8963         if(!this.resizing){
8964             this.el.removeClass("x-resizable-over");
8965         }
8966     },
8967
8968     /**
8969      * Returns the element this component is bound to.
8970      * @return {Roo.Element}
8971      */
8972     getEl : function(){
8973         return this.el;
8974     },
8975
8976     /**
8977      * Returns the resizeChild element (or null).
8978      * @return {Roo.Element}
8979      */
8980     getResizeChild : function(){
8981         return this.resizeChild;
8982     },
8983     groupHandler : function()
8984     {
8985         
8986     },
8987     /**
8988      * Destroys this resizable. If the element was wrapped and
8989      * removeEl is not true then the element remains.
8990      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8991      */
8992     destroy : function(removeEl){
8993         this.proxy.remove();
8994         if(this.overlay){
8995             this.overlay.removeAllListeners();
8996             this.overlay.remove();
8997         }
8998         var ps = Roo.Resizable.positions;
8999         for(var k in ps){
9000             if(typeof ps[k] != "function" && this[ps[k]]){
9001                 var h = this[ps[k]];
9002                 h.el.removeAllListeners();
9003                 h.el.remove();
9004             }
9005         }
9006         if(removeEl){
9007             this.el.update("");
9008             this.el.remove();
9009         }
9010     }
9011 });
9012
9013 // private
9014 // hash to map config positions to true positions
9015 Roo.Resizable.positions = {
9016     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9017     hd: "hdrag"
9018 };
9019
9020 // private
9021 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9022     if(!this.tpl){
9023         // only initialize the template if resizable is used
9024         var tpl = Roo.DomHelper.createTemplate(
9025             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9026         );
9027         tpl.compile();
9028         Roo.Resizable.Handle.prototype.tpl = tpl;
9029     }
9030     this.position = pos;
9031     this.rz = rz;
9032     // show north drag fro topdra
9033     var handlepos = pos == 'hdrag' ? 'north' : pos;
9034     
9035     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9036     if (pos == 'hdrag') {
9037         this.el.setStyle('cursor', 'pointer');
9038     }
9039     this.el.unselectable();
9040     if(transparent){
9041         this.el.setOpacity(0);
9042     }
9043     this.el.on("mousedown", this.onMouseDown, this);
9044     if(!disableTrackOver){
9045         this.el.on("mouseover", this.onMouseOver, this);
9046         this.el.on("mouseout", this.onMouseOut, this);
9047     }
9048 };
9049
9050 // private
9051 Roo.Resizable.Handle.prototype = {
9052     afterResize : function(rz){
9053         Roo.log('after?');
9054         // do nothing
9055     },
9056     // private
9057     onMouseDown : function(e){
9058         this.rz.onMouseDown(this, e);
9059     },
9060     // private
9061     onMouseOver : function(e){
9062         this.rz.handleOver(this, e);
9063     },
9064     // private
9065     onMouseOut : function(e){
9066         this.rz.handleOut(this, e);
9067     }
9068 };/*
9069  * Based on:
9070  * Ext JS Library 1.1.1
9071  * Copyright(c) 2006-2007, Ext JS, LLC.
9072  *
9073  * Originally Released Under LGPL - original licence link has changed is not relivant.
9074  *
9075  * Fork - LGPL
9076  * <script type="text/javascript">
9077  */
9078
9079 /**
9080  * @class Roo.Editor
9081  * @extends Roo.Component
9082  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9083  * @constructor
9084  * Create a new Editor
9085  * @param {Roo.form.Field} field The Field object (or descendant)
9086  * @param {Object} config The config object
9087  */
9088 Roo.Editor = function(field, config){
9089     Roo.Editor.superclass.constructor.call(this, config);
9090     this.field = field;
9091     this.addEvents({
9092         /**
9093              * @event beforestartedit
9094              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9095              * false from the handler of this event.
9096              * @param {Editor} this
9097              * @param {Roo.Element} boundEl The underlying element bound to this editor
9098              * @param {Mixed} value The field value being set
9099              */
9100         "beforestartedit" : true,
9101         /**
9102              * @event startedit
9103              * Fires when this editor is displayed
9104              * @param {Roo.Element} boundEl The underlying element bound to this editor
9105              * @param {Mixed} value The starting field value
9106              */
9107         "startedit" : true,
9108         /**
9109              * @event beforecomplete
9110              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9111              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9112              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9113              * event will not fire since no edit actually occurred.
9114              * @param {Editor} this
9115              * @param {Mixed} value The current field value
9116              * @param {Mixed} startValue The original field value
9117              */
9118         "beforecomplete" : true,
9119         /**
9120              * @event complete
9121              * Fires after editing is complete and any changed value has been written to the underlying field.
9122              * @param {Editor} this
9123              * @param {Mixed} value The current field value
9124              * @param {Mixed} startValue The original field value
9125              */
9126         "complete" : true,
9127         /**
9128          * @event specialkey
9129          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9130          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9131          * @param {Roo.form.Field} this
9132          * @param {Roo.EventObject} e The event object
9133          */
9134         "specialkey" : true
9135     });
9136 };
9137
9138 Roo.extend(Roo.Editor, Roo.Component, {
9139     /**
9140      * @cfg {Boolean/String} autosize
9141      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9142      * or "height" to adopt the height only (defaults to false)
9143      */
9144     /**
9145      * @cfg {Boolean} revertInvalid
9146      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9147      * validation fails (defaults to true)
9148      */
9149     /**
9150      * @cfg {Boolean} ignoreNoChange
9151      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9152      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9153      * will never be ignored.
9154      */
9155     /**
9156      * @cfg {Boolean} hideEl
9157      * False to keep the bound element visible while the editor is displayed (defaults to true)
9158      */
9159     /**
9160      * @cfg {Mixed} value
9161      * The data value of the underlying field (defaults to "")
9162      */
9163     value : "",
9164     /**
9165      * @cfg {String} alignment
9166      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9167      */
9168     alignment: "c-c?",
9169     /**
9170      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9171      * for bottom-right shadow (defaults to "frame")
9172      */
9173     shadow : "frame",
9174     /**
9175      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9176      */
9177     constrain : false,
9178     /**
9179      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9180      */
9181     completeOnEnter : false,
9182     /**
9183      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9184      */
9185     cancelOnEsc : false,
9186     /**
9187      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9188      */
9189     updateEl : false,
9190
9191     // private
9192     onRender : function(ct, position){
9193         this.el = new Roo.Layer({
9194             shadow: this.shadow,
9195             cls: "x-editor",
9196             parentEl : ct,
9197             shim : this.shim,
9198             shadowOffset:4,
9199             id: this.id,
9200             constrain: this.constrain
9201         });
9202         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9203         if(this.field.msgTarget != 'title'){
9204             this.field.msgTarget = 'qtip';
9205         }
9206         this.field.render(this.el);
9207         if(Roo.isGecko){
9208             this.field.el.dom.setAttribute('autocomplete', 'off');
9209         }
9210         this.field.on("specialkey", this.onSpecialKey, this);
9211         if(this.swallowKeys){
9212             this.field.el.swallowEvent(['keydown','keypress']);
9213         }
9214         this.field.show();
9215         this.field.on("blur", this.onBlur, this);
9216         if(this.field.grow){
9217             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9218         }
9219     },
9220
9221     onSpecialKey : function(field, e)
9222     {
9223         //Roo.log('editor onSpecialKey');
9224         if(this.completeOnEnter && e.getKey() == e.ENTER){
9225             e.stopEvent();
9226             this.completeEdit();
9227             return;
9228         }
9229         // do not fire special key otherwise it might hide close the editor...
9230         if(e.getKey() == e.ENTER){    
9231             return;
9232         }
9233         if(this.cancelOnEsc && e.getKey() == e.ESC){
9234             this.cancelEdit();
9235             return;
9236         } 
9237         this.fireEvent('specialkey', field, e);
9238     
9239     },
9240
9241     /**
9242      * Starts the editing process and shows the editor.
9243      * @param {String/HTMLElement/Element} el The element to edit
9244      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9245       * to the innerHTML of el.
9246      */
9247     startEdit : function(el, value){
9248         if(this.editing){
9249             this.completeEdit();
9250         }
9251         this.boundEl = Roo.get(el);
9252         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9253         if(!this.rendered){
9254             this.render(this.parentEl || document.body);
9255         }
9256         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9257             return;
9258         }
9259         this.startValue = v;
9260         this.field.setValue(v);
9261         if(this.autoSize){
9262             var sz = this.boundEl.getSize();
9263             switch(this.autoSize){
9264                 case "width":
9265                 this.setSize(sz.width,  "");
9266                 break;
9267                 case "height":
9268                 this.setSize("",  sz.height);
9269                 break;
9270                 default:
9271                 this.setSize(sz.width,  sz.height);
9272             }
9273         }
9274         this.el.alignTo(this.boundEl, this.alignment);
9275         this.editing = true;
9276         if(Roo.QuickTips){
9277             Roo.QuickTips.disable();
9278         }
9279         this.show();
9280     },
9281
9282     /**
9283      * Sets the height and width of this editor.
9284      * @param {Number} width The new width
9285      * @param {Number} height The new height
9286      */
9287     setSize : function(w, h){
9288         this.field.setSize(w, h);
9289         if(this.el){
9290             this.el.sync();
9291         }
9292     },
9293
9294     /**
9295      * Realigns the editor to the bound field based on the current alignment config value.
9296      */
9297     realign : function(){
9298         this.el.alignTo(this.boundEl, this.alignment);
9299     },
9300
9301     /**
9302      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9303      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9304      */
9305     completeEdit : function(remainVisible){
9306         if(!this.editing){
9307             return;
9308         }
9309         var v = this.getValue();
9310         if(this.revertInvalid !== false && !this.field.isValid()){
9311             v = this.startValue;
9312             this.cancelEdit(true);
9313         }
9314         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9315             this.editing = false;
9316             this.hide();
9317             return;
9318         }
9319         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9320             this.editing = false;
9321             if(this.updateEl && this.boundEl){
9322                 this.boundEl.update(v);
9323             }
9324             if(remainVisible !== true){
9325                 this.hide();
9326             }
9327             this.fireEvent("complete", this, v, this.startValue);
9328         }
9329     },
9330
9331     // private
9332     onShow : function(){
9333         this.el.show();
9334         if(this.hideEl !== false){
9335             this.boundEl.hide();
9336         }
9337         this.field.show();
9338         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9339             this.fixIEFocus = true;
9340             this.deferredFocus.defer(50, this);
9341         }else{
9342             this.field.focus();
9343         }
9344         this.fireEvent("startedit", this.boundEl, this.startValue);
9345     },
9346
9347     deferredFocus : function(){
9348         if(this.editing){
9349             this.field.focus();
9350         }
9351     },
9352
9353     /**
9354      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9355      * reverted to the original starting value.
9356      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9357      * cancel (defaults to false)
9358      */
9359     cancelEdit : function(remainVisible){
9360         if(this.editing){
9361             this.setValue(this.startValue);
9362             if(remainVisible !== true){
9363                 this.hide();
9364             }
9365         }
9366     },
9367
9368     // private
9369     onBlur : function(){
9370         if(this.allowBlur !== true && this.editing){
9371             this.completeEdit();
9372         }
9373     },
9374
9375     // private
9376     onHide : function(){
9377         if(this.editing){
9378             this.completeEdit();
9379             return;
9380         }
9381         this.field.blur();
9382         if(this.field.collapse){
9383             this.field.collapse();
9384         }
9385         this.el.hide();
9386         if(this.hideEl !== false){
9387             this.boundEl.show();
9388         }
9389         if(Roo.QuickTips){
9390             Roo.QuickTips.enable();
9391         }
9392     },
9393
9394     /**
9395      * Sets the data value of the editor
9396      * @param {Mixed} value Any valid value supported by the underlying field
9397      */
9398     setValue : function(v){
9399         this.field.setValue(v);
9400     },
9401
9402     /**
9403      * Gets the data value of the editor
9404      * @return {Mixed} The data value
9405      */
9406     getValue : function(){
9407         return this.field.getValue();
9408     }
9409 });/*
9410  * Based on:
9411  * Ext JS Library 1.1.1
9412  * Copyright(c) 2006-2007, Ext JS, LLC.
9413  *
9414  * Originally Released Under LGPL - original licence link has changed is not relivant.
9415  *
9416  * Fork - LGPL
9417  * <script type="text/javascript">
9418  */
9419  
9420 /**
9421  * @class Roo.BasicDialog
9422  * @extends Roo.util.Observable
9423  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9424  * <pre><code>
9425 var dlg = new Roo.BasicDialog("my-dlg", {
9426     height: 200,
9427     width: 300,
9428     minHeight: 100,
9429     minWidth: 150,
9430     modal: true,
9431     proxyDrag: true,
9432     shadow: true
9433 });
9434 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9435 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9436 dlg.addButton('Cancel', dlg.hide, dlg);
9437 dlg.show();
9438 </code></pre>
9439   <b>A Dialog should always be a direct child of the body element.</b>
9440  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9441  * @cfg {String} title Default text to display in the title bar (defaults to null)
9442  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9443  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9444  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9445  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9446  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9447  * (defaults to null with no animation)
9448  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9449  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9450  * property for valid values (defaults to 'all')
9451  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9452  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9453  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9454  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9455  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9456  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9457  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9458  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9459  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9460  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9461  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9462  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9463  * draggable = true (defaults to false)
9464  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9465  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9466  * shadow (defaults to false)
9467  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9468  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9469  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9470  * @cfg {Array} buttons Array of buttons
9471  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9472  * @constructor
9473  * Create a new BasicDialog.
9474  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9475  * @param {Object} config Configuration options
9476  */
9477 Roo.BasicDialog = function(el, config){
9478     this.el = Roo.get(el);
9479     var dh = Roo.DomHelper;
9480     if(!this.el && config && config.autoCreate){
9481         if(typeof config.autoCreate == "object"){
9482             if(!config.autoCreate.id){
9483                 config.autoCreate.id = el;
9484             }
9485             this.el = dh.append(document.body,
9486                         config.autoCreate, true);
9487         }else{
9488             this.el = dh.append(document.body,
9489                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9490         }
9491     }
9492     el = this.el;
9493     el.setDisplayed(true);
9494     el.hide = this.hideAction;
9495     this.id = el.id;
9496     el.addClass("x-dlg");
9497
9498     Roo.apply(this, config);
9499
9500     this.proxy = el.createProxy("x-dlg-proxy");
9501     this.proxy.hide = this.hideAction;
9502     this.proxy.setOpacity(.5);
9503     this.proxy.hide();
9504
9505     if(config.width){
9506         el.setWidth(config.width);
9507     }
9508     if(config.height){
9509         el.setHeight(config.height);
9510     }
9511     this.size = el.getSize();
9512     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9513         this.xy = [config.x,config.y];
9514     }else{
9515         this.xy = el.getCenterXY(true);
9516     }
9517     /** The header element @type Roo.Element */
9518     this.header = el.child("> .x-dlg-hd");
9519     /** The body element @type Roo.Element */
9520     this.body = el.child("> .x-dlg-bd");
9521     /** The footer element @type Roo.Element */
9522     this.footer = el.child("> .x-dlg-ft");
9523
9524     if(!this.header){
9525         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9526     }
9527     if(!this.body){
9528         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9529     }
9530
9531     this.header.unselectable();
9532     if(this.title){
9533         this.header.update(this.title);
9534     }
9535     // this element allows the dialog to be focused for keyboard event
9536     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9537     this.focusEl.swallowEvent("click", true);
9538
9539     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9540
9541     // wrap the body and footer for special rendering
9542     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9543     if(this.footer){
9544         this.bwrap.dom.appendChild(this.footer.dom);
9545     }
9546
9547     this.bg = this.el.createChild({
9548         tag: "div", cls:"x-dlg-bg",
9549         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9550     });
9551     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9552
9553
9554     if(this.autoScroll !== false && !this.autoTabs){
9555         this.body.setStyle("overflow", "auto");
9556     }
9557
9558     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9559
9560     if(this.closable !== false){
9561         this.el.addClass("x-dlg-closable");
9562         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9563         this.close.on("click", this.closeClick, this);
9564         this.close.addClassOnOver("x-dlg-close-over");
9565     }
9566     if(this.collapsible !== false){
9567         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9568         this.collapseBtn.on("click", this.collapseClick, this);
9569         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9570         this.header.on("dblclick", this.collapseClick, this);
9571     }
9572     if(this.resizable !== false){
9573         this.el.addClass("x-dlg-resizable");
9574         this.resizer = new Roo.Resizable(el, {
9575             minWidth: this.minWidth || 80,
9576             minHeight:this.minHeight || 80,
9577             handles: this.resizeHandles || "all",
9578             pinned: true
9579         });
9580         this.resizer.on("beforeresize", this.beforeResize, this);
9581         this.resizer.on("resize", this.onResize, this);
9582     }
9583     if(this.draggable !== false){
9584         el.addClass("x-dlg-draggable");
9585         if (!this.proxyDrag) {
9586             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9587         }
9588         else {
9589             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9590         }
9591         dd.setHandleElId(this.header.id);
9592         dd.endDrag = this.endMove.createDelegate(this);
9593         dd.startDrag = this.startMove.createDelegate(this);
9594         dd.onDrag = this.onDrag.createDelegate(this);
9595         dd.scroll = false;
9596         this.dd = dd;
9597     }
9598     if(this.modal){
9599         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9600         this.mask.enableDisplayMode("block");
9601         this.mask.hide();
9602         this.el.addClass("x-dlg-modal");
9603     }
9604     if(this.shadow){
9605         this.shadow = new Roo.Shadow({
9606             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9607             offset : this.shadowOffset
9608         });
9609     }else{
9610         this.shadowOffset = 0;
9611     }
9612     if(Roo.useShims && this.shim !== false){
9613         this.shim = this.el.createShim();
9614         this.shim.hide = this.hideAction;
9615         this.shim.hide();
9616     }else{
9617         this.shim = false;
9618     }
9619     if(this.autoTabs){
9620         this.initTabs();
9621     }
9622     if (this.buttons) { 
9623         var bts= this.buttons;
9624         this.buttons = [];
9625         Roo.each(bts, function(b) {
9626             this.addButton(b);
9627         }, this);
9628     }
9629     
9630     
9631     this.addEvents({
9632         /**
9633          * @event keydown
9634          * Fires when a key is pressed
9635          * @param {Roo.BasicDialog} this
9636          * @param {Roo.EventObject} e
9637          */
9638         "keydown" : true,
9639         /**
9640          * @event move
9641          * Fires when this dialog is moved by the user.
9642          * @param {Roo.BasicDialog} this
9643          * @param {Number} x The new page X
9644          * @param {Number} y The new page Y
9645          */
9646         "move" : true,
9647         /**
9648          * @event resize
9649          * Fires when this dialog is resized by the user.
9650          * @param {Roo.BasicDialog} this
9651          * @param {Number} width The new width
9652          * @param {Number} height The new height
9653          */
9654         "resize" : true,
9655         /**
9656          * @event beforehide
9657          * Fires before this dialog is hidden.
9658          * @param {Roo.BasicDialog} this
9659          */
9660         "beforehide" : true,
9661         /**
9662          * @event hide
9663          * Fires when this dialog is hidden.
9664          * @param {Roo.BasicDialog} this
9665          */
9666         "hide" : true,
9667         /**
9668          * @event beforeshow
9669          * Fires before this dialog is shown.
9670          * @param {Roo.BasicDialog} this
9671          */
9672         "beforeshow" : true,
9673         /**
9674          * @event show
9675          * Fires when this dialog is shown.
9676          * @param {Roo.BasicDialog} this
9677          */
9678         "show" : true
9679     });
9680     el.on("keydown", this.onKeyDown, this);
9681     el.on("mousedown", this.toFront, this);
9682     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9683     this.el.hide();
9684     Roo.DialogManager.register(this);
9685     Roo.BasicDialog.superclass.constructor.call(this);
9686 };
9687
9688 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9689     shadowOffset: Roo.isIE ? 6 : 5,
9690     minHeight: 80,
9691     minWidth: 200,
9692     minButtonWidth: 75,
9693     defaultButton: null,
9694     buttonAlign: "right",
9695     tabTag: 'div',
9696     firstShow: true,
9697
9698     /**
9699      * Sets the dialog title text
9700      * @param {String} text The title text to display
9701      * @return {Roo.BasicDialog} this
9702      */
9703     setTitle : function(text){
9704         this.header.update(text);
9705         return this;
9706     },
9707
9708     // private
9709     closeClick : function(){
9710         this.hide();
9711     },
9712
9713     // private
9714     collapseClick : function(){
9715         this[this.collapsed ? "expand" : "collapse"]();
9716     },
9717
9718     /**
9719      * Collapses the dialog to its minimized state (only the title bar is visible).
9720      * Equivalent to the user clicking the collapse dialog button.
9721      */
9722     collapse : function(){
9723         if(!this.collapsed){
9724             this.collapsed = true;
9725             this.el.addClass("x-dlg-collapsed");
9726             this.restoreHeight = this.el.getHeight();
9727             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9728         }
9729     },
9730
9731     /**
9732      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9733      * clicking the expand dialog button.
9734      */
9735     expand : function(){
9736         if(this.collapsed){
9737             this.collapsed = false;
9738             this.el.removeClass("x-dlg-collapsed");
9739             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9740         }
9741     },
9742
9743     /**
9744      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9745      * @return {Roo.TabPanel} The tabs component
9746      */
9747     initTabs : function(){
9748         var tabs = this.getTabs();
9749         while(tabs.getTab(0)){
9750             tabs.removeTab(0);
9751         }
9752         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9753             var dom = el.dom;
9754             tabs.addTab(Roo.id(dom), dom.title);
9755             dom.title = "";
9756         });
9757         tabs.activate(0);
9758         return tabs;
9759     },
9760
9761     // private
9762     beforeResize : function(){
9763         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9764     },
9765
9766     // private
9767     onResize : function(){
9768         this.refreshSize();
9769         this.syncBodyHeight();
9770         this.adjustAssets();
9771         this.focus();
9772         this.fireEvent("resize", this, this.size.width, this.size.height);
9773     },
9774
9775     // private
9776     onKeyDown : function(e){
9777         if(this.isVisible()){
9778             this.fireEvent("keydown", this, e);
9779         }
9780     },
9781
9782     /**
9783      * Resizes the dialog.
9784      * @param {Number} width
9785      * @param {Number} height
9786      * @return {Roo.BasicDialog} this
9787      */
9788     resizeTo : function(width, height){
9789         this.el.setSize(width, height);
9790         this.size = {width: width, height: height};
9791         this.syncBodyHeight();
9792         if(this.fixedcenter){
9793             this.center();
9794         }
9795         if(this.isVisible()){
9796             this.constrainXY();
9797             this.adjustAssets();
9798         }
9799         this.fireEvent("resize", this, width, height);
9800         return this;
9801     },
9802
9803
9804     /**
9805      * Resizes the dialog to fit the specified content size.
9806      * @param {Number} width
9807      * @param {Number} height
9808      * @return {Roo.BasicDialog} this
9809      */
9810     setContentSize : function(w, h){
9811         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9812         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9813         //if(!this.el.isBorderBox()){
9814             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9815             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9816         //}
9817         if(this.tabs){
9818             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9819             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9820         }
9821         this.resizeTo(w, h);
9822         return this;
9823     },
9824
9825     /**
9826      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9827      * executed in response to a particular key being pressed while the dialog is active.
9828      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9829      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9830      * @param {Function} fn The function to call
9831      * @param {Object} scope (optional) The scope of the function
9832      * @return {Roo.BasicDialog} this
9833      */
9834     addKeyListener : function(key, fn, scope){
9835         var keyCode, shift, ctrl, alt;
9836         if(typeof key == "object" && !(key instanceof Array)){
9837             keyCode = key["key"];
9838             shift = key["shift"];
9839             ctrl = key["ctrl"];
9840             alt = key["alt"];
9841         }else{
9842             keyCode = key;
9843         }
9844         var handler = function(dlg, e){
9845             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9846                 var k = e.getKey();
9847                 if(keyCode instanceof Array){
9848                     for(var i = 0, len = keyCode.length; i < len; i++){
9849                         if(keyCode[i] == k){
9850                           fn.call(scope || window, dlg, k, e);
9851                           return;
9852                         }
9853                     }
9854                 }else{
9855                     if(k == keyCode){
9856                         fn.call(scope || window, dlg, k, e);
9857                     }
9858                 }
9859             }
9860         };
9861         this.on("keydown", handler);
9862         return this;
9863     },
9864
9865     /**
9866      * Returns the TabPanel component (creates it if it doesn't exist).
9867      * Note: If you wish to simply check for the existence of tabs without creating them,
9868      * check for a null 'tabs' property.
9869      * @return {Roo.TabPanel} The tabs component
9870      */
9871     getTabs : function(){
9872         if(!this.tabs){
9873             this.el.addClass("x-dlg-auto-tabs");
9874             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9875             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9876         }
9877         return this.tabs;
9878     },
9879
9880     /**
9881      * Adds a button to the footer section of the dialog.
9882      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9883      * object or a valid Roo.DomHelper element config
9884      * @param {Function} handler The function called when the button is clicked
9885      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9886      * @return {Roo.Button} The new button
9887      */
9888     addButton : function(config, handler, scope){
9889         var dh = Roo.DomHelper;
9890         if(!this.footer){
9891             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9892         }
9893         if(!this.btnContainer){
9894             var tb = this.footer.createChild({
9895
9896                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9897                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9898             }, null, true);
9899             this.btnContainer = tb.firstChild.firstChild.firstChild;
9900         }
9901         var bconfig = {
9902             handler: handler,
9903             scope: scope,
9904             minWidth: this.minButtonWidth,
9905             hideParent:true
9906         };
9907         if(typeof config == "string"){
9908             bconfig.text = config;
9909         }else{
9910             if(config.tag){
9911                 bconfig.dhconfig = config;
9912             }else{
9913                 Roo.apply(bconfig, config);
9914             }
9915         }
9916         var fc = false;
9917         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9918             bconfig.position = Math.max(0, bconfig.position);
9919             fc = this.btnContainer.childNodes[bconfig.position];
9920         }
9921          
9922         var btn = new Roo.Button(
9923             fc ? 
9924                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9925                 : this.btnContainer.appendChild(document.createElement("td")),
9926             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9927             bconfig
9928         );
9929         this.syncBodyHeight();
9930         if(!this.buttons){
9931             /**
9932              * Array of all the buttons that have been added to this dialog via addButton
9933              * @type Array
9934              */
9935             this.buttons = [];
9936         }
9937         this.buttons.push(btn);
9938         return btn;
9939     },
9940
9941     /**
9942      * Sets the default button to be focused when the dialog is displayed.
9943      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9944      * @return {Roo.BasicDialog} this
9945      */
9946     setDefaultButton : function(btn){
9947         this.defaultButton = btn;
9948         return this;
9949     },
9950
9951     // private
9952     getHeaderFooterHeight : function(safe){
9953         var height = 0;
9954         if(this.header){
9955            height += this.header.getHeight();
9956         }
9957         if(this.footer){
9958            var fm = this.footer.getMargins();
9959             height += (this.footer.getHeight()+fm.top+fm.bottom);
9960         }
9961         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9962         height += this.centerBg.getPadding("tb");
9963         return height;
9964     },
9965
9966     // private
9967     syncBodyHeight : function()
9968     {
9969         var bd = this.body, // the text
9970             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9971             bw = this.bwrap;
9972         var height = this.size.height - this.getHeaderFooterHeight(false);
9973         bd.setHeight(height-bd.getMargins("tb"));
9974         var hh = this.header.getHeight();
9975         var h = this.size.height-hh;
9976         cb.setHeight(h);
9977         
9978         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9979         bw.setHeight(h-cb.getPadding("tb"));
9980         
9981         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9982         bd.setWidth(bw.getWidth(true));
9983         if(this.tabs){
9984             this.tabs.syncHeight();
9985             if(Roo.isIE){
9986                 this.tabs.el.repaint();
9987             }
9988         }
9989     },
9990
9991     /**
9992      * Restores the previous state of the dialog if Roo.state is configured.
9993      * @return {Roo.BasicDialog} this
9994      */
9995     restoreState : function(){
9996         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9997         if(box && box.width){
9998             this.xy = [box.x, box.y];
9999             this.resizeTo(box.width, box.height);
10000         }
10001         return this;
10002     },
10003
10004     // private
10005     beforeShow : function(){
10006         this.expand();
10007         if(this.fixedcenter){
10008             this.xy = this.el.getCenterXY(true);
10009         }
10010         if(this.modal){
10011             Roo.get(document.body).addClass("x-body-masked");
10012             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10013             this.mask.show();
10014         }
10015         this.constrainXY();
10016     },
10017
10018     // private
10019     animShow : function(){
10020         var b = Roo.get(this.animateTarget).getBox();
10021         this.proxy.setSize(b.width, b.height);
10022         this.proxy.setLocation(b.x, b.y);
10023         this.proxy.show();
10024         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10025                     true, .35, this.showEl.createDelegate(this));
10026     },
10027
10028     /**
10029      * Shows the dialog.
10030      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10031      * @return {Roo.BasicDialog} this
10032      */
10033     show : function(animateTarget){
10034         if (this.fireEvent("beforeshow", this) === false){
10035             return;
10036         }
10037         if(this.syncHeightBeforeShow){
10038             this.syncBodyHeight();
10039         }else if(this.firstShow){
10040             this.firstShow = false;
10041             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10042         }
10043         this.animateTarget = animateTarget || this.animateTarget;
10044         if(!this.el.isVisible()){
10045             this.beforeShow();
10046             if(this.animateTarget && Roo.get(this.animateTarget)){
10047                 this.animShow();
10048             }else{
10049                 this.showEl();
10050             }
10051         }
10052         return this;
10053     },
10054
10055     // private
10056     showEl : function(){
10057         this.proxy.hide();
10058         this.el.setXY(this.xy);
10059         this.el.show();
10060         this.adjustAssets(true);
10061         this.toFront();
10062         this.focus();
10063         // IE peekaboo bug - fix found by Dave Fenwick
10064         if(Roo.isIE){
10065             this.el.repaint();
10066         }
10067         this.fireEvent("show", this);
10068     },
10069
10070     /**
10071      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10072      * dialog itself will receive focus.
10073      */
10074     focus : function(){
10075         if(this.defaultButton){
10076             this.defaultButton.focus();
10077         }else{
10078             this.focusEl.focus();
10079         }
10080     },
10081
10082     // private
10083     constrainXY : function(){
10084         if(this.constraintoviewport !== false){
10085             if(!this.viewSize){
10086                 if(this.container){
10087                     var s = this.container.getSize();
10088                     this.viewSize = [s.width, s.height];
10089                 }else{
10090                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10091                 }
10092             }
10093             var s = Roo.get(this.container||document).getScroll();
10094
10095             var x = this.xy[0], y = this.xy[1];
10096             var w = this.size.width, h = this.size.height;
10097             var vw = this.viewSize[0], vh = this.viewSize[1];
10098             // only move it if it needs it
10099             var moved = false;
10100             // first validate right/bottom
10101             if(x + w > vw+s.left){
10102                 x = vw - w;
10103                 moved = true;
10104             }
10105             if(y + h > vh+s.top){
10106                 y = vh - h;
10107                 moved = true;
10108             }
10109             // then make sure top/left isn't negative
10110             if(x < s.left){
10111                 x = s.left;
10112                 moved = true;
10113             }
10114             if(y < s.top){
10115                 y = s.top;
10116                 moved = true;
10117             }
10118             if(moved){
10119                 // cache xy
10120                 this.xy = [x, y];
10121                 if(this.isVisible()){
10122                     this.el.setLocation(x, y);
10123                     this.adjustAssets();
10124                 }
10125             }
10126         }
10127     },
10128
10129     // private
10130     onDrag : function(){
10131         if(!this.proxyDrag){
10132             this.xy = this.el.getXY();
10133             this.adjustAssets();
10134         }
10135     },
10136
10137     // private
10138     adjustAssets : function(doShow){
10139         var x = this.xy[0], y = this.xy[1];
10140         var w = this.size.width, h = this.size.height;
10141         if(doShow === true){
10142             if(this.shadow){
10143                 this.shadow.show(this.el);
10144             }
10145             if(this.shim){
10146                 this.shim.show();
10147             }
10148         }
10149         if(this.shadow && this.shadow.isVisible()){
10150             this.shadow.show(this.el);
10151         }
10152         if(this.shim && this.shim.isVisible()){
10153             this.shim.setBounds(x, y, w, h);
10154         }
10155     },
10156
10157     // private
10158     adjustViewport : function(w, h){
10159         if(!w || !h){
10160             w = Roo.lib.Dom.getViewWidth();
10161             h = Roo.lib.Dom.getViewHeight();
10162         }
10163         // cache the size
10164         this.viewSize = [w, h];
10165         if(this.modal && this.mask.isVisible()){
10166             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10167             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10168         }
10169         if(this.isVisible()){
10170             this.constrainXY();
10171         }
10172     },
10173
10174     /**
10175      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10176      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10177      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10178      */
10179     destroy : function(removeEl){
10180         if(this.isVisible()){
10181             this.animateTarget = null;
10182             this.hide();
10183         }
10184         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10185         if(this.tabs){
10186             this.tabs.destroy(removeEl);
10187         }
10188         Roo.destroy(
10189              this.shim,
10190              this.proxy,
10191              this.resizer,
10192              this.close,
10193              this.mask
10194         );
10195         if(this.dd){
10196             this.dd.unreg();
10197         }
10198         if(this.buttons){
10199            for(var i = 0, len = this.buttons.length; i < len; i++){
10200                this.buttons[i].destroy();
10201            }
10202         }
10203         this.el.removeAllListeners();
10204         if(removeEl === true){
10205             this.el.update("");
10206             this.el.remove();
10207         }
10208         Roo.DialogManager.unregister(this);
10209     },
10210
10211     // private
10212     startMove : function(){
10213         if(this.proxyDrag){
10214             this.proxy.show();
10215         }
10216         if(this.constraintoviewport !== false){
10217             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10218         }
10219     },
10220
10221     // private
10222     endMove : function(){
10223         if(!this.proxyDrag){
10224             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10225         }else{
10226             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10227             this.proxy.hide();
10228         }
10229         this.refreshSize();
10230         this.adjustAssets();
10231         this.focus();
10232         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10233     },
10234
10235     /**
10236      * Brings this dialog to the front of any other visible dialogs
10237      * @return {Roo.BasicDialog} this
10238      */
10239     toFront : function(){
10240         Roo.DialogManager.bringToFront(this);
10241         return this;
10242     },
10243
10244     /**
10245      * Sends this dialog to the back (under) of any other visible dialogs
10246      * @return {Roo.BasicDialog} this
10247      */
10248     toBack : function(){
10249         Roo.DialogManager.sendToBack(this);
10250         return this;
10251     },
10252
10253     /**
10254      * Centers this dialog in the viewport
10255      * @return {Roo.BasicDialog} this
10256      */
10257     center : function(){
10258         var xy = this.el.getCenterXY(true);
10259         this.moveTo(xy[0], xy[1]);
10260         return this;
10261     },
10262
10263     /**
10264      * Moves the dialog's top-left corner to the specified point
10265      * @param {Number} x
10266      * @param {Number} y
10267      * @return {Roo.BasicDialog} this
10268      */
10269     moveTo : function(x, y){
10270         this.xy = [x,y];
10271         if(this.isVisible()){
10272             this.el.setXY(this.xy);
10273             this.adjustAssets();
10274         }
10275         return this;
10276     },
10277
10278     /**
10279      * Aligns the dialog to the specified element
10280      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10281      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10282      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10283      * @return {Roo.BasicDialog} this
10284      */
10285     alignTo : function(element, position, offsets){
10286         this.xy = this.el.getAlignToXY(element, position, offsets);
10287         if(this.isVisible()){
10288             this.el.setXY(this.xy);
10289             this.adjustAssets();
10290         }
10291         return this;
10292     },
10293
10294     /**
10295      * Anchors an element to another element and realigns it when the window is resized.
10296      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10297      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10298      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10299      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10300      * is a number, it is used as the buffer delay (defaults to 50ms).
10301      * @return {Roo.BasicDialog} this
10302      */
10303     anchorTo : function(el, alignment, offsets, monitorScroll){
10304         var action = function(){
10305             this.alignTo(el, alignment, offsets);
10306         };
10307         Roo.EventManager.onWindowResize(action, this);
10308         var tm = typeof monitorScroll;
10309         if(tm != 'undefined'){
10310             Roo.EventManager.on(window, 'scroll', action, this,
10311                 {buffer: tm == 'number' ? monitorScroll : 50});
10312         }
10313         action.call(this);
10314         return this;
10315     },
10316
10317     /**
10318      * Returns true if the dialog is visible
10319      * @return {Boolean}
10320      */
10321     isVisible : function(){
10322         return this.el.isVisible();
10323     },
10324
10325     // private
10326     animHide : function(callback){
10327         var b = Roo.get(this.animateTarget).getBox();
10328         this.proxy.show();
10329         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10330         this.el.hide();
10331         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10332                     this.hideEl.createDelegate(this, [callback]));
10333     },
10334
10335     /**
10336      * Hides the dialog.
10337      * @param {Function} callback (optional) Function to call when the dialog is hidden
10338      * @return {Roo.BasicDialog} this
10339      */
10340     hide : function(callback){
10341         if (this.fireEvent("beforehide", this) === false){
10342             return;
10343         }
10344         if(this.shadow){
10345             this.shadow.hide();
10346         }
10347         if(this.shim) {
10348           this.shim.hide();
10349         }
10350         // sometimes animateTarget seems to get set.. causing problems...
10351         // this just double checks..
10352         if(this.animateTarget && Roo.get(this.animateTarget)) {
10353            this.animHide(callback);
10354         }else{
10355             this.el.hide();
10356             this.hideEl(callback);
10357         }
10358         return this;
10359     },
10360
10361     // private
10362     hideEl : function(callback){
10363         this.proxy.hide();
10364         if(this.modal){
10365             this.mask.hide();
10366             Roo.get(document.body).removeClass("x-body-masked");
10367         }
10368         this.fireEvent("hide", this);
10369         if(typeof callback == "function"){
10370             callback();
10371         }
10372     },
10373
10374     // private
10375     hideAction : function(){
10376         this.setLeft("-10000px");
10377         this.setTop("-10000px");
10378         this.setStyle("visibility", "hidden");
10379     },
10380
10381     // private
10382     refreshSize : function(){
10383         this.size = this.el.getSize();
10384         this.xy = this.el.getXY();
10385         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10386     },
10387
10388     // private
10389     // z-index is managed by the DialogManager and may be overwritten at any time
10390     setZIndex : function(index){
10391         if(this.modal){
10392             this.mask.setStyle("z-index", index);
10393         }
10394         if(this.shim){
10395             this.shim.setStyle("z-index", ++index);
10396         }
10397         if(this.shadow){
10398             this.shadow.setZIndex(++index);
10399         }
10400         this.el.setStyle("z-index", ++index);
10401         if(this.proxy){
10402             this.proxy.setStyle("z-index", ++index);
10403         }
10404         if(this.resizer){
10405             this.resizer.proxy.setStyle("z-index", ++index);
10406         }
10407
10408         this.lastZIndex = index;
10409     },
10410
10411     /**
10412      * Returns the element for this dialog
10413      * @return {Roo.Element} The underlying dialog Element
10414      */
10415     getEl : function(){
10416         return this.el;
10417     }
10418 });
10419
10420 /**
10421  * @class Roo.DialogManager
10422  * Provides global access to BasicDialogs that have been created and
10423  * support for z-indexing (layering) multiple open dialogs.
10424  */
10425 Roo.DialogManager = function(){
10426     var list = {};
10427     var accessList = [];
10428     var front = null;
10429
10430     // private
10431     var sortDialogs = function(d1, d2){
10432         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10433     };
10434
10435     // private
10436     var orderDialogs = function(){
10437         accessList.sort(sortDialogs);
10438         var seed = Roo.DialogManager.zseed;
10439         for(var i = 0, len = accessList.length; i < len; i++){
10440             var dlg = accessList[i];
10441             if(dlg){
10442                 dlg.setZIndex(seed + (i*10));
10443             }
10444         }
10445     };
10446
10447     return {
10448         /**
10449          * The starting z-index for BasicDialogs (defaults to 9000)
10450          * @type Number The z-index value
10451          */
10452         zseed : 9000,
10453
10454         // private
10455         register : function(dlg){
10456             list[dlg.id] = dlg;
10457             accessList.push(dlg);
10458         },
10459
10460         // private
10461         unregister : function(dlg){
10462             delete list[dlg.id];
10463             var i=0;
10464             var len=0;
10465             if(!accessList.indexOf){
10466                 for(  i = 0, len = accessList.length; i < len; i++){
10467                     if(accessList[i] == dlg){
10468                         accessList.splice(i, 1);
10469                         return;
10470                     }
10471                 }
10472             }else{
10473                  i = accessList.indexOf(dlg);
10474                 if(i != -1){
10475                     accessList.splice(i, 1);
10476                 }
10477             }
10478         },
10479
10480         /**
10481          * Gets a registered dialog by id
10482          * @param {String/Object} id The id of the dialog or a dialog
10483          * @return {Roo.BasicDialog} this
10484          */
10485         get : function(id){
10486             return typeof id == "object" ? id : list[id];
10487         },
10488
10489         /**
10490          * Brings the specified dialog to the front
10491          * @param {String/Object} dlg The id of the dialog or a dialog
10492          * @return {Roo.BasicDialog} this
10493          */
10494         bringToFront : function(dlg){
10495             dlg = this.get(dlg);
10496             if(dlg != front){
10497                 front = dlg;
10498                 dlg._lastAccess = new Date().getTime();
10499                 orderDialogs();
10500             }
10501             return dlg;
10502         },
10503
10504         /**
10505          * Sends the specified dialog to the back
10506          * @param {String/Object} dlg The id of the dialog or a dialog
10507          * @return {Roo.BasicDialog} this
10508          */
10509         sendToBack : function(dlg){
10510             dlg = this.get(dlg);
10511             dlg._lastAccess = -(new Date().getTime());
10512             orderDialogs();
10513             return dlg;
10514         },
10515
10516         /**
10517          * Hides all dialogs
10518          */
10519         hideAll : function(){
10520             for(var id in list){
10521                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10522                     list[id].hide();
10523                 }
10524             }
10525         }
10526     };
10527 }();
10528
10529 /**
10530  * @class Roo.LayoutDialog
10531  * @extends Roo.BasicDialog
10532  * Dialog which provides adjustments for working with a layout in a Dialog.
10533  * Add your necessary layout config options to the dialog's config.<br>
10534  * Example usage (including a nested layout):
10535  * <pre><code>
10536 if(!dialog){
10537     dialog = new Roo.LayoutDialog("download-dlg", {
10538         modal: true,
10539         width:600,
10540         height:450,
10541         shadow:true,
10542         minWidth:500,
10543         minHeight:350,
10544         autoTabs:true,
10545         proxyDrag:true,
10546         // layout config merges with the dialog config
10547         center:{
10548             tabPosition: "top",
10549             alwaysShowTabs: true
10550         }
10551     });
10552     dialog.addKeyListener(27, dialog.hide, dialog);
10553     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10554     dialog.addButton("Build It!", this.getDownload, this);
10555
10556     // we can even add nested layouts
10557     var innerLayout = new Roo.BorderLayout("dl-inner", {
10558         east: {
10559             initialSize: 200,
10560             autoScroll:true,
10561             split:true
10562         },
10563         center: {
10564             autoScroll:true
10565         }
10566     });
10567     innerLayout.beginUpdate();
10568     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10569     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10570     innerLayout.endUpdate(true);
10571
10572     var layout = dialog.getLayout();
10573     layout.beginUpdate();
10574     layout.add("center", new Roo.ContentPanel("standard-panel",
10575                         {title: "Download the Source", fitToFrame:true}));
10576     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10577                {title: "Build your own roo.js"}));
10578     layout.getRegion("center").showPanel(sp);
10579     layout.endUpdate();
10580 }
10581 </code></pre>
10582     * @constructor
10583     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10584     * @param {Object} config configuration options
10585   */
10586 Roo.LayoutDialog = function(el, cfg){
10587     
10588     var config=  cfg;
10589     if (typeof(cfg) == 'undefined') {
10590         config = Roo.apply({}, el);
10591         // not sure why we use documentElement here.. - it should always be body.
10592         // IE7 borks horribly if we use documentElement.
10593         // webkit also does not like documentElement - it creates a body element...
10594         el = Roo.get( document.body || document.documentElement ).createChild();
10595         //config.autoCreate = true;
10596     }
10597     
10598     
10599     config.autoTabs = false;
10600     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10601     this.body.setStyle({overflow:"hidden", position:"relative"});
10602     this.layout = new Roo.BorderLayout(this.body.dom, config);
10603     this.layout.monitorWindowResize = false;
10604     this.el.addClass("x-dlg-auto-layout");
10605     // fix case when center region overwrites center function
10606     this.center = Roo.BasicDialog.prototype.center;
10607     this.on("show", this.layout.layout, this.layout, true);
10608     if (config.items) {
10609         var xitems = config.items;
10610         delete config.items;
10611         Roo.each(xitems, this.addxtype, this);
10612     }
10613     
10614     
10615 };
10616 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10617     /**
10618      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10619      * @deprecated
10620      */
10621     endUpdate : function(){
10622         this.layout.endUpdate();
10623     },
10624
10625     /**
10626      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10627      *  @deprecated
10628      */
10629     beginUpdate : function(){
10630         this.layout.beginUpdate();
10631     },
10632
10633     /**
10634      * Get the BorderLayout for this dialog
10635      * @return {Roo.BorderLayout}
10636      */
10637     getLayout : function(){
10638         return this.layout;
10639     },
10640
10641     showEl : function(){
10642         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10643         if(Roo.isIE7){
10644             this.layout.layout();
10645         }
10646     },
10647
10648     // private
10649     // Use the syncHeightBeforeShow config option to control this automatically
10650     syncBodyHeight : function(){
10651         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10652         if(this.layout){this.layout.layout();}
10653     },
10654     
10655       /**
10656      * Add an xtype element (actually adds to the layout.)
10657      * @return {Object} xdata xtype object data.
10658      */
10659     
10660     addxtype : function(c) {
10661         return this.layout.addxtype(c);
10662     }
10663 });/*
10664  * Based on:
10665  * Ext JS Library 1.1.1
10666  * Copyright(c) 2006-2007, Ext JS, LLC.
10667  *
10668  * Originally Released Under LGPL - original licence link has changed is not relivant.
10669  *
10670  * Fork - LGPL
10671  * <script type="text/javascript">
10672  */
10673  
10674 /**
10675  * @class Roo.MessageBox
10676  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10677  * Example usage:
10678  *<pre><code>
10679 // Basic alert:
10680 Roo.Msg.alert('Status', 'Changes saved successfully.');
10681
10682 // Prompt for user data:
10683 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10684     if (btn == 'ok'){
10685         // process text value...
10686     }
10687 });
10688
10689 // Show a dialog using config options:
10690 Roo.Msg.show({
10691    title:'Save Changes?',
10692    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10693    buttons: Roo.Msg.YESNOCANCEL,
10694    fn: processResult,
10695    animEl: 'elId'
10696 });
10697 </code></pre>
10698  * @singleton
10699  */
10700 Roo.MessageBox = function(){
10701     var dlg, opt, mask, waitTimer;
10702     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10703     var buttons, activeTextEl, bwidth;
10704
10705     // private
10706     var handleButton = function(button){
10707         dlg.hide();
10708         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10709     };
10710
10711     // private
10712     var handleHide = function(){
10713         if(opt && opt.cls){
10714             dlg.el.removeClass(opt.cls);
10715         }
10716         if(waitTimer){
10717             Roo.TaskMgr.stop(waitTimer);
10718             waitTimer = null;
10719         }
10720     };
10721
10722     // private
10723     var updateButtons = function(b){
10724         var width = 0;
10725         if(!b){
10726             buttons["ok"].hide();
10727             buttons["cancel"].hide();
10728             buttons["yes"].hide();
10729             buttons["no"].hide();
10730             dlg.footer.dom.style.display = 'none';
10731             return width;
10732         }
10733         dlg.footer.dom.style.display = '';
10734         for(var k in buttons){
10735             if(typeof buttons[k] != "function"){
10736                 if(b[k]){
10737                     buttons[k].show();
10738                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10739                     width += buttons[k].el.getWidth()+15;
10740                 }else{
10741                     buttons[k].hide();
10742                 }
10743             }
10744         }
10745         return width;
10746     };
10747
10748     // private
10749     var handleEsc = function(d, k, e){
10750         if(opt && opt.closable !== false){
10751             dlg.hide();
10752         }
10753         if(e){
10754             e.stopEvent();
10755         }
10756     };
10757
10758     return {
10759         /**
10760          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10761          * @return {Roo.BasicDialog} The BasicDialog element
10762          */
10763         getDialog : function(){
10764            if(!dlg){
10765                 dlg = new Roo.BasicDialog("x-msg-box", {
10766                     autoCreate : true,
10767                     shadow: true,
10768                     draggable: true,
10769                     resizable:false,
10770                     constraintoviewport:false,
10771                     fixedcenter:true,
10772                     collapsible : false,
10773                     shim:true,
10774                     modal: true,
10775                     width:400, height:100,
10776                     buttonAlign:"center",
10777                     closeClick : function(){
10778                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10779                             handleButton("no");
10780                         }else{
10781                             handleButton("cancel");
10782                         }
10783                     }
10784                 });
10785                 dlg.on("hide", handleHide);
10786                 mask = dlg.mask;
10787                 dlg.addKeyListener(27, handleEsc);
10788                 buttons = {};
10789                 var bt = this.buttonText;
10790                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10791                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10792                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10793                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10794                 bodyEl = dlg.body.createChild({
10795
10796                     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>'
10797                 });
10798                 msgEl = bodyEl.dom.firstChild;
10799                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10800                 textboxEl.enableDisplayMode();
10801                 textboxEl.addKeyListener([10,13], function(){
10802                     if(dlg.isVisible() && opt && opt.buttons){
10803                         if(opt.buttons.ok){
10804                             handleButton("ok");
10805                         }else if(opt.buttons.yes){
10806                             handleButton("yes");
10807                         }
10808                     }
10809                 });
10810                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10811                 textareaEl.enableDisplayMode();
10812                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10813                 progressEl.enableDisplayMode();
10814                 var pf = progressEl.dom.firstChild;
10815                 if (pf) {
10816                     pp = Roo.get(pf.firstChild);
10817                     pp.setHeight(pf.offsetHeight);
10818                 }
10819                 
10820             }
10821             return dlg;
10822         },
10823
10824         /**
10825          * Updates the message box body text
10826          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10827          * the XHTML-compliant non-breaking space character '&amp;#160;')
10828          * @return {Roo.MessageBox} This message box
10829          */
10830         updateText : function(text){
10831             if(!dlg.isVisible() && !opt.width){
10832                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10833             }
10834             msgEl.innerHTML = text || '&#160;';
10835       
10836             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10837             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10838             var w = Math.max(
10839                     Math.min(opt.width || cw , this.maxWidth), 
10840                     Math.max(opt.minWidth || this.minWidth, bwidth)
10841             );
10842             if(opt.prompt){
10843                 activeTextEl.setWidth(w);
10844             }
10845             if(dlg.isVisible()){
10846                 dlg.fixedcenter = false;
10847             }
10848             // to big, make it scroll. = But as usual stupid IE does not support
10849             // !important..
10850             
10851             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10852                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10853                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10854             } else {
10855                 bodyEl.dom.style.height = '';
10856                 bodyEl.dom.style.overflowY = '';
10857             }
10858             if (cw > w) {
10859                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10860             } else {
10861                 bodyEl.dom.style.overflowX = '';
10862             }
10863             
10864             dlg.setContentSize(w, bodyEl.getHeight());
10865             if(dlg.isVisible()){
10866                 dlg.fixedcenter = true;
10867             }
10868             return this;
10869         },
10870
10871         /**
10872          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10873          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10874          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10875          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10876          * @return {Roo.MessageBox} This message box
10877          */
10878         updateProgress : function(value, text){
10879             if(text){
10880                 this.updateText(text);
10881             }
10882             if (pp) { // weird bug on my firefox - for some reason this is not defined
10883                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10884             }
10885             return this;
10886         },        
10887
10888         /**
10889          * Returns true if the message box is currently displayed
10890          * @return {Boolean} True if the message box is visible, else false
10891          */
10892         isVisible : function(){
10893             return dlg && dlg.isVisible();  
10894         },
10895
10896         /**
10897          * Hides the message box if it is displayed
10898          */
10899         hide : function(){
10900             if(this.isVisible()){
10901                 dlg.hide();
10902             }  
10903         },
10904
10905         /**
10906          * Displays a new message box, or reinitializes an existing message box, based on the config options
10907          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10908          * The following config object properties are supported:
10909          * <pre>
10910 Property    Type             Description
10911 ----------  ---------------  ------------------------------------------------------------------------------------
10912 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10913                                    closes (defaults to undefined)
10914 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10915                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10916 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10917                                    progress and wait dialogs will ignore this property and always hide the
10918                                    close button as they can only be closed programmatically.
10919 cls               String           A custom CSS class to apply to the message box element
10920 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10921                                    displayed (defaults to 75)
10922 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10923                                    function will be btn (the name of the button that was clicked, if applicable,
10924                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10925                                    Progress and wait dialogs will ignore this option since they do not respond to
10926                                    user actions and can only be closed programmatically, so any required function
10927                                    should be called by the same code after it closes the dialog.
10928 icon              String           A CSS class that provides a background image to be used as an icon for
10929                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10930 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10931 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10932 modal             Boolean          False to allow user interaction with the page while the message box is
10933                                    displayed (defaults to true)
10934 msg               String           A string that will replace the existing message box body text (defaults
10935                                    to the XHTML-compliant non-breaking space character '&#160;')
10936 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10937 progress          Boolean          True to display a progress bar (defaults to false)
10938 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10939 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10940 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10941 title             String           The title text
10942 value             String           The string value to set into the active textbox element if displayed
10943 wait              Boolean          True to display a progress bar (defaults to false)
10944 width             Number           The width of the dialog in pixels
10945 </pre>
10946          *
10947          * Example usage:
10948          * <pre><code>
10949 Roo.Msg.show({
10950    title: 'Address',
10951    msg: 'Please enter your address:',
10952    width: 300,
10953    buttons: Roo.MessageBox.OKCANCEL,
10954    multiline: true,
10955    fn: saveAddress,
10956    animEl: 'addAddressBtn'
10957 });
10958 </code></pre>
10959          * @param {Object} config Configuration options
10960          * @return {Roo.MessageBox} This message box
10961          */
10962         show : function(options)
10963         {
10964             
10965             // this causes nightmares if you show one dialog after another
10966             // especially on callbacks..
10967              
10968             if(this.isVisible()){
10969                 
10970                 this.hide();
10971                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10972                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10973                 Roo.log("New Dialog Message:" +  options.msg )
10974                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10975                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10976                 
10977             }
10978             var d = this.getDialog();
10979             opt = options;
10980             d.setTitle(opt.title || "&#160;");
10981             d.close.setDisplayed(opt.closable !== false);
10982             activeTextEl = textboxEl;
10983             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10984             if(opt.prompt){
10985                 if(opt.multiline){
10986                     textboxEl.hide();
10987                     textareaEl.show();
10988                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10989                         opt.multiline : this.defaultTextHeight);
10990                     activeTextEl = textareaEl;
10991                 }else{
10992                     textboxEl.show();
10993                     textareaEl.hide();
10994                 }
10995             }else{
10996                 textboxEl.hide();
10997                 textareaEl.hide();
10998             }
10999             progressEl.setDisplayed(opt.progress === true);
11000             this.updateProgress(0);
11001             activeTextEl.dom.value = opt.value || "";
11002             if(opt.prompt){
11003                 dlg.setDefaultButton(activeTextEl);
11004             }else{
11005                 var bs = opt.buttons;
11006                 var db = null;
11007                 if(bs && bs.ok){
11008                     db = buttons["ok"];
11009                 }else if(bs && bs.yes){
11010                     db = buttons["yes"];
11011                 }
11012                 dlg.setDefaultButton(db);
11013             }
11014             bwidth = updateButtons(opt.buttons);
11015             this.updateText(opt.msg);
11016             if(opt.cls){
11017                 d.el.addClass(opt.cls);
11018             }
11019             d.proxyDrag = opt.proxyDrag === true;
11020             d.modal = opt.modal !== false;
11021             d.mask = opt.modal !== false ? mask : false;
11022             if(!d.isVisible()){
11023                 // force it to the end of the z-index stack so it gets a cursor in FF
11024                 document.body.appendChild(dlg.el.dom);
11025                 d.animateTarget = null;
11026                 d.show(options.animEl);
11027             }
11028             return this;
11029         },
11030
11031         /**
11032          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11033          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11034          * and closing the message box when the process is complete.
11035          * @param {String} title The title bar text
11036          * @param {String} msg The message box body text
11037          * @return {Roo.MessageBox} This message box
11038          */
11039         progress : function(title, msg){
11040             this.show({
11041                 title : title,
11042                 msg : msg,
11043                 buttons: false,
11044                 progress:true,
11045                 closable:false,
11046                 minWidth: this.minProgressWidth,
11047                 modal : true
11048             });
11049             return this;
11050         },
11051
11052         /**
11053          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11054          * If a callback function is passed it will be called after the user clicks the button, and the
11055          * id of the button that was clicked will be passed as the only parameter to the callback
11056          * (could also be the top-right close button).
11057          * @param {String} title The title bar text
11058          * @param {String} msg The message box body text
11059          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11060          * @param {Object} scope (optional) The scope of the callback function
11061          * @return {Roo.MessageBox} This message box
11062          */
11063         alert : function(title, msg, fn, scope){
11064             this.show({
11065                 title : title,
11066                 msg : msg,
11067                 buttons: this.OK,
11068                 fn: fn,
11069                 scope : scope,
11070                 modal : true
11071             });
11072             return this;
11073         },
11074
11075         /**
11076          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11077          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11078          * You are responsible for closing the message box when the process is complete.
11079          * @param {String} msg The message box body text
11080          * @param {String} title (optional) The title bar text
11081          * @return {Roo.MessageBox} This message box
11082          */
11083         wait : function(msg, title){
11084             this.show({
11085                 title : title,
11086                 msg : msg,
11087                 buttons: false,
11088                 closable:false,
11089                 progress:true,
11090                 modal:true,
11091                 width:300,
11092                 wait:true
11093             });
11094             waitTimer = Roo.TaskMgr.start({
11095                 run: function(i){
11096                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11097                 },
11098                 interval: 1000
11099             });
11100             return this;
11101         },
11102
11103         /**
11104          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11105          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11106          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11107          * @param {String} title The title bar text
11108          * @param {String} msg The message box body text
11109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11110          * @param {Object} scope (optional) The scope of the callback function
11111          * @return {Roo.MessageBox} This message box
11112          */
11113         confirm : function(title, msg, fn, scope){
11114             this.show({
11115                 title : title,
11116                 msg : msg,
11117                 buttons: this.YESNO,
11118                 fn: fn,
11119                 scope : scope,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11127          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11128          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11129          * (could also be the top-right close button) and the text that was entered will be passed as the two
11130          * parameters to the callback.
11131          * @param {String} title The title bar text
11132          * @param {String} msg The message box body text
11133          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11134          * @param {Object} scope (optional) The scope of the callback function
11135          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11136          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11137          * @return {Roo.MessageBox} This message box
11138          */
11139         prompt : function(title, msg, fn, scope, multiline){
11140             this.show({
11141                 title : title,
11142                 msg : msg,
11143                 buttons: this.OKCANCEL,
11144                 fn: fn,
11145                 minWidth:250,
11146                 scope : scope,
11147                 prompt:true,
11148                 multiline: multiline,
11149                 modal : true
11150             });
11151             return this;
11152         },
11153
11154         /**
11155          * Button config that displays a single OK button
11156          * @type Object
11157          */
11158         OK : {ok:true},
11159         /**
11160          * Button config that displays Yes and No buttons
11161          * @type Object
11162          */
11163         YESNO : {yes:true, no:true},
11164         /**
11165          * Button config that displays OK and Cancel buttons
11166          * @type Object
11167          */
11168         OKCANCEL : {ok:true, cancel:true},
11169         /**
11170          * Button config that displays Yes, No and Cancel buttons
11171          * @type Object
11172          */
11173         YESNOCANCEL : {yes:true, no:true, cancel:true},
11174
11175         /**
11176          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11177          * @type Number
11178          */
11179         defaultTextHeight : 75,
11180         /**
11181          * The maximum width in pixels of the message box (defaults to 600)
11182          * @type Number
11183          */
11184         maxWidth : 600,
11185         /**
11186          * The minimum width in pixels of the message box (defaults to 100)
11187          * @type Number
11188          */
11189         minWidth : 100,
11190         /**
11191          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11192          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11193          * @type Number
11194          */
11195         minProgressWidth : 250,
11196         /**
11197          * An object containing the default button text strings that can be overriden for localized language support.
11198          * Supported properties are: ok, cancel, yes and no.
11199          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11200          * @type Object
11201          */
11202         buttonText : {
11203             ok : "OK",
11204             cancel : "Cancel",
11205             yes : "Yes",
11206             no : "No"
11207         }
11208     };
11209 }();
11210
11211 /**
11212  * Shorthand for {@link Roo.MessageBox}
11213  */
11214 Roo.Msg = Roo.MessageBox;/*
11215  * Based on:
11216  * Ext JS Library 1.1.1
11217  * Copyright(c) 2006-2007, Ext JS, LLC.
11218  *
11219  * Originally Released Under LGPL - original licence link has changed is not relivant.
11220  *
11221  * Fork - LGPL
11222  * <script type="text/javascript">
11223  */
11224 /**
11225  * @class Roo.QuickTips
11226  * Provides attractive and customizable tooltips for any element.
11227  * @singleton
11228  */
11229 Roo.QuickTips = function(){
11230     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11231     var ce, bd, xy, dd;
11232     var visible = false, disabled = true, inited = false;
11233     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11234     
11235     var onOver = function(e){
11236         if(disabled){
11237             return;
11238         }
11239         var t = e.getTarget();
11240         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11241             return;
11242         }
11243         if(ce && t == ce.el){
11244             clearTimeout(hideProc);
11245             return;
11246         }
11247         if(t && tagEls[t.id]){
11248             tagEls[t.id].el = t;
11249             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11250             return;
11251         }
11252         var ttp, et = Roo.fly(t);
11253         var ns = cfg.namespace;
11254         if(tm.interceptTitles && t.title){
11255             ttp = t.title;
11256             t.qtip = ttp;
11257             t.removeAttribute("title");
11258             e.preventDefault();
11259         }else{
11260             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11261         }
11262         if(ttp){
11263             showProc = show.defer(tm.showDelay, tm, [{
11264                 el: t, 
11265                 text: ttp.replace(/\\n/g,'<br/>'),
11266                 width: et.getAttributeNS(ns, cfg.width),
11267                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11268                 title: et.getAttributeNS(ns, cfg.title),
11269                     cls: et.getAttributeNS(ns, cfg.cls)
11270             }]);
11271         }
11272     };
11273     
11274     var onOut = function(e){
11275         clearTimeout(showProc);
11276         var t = e.getTarget();
11277         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11278             hideProc = setTimeout(hide, tm.hideDelay);
11279         }
11280     };
11281     
11282     var onMove = function(e){
11283         if(disabled){
11284             return;
11285         }
11286         xy = e.getXY();
11287         xy[1] += 18;
11288         if(tm.trackMouse && ce){
11289             el.setXY(xy);
11290         }
11291     };
11292     
11293     var onDown = function(e){
11294         clearTimeout(showProc);
11295         clearTimeout(hideProc);
11296         if(!e.within(el)){
11297             if(tm.hideOnClick){
11298                 hide();
11299                 tm.disable();
11300                 tm.enable.defer(100, tm);
11301             }
11302         }
11303     };
11304     
11305     var getPad = function(){
11306         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11307     };
11308
11309     var show = function(o){
11310         if(disabled){
11311             return;
11312         }
11313         clearTimeout(dismissProc);
11314         ce = o;
11315         if(removeCls){ // in case manually hidden
11316             el.removeClass(removeCls);
11317             removeCls = null;
11318         }
11319         if(ce.cls){
11320             el.addClass(ce.cls);
11321             removeCls = ce.cls;
11322         }
11323         if(ce.title){
11324             tipTitle.update(ce.title);
11325             tipTitle.show();
11326         }else{
11327             tipTitle.update('');
11328             tipTitle.hide();
11329         }
11330         el.dom.style.width  = tm.maxWidth+'px';
11331         //tipBody.dom.style.width = '';
11332         tipBodyText.update(o.text);
11333         var p = getPad(), w = ce.width;
11334         if(!w){
11335             var td = tipBodyText.dom;
11336             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11337             if(aw > tm.maxWidth){
11338                 w = tm.maxWidth;
11339             }else if(aw < tm.minWidth){
11340                 w = tm.minWidth;
11341             }else{
11342                 w = aw;
11343             }
11344         }
11345         //tipBody.setWidth(w);
11346         el.setWidth(parseInt(w, 10) + p);
11347         if(ce.autoHide === false){
11348             close.setDisplayed(true);
11349             if(dd){
11350                 dd.unlock();
11351             }
11352         }else{
11353             close.setDisplayed(false);
11354             if(dd){
11355                 dd.lock();
11356             }
11357         }
11358         if(xy){
11359             el.avoidY = xy[1]-18;
11360             el.setXY(xy);
11361         }
11362         if(tm.animate){
11363             el.setOpacity(.1);
11364             el.setStyle("visibility", "visible");
11365             el.fadeIn({callback: afterShow});
11366         }else{
11367             afterShow();
11368         }
11369     };
11370     
11371     var afterShow = function(){
11372         if(ce){
11373             el.show();
11374             esc.enable();
11375             if(tm.autoDismiss && ce.autoHide !== false){
11376                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11377             }
11378         }
11379     };
11380     
11381     var hide = function(noanim){
11382         clearTimeout(dismissProc);
11383         clearTimeout(hideProc);
11384         ce = null;
11385         if(el.isVisible()){
11386             esc.disable();
11387             if(noanim !== true && tm.animate){
11388                 el.fadeOut({callback: afterHide});
11389             }else{
11390                 afterHide();
11391             } 
11392         }
11393     };
11394     
11395     var afterHide = function(){
11396         el.hide();
11397         if(removeCls){
11398             el.removeClass(removeCls);
11399             removeCls = null;
11400         }
11401     };
11402     
11403     return {
11404         /**
11405         * @cfg {Number} minWidth
11406         * The minimum width of the quick tip (defaults to 40)
11407         */
11408        minWidth : 40,
11409         /**
11410         * @cfg {Number} maxWidth
11411         * The maximum width of the quick tip (defaults to 300)
11412         */
11413        maxWidth : 300,
11414         /**
11415         * @cfg {Boolean} interceptTitles
11416         * True to automatically use the element's DOM title value if available (defaults to false)
11417         */
11418        interceptTitles : false,
11419         /**
11420         * @cfg {Boolean} trackMouse
11421         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11422         */
11423        trackMouse : false,
11424         /**
11425         * @cfg {Boolean} hideOnClick
11426         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11427         */
11428        hideOnClick : true,
11429         /**
11430         * @cfg {Number} showDelay
11431         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11432         */
11433        showDelay : 500,
11434         /**
11435         * @cfg {Number} hideDelay
11436         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11437         */
11438        hideDelay : 200,
11439         /**
11440         * @cfg {Boolean} autoHide
11441         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11442         * Used in conjunction with hideDelay.
11443         */
11444        autoHide : true,
11445         /**
11446         * @cfg {Boolean}
11447         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11448         * (defaults to true).  Used in conjunction with autoDismissDelay.
11449         */
11450        autoDismiss : true,
11451         /**
11452         * @cfg {Number}
11453         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11454         */
11455        autoDismissDelay : 5000,
11456        /**
11457         * @cfg {Boolean} animate
11458         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11459         */
11460        animate : false,
11461
11462        /**
11463         * @cfg {String} title
11464         * Title text to display (defaults to '').  This can be any valid HTML markup.
11465         */
11466         title: '',
11467        /**
11468         * @cfg {String} text
11469         * Body text to display (defaults to '').  This can be any valid HTML markup.
11470         */
11471         text : '',
11472        /**
11473         * @cfg {String} cls
11474         * A CSS class to apply to the base quick tip element (defaults to '').
11475         */
11476         cls : '',
11477        /**
11478         * @cfg {Number} width
11479         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11480         * minWidth or maxWidth.
11481         */
11482         width : null,
11483
11484     /**
11485      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11486      * or display QuickTips in a page.
11487      */
11488        init : function(){
11489           tm = Roo.QuickTips;
11490           cfg = tm.tagConfig;
11491           if(!inited){
11492               if(!Roo.isReady){ // allow calling of init() before onReady
11493                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11494                   return;
11495               }
11496               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11497               el.fxDefaults = {stopFx: true};
11498               // maximum custom styling
11499               //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>');
11500               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>');              
11501               tipTitle = el.child('h3');
11502               tipTitle.enableDisplayMode("block");
11503               tipBody = el.child('div.x-tip-bd');
11504               tipBodyText = el.child('div.x-tip-bd-inner');
11505               //bdLeft = el.child('div.x-tip-bd-left');
11506               //bdRight = el.child('div.x-tip-bd-right');
11507               close = el.child('div.x-tip-close');
11508               close.enableDisplayMode("block");
11509               close.on("click", hide);
11510               var d = Roo.get(document);
11511               d.on("mousedown", onDown);
11512               d.on("mouseover", onOver);
11513               d.on("mouseout", onOut);
11514               d.on("mousemove", onMove);
11515               esc = d.addKeyListener(27, hide);
11516               esc.disable();
11517               if(Roo.dd.DD){
11518                   dd = el.initDD("default", null, {
11519                       onDrag : function(){
11520                           el.sync();  
11521                       }
11522                   });
11523                   dd.setHandleElId(tipTitle.id);
11524                   dd.lock();
11525               }
11526               inited = true;
11527           }
11528           this.enable(); 
11529        },
11530
11531     /**
11532      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11533      * are supported:
11534      * <pre>
11535 Property    Type                   Description
11536 ----------  ---------------------  ------------------------------------------------------------------------
11537 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11538      * </ul>
11539      * @param {Object} config The config object
11540      */
11541        register : function(config){
11542            var cs = config instanceof Array ? config : arguments;
11543            for(var i = 0, len = cs.length; i < len; i++) {
11544                var c = cs[i];
11545                var target = c.target;
11546                if(target){
11547                    if(target instanceof Array){
11548                        for(var j = 0, jlen = target.length; j < jlen; j++){
11549                            tagEls[target[j]] = c;
11550                        }
11551                    }else{
11552                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11553                    }
11554                }
11555            }
11556        },
11557
11558     /**
11559      * Removes this quick tip from its element and destroys it.
11560      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11561      */
11562        unregister : function(el){
11563            delete tagEls[Roo.id(el)];
11564        },
11565
11566     /**
11567      * Enable this quick tip.
11568      */
11569        enable : function(){
11570            if(inited && disabled){
11571                locks.pop();
11572                if(locks.length < 1){
11573                    disabled = false;
11574                }
11575            }
11576        },
11577
11578     /**
11579      * Disable this quick tip.
11580      */
11581        disable : function(){
11582           disabled = true;
11583           clearTimeout(showProc);
11584           clearTimeout(hideProc);
11585           clearTimeout(dismissProc);
11586           if(ce){
11587               hide(true);
11588           }
11589           locks.push(1);
11590        },
11591
11592     /**
11593      * Returns true if the quick tip is enabled, else false.
11594      */
11595        isEnabled : function(){
11596             return !disabled;
11597        },
11598
11599         // private
11600        tagConfig : {
11601            namespace : "roo", // was ext?? this may break..
11602            alt_namespace : "ext",
11603            attribute : "qtip",
11604            width : "width",
11605            target : "target",
11606            title : "qtitle",
11607            hide : "hide",
11608            cls : "qclass"
11609        }
11610    };
11611 }();
11612
11613 // backwards compat
11614 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11615  * Based on:
11616  * Ext JS Library 1.1.1
11617  * Copyright(c) 2006-2007, Ext JS, LLC.
11618  *
11619  * Originally Released Under LGPL - original licence link has changed is not relivant.
11620  *
11621  * Fork - LGPL
11622  * <script type="text/javascript">
11623  */
11624  
11625
11626 /**
11627  * @class Roo.tree.TreePanel
11628  * @extends Roo.data.Tree
11629
11630  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11631  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11632  * @cfg {Boolean} enableDD true to enable drag and drop
11633  * @cfg {Boolean} enableDrag true to enable just drag
11634  * @cfg {Boolean} enableDrop true to enable just drop
11635  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11636  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11637  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11638  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11639  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11640  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11641  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11642  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11643  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11644  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11645  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11646  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11647  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11648  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11649  * @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>
11650  * @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>
11651  * 
11652  * @constructor
11653  * @param {String/HTMLElement/Element} el The container element
11654  * @param {Object} config
11655  */
11656 Roo.tree.TreePanel = function(el, config){
11657     var root = false;
11658     var loader = false;
11659     if (config.root) {
11660         root = config.root;
11661         delete config.root;
11662     }
11663     if (config.loader) {
11664         loader = config.loader;
11665         delete config.loader;
11666     }
11667     
11668     Roo.apply(this, config);
11669     Roo.tree.TreePanel.superclass.constructor.call(this);
11670     this.el = Roo.get(el);
11671     this.el.addClass('x-tree');
11672     //console.log(root);
11673     if (root) {
11674         this.setRootNode( Roo.factory(root, Roo.tree));
11675     }
11676     if (loader) {
11677         this.loader = Roo.factory(loader, Roo.tree);
11678     }
11679    /**
11680     * Read-only. The id of the container element becomes this TreePanel's id.
11681     */
11682     this.id = this.el.id;
11683     this.addEvents({
11684         /**
11685         * @event beforeload
11686         * Fires before a node is loaded, return false to cancel
11687         * @param {Node} node The node being loaded
11688         */
11689         "beforeload" : true,
11690         /**
11691         * @event load
11692         * Fires when a node is loaded
11693         * @param {Node} node The node that was loaded
11694         */
11695         "load" : true,
11696         /**
11697         * @event textchange
11698         * Fires when the text for a node is changed
11699         * @param {Node} node The node
11700         * @param {String} text The new text
11701         * @param {String} oldText The old text
11702         */
11703         "textchange" : true,
11704         /**
11705         * @event beforeexpand
11706         * Fires before a node is expanded, return false to cancel.
11707         * @param {Node} node The node
11708         * @param {Boolean} deep
11709         * @param {Boolean} anim
11710         */
11711         "beforeexpand" : true,
11712         /**
11713         * @event beforecollapse
11714         * Fires before a node is collapsed, return false to cancel.
11715         * @param {Node} node The node
11716         * @param {Boolean} deep
11717         * @param {Boolean} anim
11718         */
11719         "beforecollapse" : true,
11720         /**
11721         * @event expand
11722         * Fires when a node is expanded
11723         * @param {Node} node The node
11724         */
11725         "expand" : true,
11726         /**
11727         * @event disabledchange
11728         * Fires when the disabled status of a node changes
11729         * @param {Node} node The node
11730         * @param {Boolean} disabled
11731         */
11732         "disabledchange" : true,
11733         /**
11734         * @event collapse
11735         * Fires when a node is collapsed
11736         * @param {Node} node The node
11737         */
11738         "collapse" : true,
11739         /**
11740         * @event beforeclick
11741         * Fires before click processing on a node. Return false to cancel the default action.
11742         * @param {Node} node The node
11743         * @param {Roo.EventObject} e The event object
11744         */
11745         "beforeclick":true,
11746         /**
11747         * @event checkchange
11748         * Fires when a node with a checkbox's checked property changes
11749         * @param {Node} this This node
11750         * @param {Boolean} checked
11751         */
11752         "checkchange":true,
11753         /**
11754         * @event click
11755         * Fires when a node is clicked
11756         * @param {Node} node The node
11757         * @param {Roo.EventObject} e The event object
11758         */
11759         "click":true,
11760         /**
11761         * @event dblclick
11762         * Fires when a node is double clicked
11763         * @param {Node} node The node
11764         * @param {Roo.EventObject} e The event object
11765         */
11766         "dblclick":true,
11767         /**
11768         * @event contextmenu
11769         * Fires when a node is right clicked
11770         * @param {Node} node The node
11771         * @param {Roo.EventObject} e The event object
11772         */
11773         "contextmenu":true,
11774         /**
11775         * @event beforechildrenrendered
11776         * Fires right before the child nodes for a node are rendered
11777         * @param {Node} node The node
11778         */
11779         "beforechildrenrendered":true,
11780         /**
11781         * @event startdrag
11782         * Fires when a node starts being dragged
11783         * @param {Roo.tree.TreePanel} this
11784         * @param {Roo.tree.TreeNode} node
11785         * @param {event} e The raw browser event
11786         */ 
11787        "startdrag" : true,
11788        /**
11789         * @event enddrag
11790         * Fires when a drag operation is complete
11791         * @param {Roo.tree.TreePanel} this
11792         * @param {Roo.tree.TreeNode} node
11793         * @param {event} e The raw browser event
11794         */
11795        "enddrag" : true,
11796        /**
11797         * @event dragdrop
11798         * Fires when a dragged node is dropped on a valid DD target
11799         * @param {Roo.tree.TreePanel} this
11800         * @param {Roo.tree.TreeNode} node
11801         * @param {DD} dd The dd it was dropped on
11802         * @param {event} e The raw browser event
11803         */
11804        "dragdrop" : true,
11805        /**
11806         * @event beforenodedrop
11807         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11808         * passed to handlers has the following properties:<br />
11809         * <ul style="padding:5px;padding-left:16px;">
11810         * <li>tree - The TreePanel</li>
11811         * <li>target - The node being targeted for the drop</li>
11812         * <li>data - The drag data from the drag source</li>
11813         * <li>point - The point of the drop - append, above or below</li>
11814         * <li>source - The drag source</li>
11815         * <li>rawEvent - Raw mouse event</li>
11816         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11817         * to be inserted by setting them on this object.</li>
11818         * <li>cancel - Set this to true to cancel the drop.</li>
11819         * </ul>
11820         * @param {Object} dropEvent
11821         */
11822        "beforenodedrop" : true,
11823        /**
11824         * @event nodedrop
11825         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11826         * passed to handlers has the following properties:<br />
11827         * <ul style="padding:5px;padding-left:16px;">
11828         * <li>tree - The TreePanel</li>
11829         * <li>target - The node being targeted for the drop</li>
11830         * <li>data - The drag data from the drag source</li>
11831         * <li>point - The point of the drop - append, above or below</li>
11832         * <li>source - The drag source</li>
11833         * <li>rawEvent - Raw mouse event</li>
11834         * <li>dropNode - Dropped node(s).</li>
11835         * </ul>
11836         * @param {Object} dropEvent
11837         */
11838        "nodedrop" : true,
11839         /**
11840         * @event nodedragover
11841         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11842         * passed to handlers has the following properties:<br />
11843         * <ul style="padding:5px;padding-left:16px;">
11844         * <li>tree - The TreePanel</li>
11845         * <li>target - The node being targeted for the drop</li>
11846         * <li>data - The drag data from the drag source</li>
11847         * <li>point - The point of the drop - append, above or below</li>
11848         * <li>source - The drag source</li>
11849         * <li>rawEvent - Raw mouse event</li>
11850         * <li>dropNode - Drop node(s) provided by the source.</li>
11851         * <li>cancel - Set this to true to signal drop not allowed.</li>
11852         * </ul>
11853         * @param {Object} dragOverEvent
11854         */
11855        "nodedragover" : true,
11856        /**
11857         * @event appendnode
11858         * Fires when append node to the tree
11859         * @param {Roo.tree.TreePanel} this
11860         * @param {Roo.tree.TreeNode} node
11861         * @param {Number} index The index of the newly appended node
11862         */
11863        "appendnode" : true
11864         
11865     });
11866     if(this.singleExpand){
11867        this.on("beforeexpand", this.restrictExpand, this);
11868     }
11869     if (this.editor) {
11870         this.editor.tree = this;
11871         this.editor = Roo.factory(this.editor, Roo.tree);
11872     }
11873     
11874     if (this.selModel) {
11875         this.selModel = Roo.factory(this.selModel, Roo.tree);
11876     }
11877    
11878 };
11879 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11880     rootVisible : true,
11881     animate: Roo.enableFx,
11882     lines : true,
11883     enableDD : false,
11884     hlDrop : Roo.enableFx,
11885   
11886     renderer: false,
11887     
11888     rendererTip: false,
11889     // private
11890     restrictExpand : function(node){
11891         var p = node.parentNode;
11892         if(p){
11893             if(p.expandedChild && p.expandedChild.parentNode == p){
11894                 p.expandedChild.collapse();
11895             }
11896             p.expandedChild = node;
11897         }
11898     },
11899
11900     // private override
11901     setRootNode : function(node){
11902         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11903         if(!this.rootVisible){
11904             node.ui = new Roo.tree.RootTreeNodeUI(node);
11905         }
11906         return node;
11907     },
11908
11909     /**
11910      * Returns the container element for this TreePanel
11911      */
11912     getEl : function(){
11913         return this.el;
11914     },
11915
11916     /**
11917      * Returns the default TreeLoader for this TreePanel
11918      */
11919     getLoader : function(){
11920         return this.loader;
11921     },
11922
11923     /**
11924      * Expand all nodes
11925      */
11926     expandAll : function(){
11927         this.root.expand(true);
11928     },
11929
11930     /**
11931      * Collapse all nodes
11932      */
11933     collapseAll : function(){
11934         this.root.collapse(true);
11935     },
11936
11937     /**
11938      * Returns the selection model used by this TreePanel
11939      */
11940     getSelectionModel : function(){
11941         if(!this.selModel){
11942             this.selModel = new Roo.tree.DefaultSelectionModel();
11943         }
11944         return this.selModel;
11945     },
11946
11947     /**
11948      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11949      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11950      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11951      * @return {Array}
11952      */
11953     getChecked : function(a, startNode){
11954         startNode = startNode || this.root;
11955         var r = [];
11956         var f = function(){
11957             if(this.attributes.checked){
11958                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11959             }
11960         }
11961         startNode.cascade(f);
11962         return r;
11963     },
11964
11965     /**
11966      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11967      * @param {String} path
11968      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11969      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11970      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11971      */
11972     expandPath : function(path, attr, callback){
11973         attr = attr || "id";
11974         var keys = path.split(this.pathSeparator);
11975         var curNode = this.root;
11976         if(curNode.attributes[attr] != keys[1]){ // invalid root
11977             if(callback){
11978                 callback(false, null);
11979             }
11980             return;
11981         }
11982         var index = 1;
11983         var f = function(){
11984             if(++index == keys.length){
11985                 if(callback){
11986                     callback(true, curNode);
11987                 }
11988                 return;
11989             }
11990             var c = curNode.findChild(attr, keys[index]);
11991             if(!c){
11992                 if(callback){
11993                     callback(false, curNode);
11994                 }
11995                 return;
11996             }
11997             curNode = c;
11998             c.expand(false, false, f);
11999         };
12000         curNode.expand(false, false, f);
12001     },
12002
12003     /**
12004      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12005      * @param {String} path
12006      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12007      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12008      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12009      */
12010     selectPath : function(path, attr, callback){
12011         attr = attr || "id";
12012         var keys = path.split(this.pathSeparator);
12013         var v = keys.pop();
12014         if(keys.length > 0){
12015             var f = function(success, node){
12016                 if(success && node){
12017                     var n = node.findChild(attr, v);
12018                     if(n){
12019                         n.select();
12020                         if(callback){
12021                             callback(true, n);
12022                         }
12023                     }else if(callback){
12024                         callback(false, n);
12025                     }
12026                 }else{
12027                     if(callback){
12028                         callback(false, n);
12029                     }
12030                 }
12031             };
12032             this.expandPath(keys.join(this.pathSeparator), attr, f);
12033         }else{
12034             this.root.select();
12035             if(callback){
12036                 callback(true, this.root);
12037             }
12038         }
12039     },
12040
12041     getTreeEl : function(){
12042         return this.el;
12043     },
12044
12045     /**
12046      * Trigger rendering of this TreePanel
12047      */
12048     render : function(){
12049         if (this.innerCt) {
12050             return this; // stop it rendering more than once!!
12051         }
12052         
12053         this.innerCt = this.el.createChild({tag:"ul",
12054                cls:"x-tree-root-ct " +
12055                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12056
12057         if(this.containerScroll){
12058             Roo.dd.ScrollManager.register(this.el);
12059         }
12060         if((this.enableDD || this.enableDrop) && !this.dropZone){
12061            /**
12062             * The dropZone used by this tree if drop is enabled
12063             * @type Roo.tree.TreeDropZone
12064             */
12065              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12066                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12067            });
12068         }
12069         if((this.enableDD || this.enableDrag) && !this.dragZone){
12070            /**
12071             * The dragZone used by this tree if drag is enabled
12072             * @type Roo.tree.TreeDragZone
12073             */
12074             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12075                ddGroup: this.ddGroup || "TreeDD",
12076                scroll: this.ddScroll
12077            });
12078         }
12079         this.getSelectionModel().init(this);
12080         if (!this.root) {
12081             Roo.log("ROOT not set in tree");
12082             return this;
12083         }
12084         this.root.render();
12085         if(!this.rootVisible){
12086             this.root.renderChildren();
12087         }
12088         return this;
12089     }
12090 });/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100  
12101
12102 /**
12103  * @class Roo.tree.DefaultSelectionModel
12104  * @extends Roo.util.Observable
12105  * The default single selection for a TreePanel.
12106  * @param {Object} cfg Configuration
12107  */
12108 Roo.tree.DefaultSelectionModel = function(cfg){
12109    this.selNode = null;
12110    
12111    
12112    
12113    this.addEvents({
12114        /**
12115         * @event selectionchange
12116         * Fires when the selected node changes
12117         * @param {DefaultSelectionModel} this
12118         * @param {TreeNode} node the new selection
12119         */
12120        "selectionchange" : true,
12121
12122        /**
12123         * @event beforeselect
12124         * Fires before the selected node changes, return false to cancel the change
12125         * @param {DefaultSelectionModel} this
12126         * @param {TreeNode} node the new selection
12127         * @param {TreeNode} node the old selection
12128         */
12129        "beforeselect" : true
12130    });
12131    
12132     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12133 };
12134
12135 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12136     init : function(tree){
12137         this.tree = tree;
12138         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12139         tree.on("click", this.onNodeClick, this);
12140     },
12141     
12142     onNodeClick : function(node, e){
12143         if (e.ctrlKey && this.selNode == node)  {
12144             this.unselect(node);
12145             return;
12146         }
12147         this.select(node);
12148     },
12149     
12150     /**
12151      * Select a node.
12152      * @param {TreeNode} node The node to select
12153      * @return {TreeNode} The selected node
12154      */
12155     select : function(node){
12156         var last = this.selNode;
12157         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12158             if(last){
12159                 last.ui.onSelectedChange(false);
12160             }
12161             this.selNode = node;
12162             node.ui.onSelectedChange(true);
12163             this.fireEvent("selectionchange", this, node, last);
12164         }
12165         return node;
12166     },
12167     
12168     /**
12169      * Deselect a node.
12170      * @param {TreeNode} node The node to unselect
12171      */
12172     unselect : function(node){
12173         if(this.selNode == node){
12174             this.clearSelections();
12175         }    
12176     },
12177     
12178     /**
12179      * Clear all selections
12180      */
12181     clearSelections : function(){
12182         var n = this.selNode;
12183         if(n){
12184             n.ui.onSelectedChange(false);
12185             this.selNode = null;
12186             this.fireEvent("selectionchange", this, null);
12187         }
12188         return n;
12189     },
12190     
12191     /**
12192      * Get the selected node
12193      * @return {TreeNode} The selected node
12194      */
12195     getSelectedNode : function(){
12196         return this.selNode;    
12197     },
12198     
12199     /**
12200      * Returns true if the node is selected
12201      * @param {TreeNode} node The node to check
12202      * @return {Boolean}
12203      */
12204     isSelected : function(node){
12205         return this.selNode == node;  
12206     },
12207
12208     /**
12209      * Selects the node above the selected node in the tree, intelligently walking the nodes
12210      * @return TreeNode The new selection
12211      */
12212     selectPrevious : function(){
12213         var s = this.selNode || this.lastSelNode;
12214         if(!s){
12215             return null;
12216         }
12217         var ps = s.previousSibling;
12218         if(ps){
12219             if(!ps.isExpanded() || ps.childNodes.length < 1){
12220                 return this.select(ps);
12221             } else{
12222                 var lc = ps.lastChild;
12223                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12224                     lc = lc.lastChild;
12225                 }
12226                 return this.select(lc);
12227             }
12228         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12229             return this.select(s.parentNode);
12230         }
12231         return null;
12232     },
12233
12234     /**
12235      * Selects the node above the selected node in the tree, intelligently walking the nodes
12236      * @return TreeNode The new selection
12237      */
12238     selectNext : function(){
12239         var s = this.selNode || this.lastSelNode;
12240         if(!s){
12241             return null;
12242         }
12243         if(s.firstChild && s.isExpanded()){
12244              return this.select(s.firstChild);
12245          }else if(s.nextSibling){
12246              return this.select(s.nextSibling);
12247          }else if(s.parentNode){
12248             var newS = null;
12249             s.parentNode.bubble(function(){
12250                 if(this.nextSibling){
12251                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12252                     return false;
12253                 }
12254             });
12255             return newS;
12256          }
12257         return null;
12258     },
12259
12260     onKeyDown : function(e){
12261         var s = this.selNode || this.lastSelNode;
12262         // undesirable, but required
12263         var sm = this;
12264         if(!s){
12265             return;
12266         }
12267         var k = e.getKey();
12268         switch(k){
12269              case e.DOWN:
12270                  e.stopEvent();
12271                  this.selectNext();
12272              break;
12273              case e.UP:
12274                  e.stopEvent();
12275                  this.selectPrevious();
12276              break;
12277              case e.RIGHT:
12278                  e.preventDefault();
12279                  if(s.hasChildNodes()){
12280                      if(!s.isExpanded()){
12281                          s.expand();
12282                      }else if(s.firstChild){
12283                          this.select(s.firstChild, e);
12284                      }
12285                  }
12286              break;
12287              case e.LEFT:
12288                  e.preventDefault();
12289                  if(s.hasChildNodes() && s.isExpanded()){
12290                      s.collapse();
12291                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12292                      this.select(s.parentNode, e);
12293                  }
12294              break;
12295         };
12296     }
12297 });
12298
12299 /**
12300  * @class Roo.tree.MultiSelectionModel
12301  * @extends Roo.util.Observable
12302  * Multi selection for a TreePanel.
12303  * @param {Object} cfg Configuration
12304  */
12305 Roo.tree.MultiSelectionModel = function(){
12306    this.selNodes = [];
12307    this.selMap = {};
12308    this.addEvents({
12309        /**
12310         * @event selectionchange
12311         * Fires when the selected nodes change
12312         * @param {MultiSelectionModel} this
12313         * @param {Array} nodes Array of the selected nodes
12314         */
12315        "selectionchange" : true
12316    });
12317    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12318    
12319 };
12320
12321 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12322     init : function(tree){
12323         this.tree = tree;
12324         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12325         tree.on("click", this.onNodeClick, this);
12326     },
12327     
12328     onNodeClick : function(node, e){
12329         this.select(node, e, e.ctrlKey);
12330     },
12331     
12332     /**
12333      * Select a node.
12334      * @param {TreeNode} node The node to select
12335      * @param {EventObject} e (optional) An event associated with the selection
12336      * @param {Boolean} keepExisting True to retain existing selections
12337      * @return {TreeNode} The selected node
12338      */
12339     select : function(node, e, keepExisting){
12340         if(keepExisting !== true){
12341             this.clearSelections(true);
12342         }
12343         if(this.isSelected(node)){
12344             this.lastSelNode = node;
12345             return node;
12346         }
12347         this.selNodes.push(node);
12348         this.selMap[node.id] = node;
12349         this.lastSelNode = node;
12350         node.ui.onSelectedChange(true);
12351         this.fireEvent("selectionchange", this, this.selNodes);
12352         return node;
12353     },
12354     
12355     /**
12356      * Deselect a node.
12357      * @param {TreeNode} node The node to unselect
12358      */
12359     unselect : function(node){
12360         if(this.selMap[node.id]){
12361             node.ui.onSelectedChange(false);
12362             var sn = this.selNodes;
12363             var index = -1;
12364             if(sn.indexOf){
12365                 index = sn.indexOf(node);
12366             }else{
12367                 for(var i = 0, len = sn.length; i < len; i++){
12368                     if(sn[i] == node){
12369                         index = i;
12370                         break;
12371                     }
12372                 }
12373             }
12374             if(index != -1){
12375                 this.selNodes.splice(index, 1);
12376             }
12377             delete this.selMap[node.id];
12378             this.fireEvent("selectionchange", this, this.selNodes);
12379         }
12380     },
12381     
12382     /**
12383      * Clear all selections
12384      */
12385     clearSelections : function(suppressEvent){
12386         var sn = this.selNodes;
12387         if(sn.length > 0){
12388             for(var i = 0, len = sn.length; i < len; i++){
12389                 sn[i].ui.onSelectedChange(false);
12390             }
12391             this.selNodes = [];
12392             this.selMap = {};
12393             if(suppressEvent !== true){
12394                 this.fireEvent("selectionchange", this, this.selNodes);
12395             }
12396         }
12397     },
12398     
12399     /**
12400      * Returns true if the node is selected
12401      * @param {TreeNode} node The node to check
12402      * @return {Boolean}
12403      */
12404     isSelected : function(node){
12405         return this.selMap[node.id] ? true : false;  
12406     },
12407     
12408     /**
12409      * Returns an array of the selected nodes
12410      * @return {Array}
12411      */
12412     getSelectedNodes : function(){
12413         return this.selNodes;    
12414     },
12415
12416     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12417
12418     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12419
12420     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12421 });/*
12422  * Based on:
12423  * Ext JS Library 1.1.1
12424  * Copyright(c) 2006-2007, Ext JS, LLC.
12425  *
12426  * Originally Released Under LGPL - original licence link has changed is not relivant.
12427  *
12428  * Fork - LGPL
12429  * <script type="text/javascript">
12430  */
12431  
12432 /**
12433  * @class Roo.tree.TreeNode
12434  * @extends Roo.data.Node
12435  * @cfg {String} text The text for this node
12436  * @cfg {Boolean} expanded true to start the node expanded
12437  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12438  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12439  * @cfg {Boolean} disabled true to start the node disabled
12440  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12441  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12442  * @cfg {String} cls A css class to be added to the node
12443  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12444  * @cfg {String} href URL of the link used for the node (defaults to #)
12445  * @cfg {String} hrefTarget target frame for the link
12446  * @cfg {String} qtip An Ext QuickTip for the node
12447  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12448  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12449  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12450  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12451  * (defaults to undefined with no checkbox rendered)
12452  * @constructor
12453  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12454  */
12455 Roo.tree.TreeNode = function(attributes){
12456     attributes = attributes || {};
12457     if(typeof attributes == "string"){
12458         attributes = {text: attributes};
12459     }
12460     this.childrenRendered = false;
12461     this.rendered = false;
12462     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12463     this.expanded = attributes.expanded === true;
12464     this.isTarget = attributes.isTarget !== false;
12465     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12466     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12467
12468     /**
12469      * Read-only. The text for this node. To change it use setText().
12470      * @type String
12471      */
12472     this.text = attributes.text;
12473     /**
12474      * True if this node is disabled.
12475      * @type Boolean
12476      */
12477     this.disabled = attributes.disabled === true;
12478
12479     this.addEvents({
12480         /**
12481         * @event textchange
12482         * Fires when the text for this node is changed
12483         * @param {Node} this This node
12484         * @param {String} text The new text
12485         * @param {String} oldText The old text
12486         */
12487         "textchange" : true,
12488         /**
12489         * @event beforeexpand
12490         * Fires before this node is expanded, return false to cancel.
12491         * @param {Node} this This node
12492         * @param {Boolean} deep
12493         * @param {Boolean} anim
12494         */
12495         "beforeexpand" : true,
12496         /**
12497         * @event beforecollapse
12498         * Fires before this node is collapsed, return false to cancel.
12499         * @param {Node} this This node
12500         * @param {Boolean} deep
12501         * @param {Boolean} anim
12502         */
12503         "beforecollapse" : true,
12504         /**
12505         * @event expand
12506         * Fires when this node is expanded
12507         * @param {Node} this This node
12508         */
12509         "expand" : true,
12510         /**
12511         * @event disabledchange
12512         * Fires when the disabled status of this node changes
12513         * @param {Node} this This node
12514         * @param {Boolean} disabled
12515         */
12516         "disabledchange" : true,
12517         /**
12518         * @event collapse
12519         * Fires when this node is collapsed
12520         * @param {Node} this This node
12521         */
12522         "collapse" : true,
12523         /**
12524         * @event beforeclick
12525         * Fires before click processing. Return false to cancel the default action.
12526         * @param {Node} this This node
12527         * @param {Roo.EventObject} e The event object
12528         */
12529         "beforeclick":true,
12530         /**
12531         * @event checkchange
12532         * Fires when a node with a checkbox's checked property changes
12533         * @param {Node} this This node
12534         * @param {Boolean} checked
12535         */
12536         "checkchange":true,
12537         /**
12538         * @event click
12539         * Fires when this node is clicked
12540         * @param {Node} this This node
12541         * @param {Roo.EventObject} e The event object
12542         */
12543         "click":true,
12544         /**
12545         * @event dblclick
12546         * Fires when this node is double clicked
12547         * @param {Node} this This node
12548         * @param {Roo.EventObject} e The event object
12549         */
12550         "dblclick":true,
12551         /**
12552         * @event contextmenu
12553         * Fires when this node is right clicked
12554         * @param {Node} this This node
12555         * @param {Roo.EventObject} e The event object
12556         */
12557         "contextmenu":true,
12558         /**
12559         * @event beforechildrenrendered
12560         * Fires right before the child nodes for this node are rendered
12561         * @param {Node} this This node
12562         */
12563         "beforechildrenrendered":true
12564     });
12565
12566     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12567
12568     /**
12569      * Read-only. The UI for this node
12570      * @type TreeNodeUI
12571      */
12572     this.ui = new uiClass(this);
12573     
12574     // finally support items[]
12575     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12576         return;
12577     }
12578     
12579     
12580     Roo.each(this.attributes.items, function(c) {
12581         this.appendChild(Roo.factory(c,Roo.Tree));
12582     }, this);
12583     delete this.attributes.items;
12584     
12585     
12586     
12587 };
12588 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12589     preventHScroll: true,
12590     /**
12591      * Returns true if this node is expanded
12592      * @return {Boolean}
12593      */
12594     isExpanded : function(){
12595         return this.expanded;
12596     },
12597
12598     /**
12599      * Returns the UI object for this node
12600      * @return {TreeNodeUI}
12601      */
12602     getUI : function(){
12603         return this.ui;
12604     },
12605
12606     // private override
12607     setFirstChild : function(node){
12608         var of = this.firstChild;
12609         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12610         if(this.childrenRendered && of && node != of){
12611             of.renderIndent(true, true);
12612         }
12613         if(this.rendered){
12614             this.renderIndent(true, true);
12615         }
12616     },
12617
12618     // private override
12619     setLastChild : function(node){
12620         var ol = this.lastChild;
12621         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12622         if(this.childrenRendered && ol && node != ol){
12623             ol.renderIndent(true, true);
12624         }
12625         if(this.rendered){
12626             this.renderIndent(true, true);
12627         }
12628     },
12629
12630     // these methods are overridden to provide lazy rendering support
12631     // private override
12632     appendChild : function()
12633     {
12634         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12635         if(node && this.childrenRendered){
12636             node.render();
12637         }
12638         this.ui.updateExpandIcon();
12639         return node;
12640     },
12641
12642     // private override
12643     removeChild : function(node){
12644         this.ownerTree.getSelectionModel().unselect(node);
12645         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12646         // if it's been rendered remove dom node
12647         if(this.childrenRendered){
12648             node.ui.remove();
12649         }
12650         if(this.childNodes.length < 1){
12651             this.collapse(false, false);
12652         }else{
12653             this.ui.updateExpandIcon();
12654         }
12655         if(!this.firstChild) {
12656             this.childrenRendered = false;
12657         }
12658         return node;
12659     },
12660
12661     // private override
12662     insertBefore : function(node, refNode){
12663         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12664         if(newNode && refNode && this.childrenRendered){
12665             node.render();
12666         }
12667         this.ui.updateExpandIcon();
12668         return newNode;
12669     },
12670
12671     /**
12672      * Sets the text for this node
12673      * @param {String} text
12674      */
12675     setText : function(text){
12676         var oldText = this.text;
12677         this.text = text;
12678         this.attributes.text = text;
12679         if(this.rendered){ // event without subscribing
12680             this.ui.onTextChange(this, text, oldText);
12681         }
12682         this.fireEvent("textchange", this, text, oldText);
12683     },
12684
12685     /**
12686      * Triggers selection of this node
12687      */
12688     select : function(){
12689         this.getOwnerTree().getSelectionModel().select(this);
12690     },
12691
12692     /**
12693      * Triggers deselection of this node
12694      */
12695     unselect : function(){
12696         this.getOwnerTree().getSelectionModel().unselect(this);
12697     },
12698
12699     /**
12700      * Returns true if this node is selected
12701      * @return {Boolean}
12702      */
12703     isSelected : function(){
12704         return this.getOwnerTree().getSelectionModel().isSelected(this);
12705     },
12706
12707     /**
12708      * Expand this node.
12709      * @param {Boolean} deep (optional) True to expand all children as well
12710      * @param {Boolean} anim (optional) false to cancel the default animation
12711      * @param {Function} callback (optional) A callback to be called when
12712      * expanding this node completes (does not wait for deep expand to complete).
12713      * Called with 1 parameter, this node.
12714      */
12715     expand : function(deep, anim, callback){
12716         if(!this.expanded){
12717             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12718                 return;
12719             }
12720             if(!this.childrenRendered){
12721                 this.renderChildren();
12722             }
12723             this.expanded = true;
12724             
12725             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12726                 this.ui.animExpand(function(){
12727                     this.fireEvent("expand", this);
12728                     if(typeof callback == "function"){
12729                         callback(this);
12730                     }
12731                     if(deep === true){
12732                         this.expandChildNodes(true);
12733                     }
12734                 }.createDelegate(this));
12735                 return;
12736             }else{
12737                 this.ui.expand();
12738                 this.fireEvent("expand", this);
12739                 if(typeof callback == "function"){
12740                     callback(this);
12741                 }
12742             }
12743         }else{
12744            if(typeof callback == "function"){
12745                callback(this);
12746            }
12747         }
12748         if(deep === true){
12749             this.expandChildNodes(true);
12750         }
12751     },
12752
12753     isHiddenRoot : function(){
12754         return this.isRoot && !this.getOwnerTree().rootVisible;
12755     },
12756
12757     /**
12758      * Collapse this node.
12759      * @param {Boolean} deep (optional) True to collapse all children as well
12760      * @param {Boolean} anim (optional) false to cancel the default animation
12761      */
12762     collapse : function(deep, anim){
12763         if(this.expanded && !this.isHiddenRoot()){
12764             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12765                 return;
12766             }
12767             this.expanded = false;
12768             if((this.getOwnerTree().animate && anim !== false) || anim){
12769                 this.ui.animCollapse(function(){
12770                     this.fireEvent("collapse", this);
12771                     if(deep === true){
12772                         this.collapseChildNodes(true);
12773                     }
12774                 }.createDelegate(this));
12775                 return;
12776             }else{
12777                 this.ui.collapse();
12778                 this.fireEvent("collapse", this);
12779             }
12780         }
12781         if(deep === true){
12782             var cs = this.childNodes;
12783             for(var i = 0, len = cs.length; i < len; i++) {
12784                 cs[i].collapse(true, false);
12785             }
12786         }
12787     },
12788
12789     // private
12790     delayedExpand : function(delay){
12791         if(!this.expandProcId){
12792             this.expandProcId = this.expand.defer(delay, this);
12793         }
12794     },
12795
12796     // private
12797     cancelExpand : function(){
12798         if(this.expandProcId){
12799             clearTimeout(this.expandProcId);
12800         }
12801         this.expandProcId = false;
12802     },
12803
12804     /**
12805      * Toggles expanded/collapsed state of the node
12806      */
12807     toggle : function(){
12808         if(this.expanded){
12809             this.collapse();
12810         }else{
12811             this.expand();
12812         }
12813     },
12814
12815     /**
12816      * Ensures all parent nodes are expanded
12817      */
12818     ensureVisible : function(callback){
12819         var tree = this.getOwnerTree();
12820         tree.expandPath(this.parentNode.getPath(), false, function(){
12821             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12822             Roo.callback(callback);
12823         }.createDelegate(this));
12824     },
12825
12826     /**
12827      * Expand all child nodes
12828      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12829      */
12830     expandChildNodes : function(deep){
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++) {
12833                 cs[i].expand(deep);
12834         }
12835     },
12836
12837     /**
12838      * Collapse all child nodes
12839      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12840      */
12841     collapseChildNodes : function(deep){
12842         var cs = this.childNodes;
12843         for(var i = 0, len = cs.length; i < len; i++) {
12844                 cs[i].collapse(deep);
12845         }
12846     },
12847
12848     /**
12849      * Disables this node
12850      */
12851     disable : function(){
12852         this.disabled = true;
12853         this.unselect();
12854         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12855             this.ui.onDisableChange(this, true);
12856         }
12857         this.fireEvent("disabledchange", this, true);
12858     },
12859
12860     /**
12861      * Enables this node
12862      */
12863     enable : function(){
12864         this.disabled = false;
12865         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12866             this.ui.onDisableChange(this, false);
12867         }
12868         this.fireEvent("disabledchange", this, false);
12869     },
12870
12871     // private
12872     renderChildren : function(suppressEvent){
12873         if(suppressEvent !== false){
12874             this.fireEvent("beforechildrenrendered", this);
12875         }
12876         var cs = this.childNodes;
12877         for(var i = 0, len = cs.length; i < len; i++){
12878             cs[i].render(true);
12879         }
12880         this.childrenRendered = true;
12881     },
12882
12883     // private
12884     sort : function(fn, scope){
12885         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12886         if(this.childrenRendered){
12887             var cs = this.childNodes;
12888             for(var i = 0, len = cs.length; i < len; i++){
12889                 cs[i].render(true);
12890             }
12891         }
12892     },
12893
12894     // private
12895     render : function(bulkRender){
12896         this.ui.render(bulkRender);
12897         if(!this.rendered){
12898             this.rendered = true;
12899             if(this.expanded){
12900                 this.expanded = false;
12901                 this.expand(false, false);
12902             }
12903         }
12904     },
12905
12906     // private
12907     renderIndent : function(deep, refresh){
12908         if(refresh){
12909             this.ui.childIndent = null;
12910         }
12911         this.ui.renderIndent();
12912         if(deep === true && this.childrenRendered){
12913             var cs = this.childNodes;
12914             for(var i = 0, len = cs.length; i < len; i++){
12915                 cs[i].renderIndent(true, refresh);
12916             }
12917         }
12918     }
12919 });/*
12920  * Based on:
12921  * Ext JS Library 1.1.1
12922  * Copyright(c) 2006-2007, Ext JS, LLC.
12923  *
12924  * Originally Released Under LGPL - original licence link has changed is not relivant.
12925  *
12926  * Fork - LGPL
12927  * <script type="text/javascript">
12928  */
12929  
12930 /**
12931  * @class Roo.tree.AsyncTreeNode
12932  * @extends Roo.tree.TreeNode
12933  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12934  * @constructor
12935  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12936  */
12937  Roo.tree.AsyncTreeNode = function(config){
12938     this.loaded = false;
12939     this.loading = false;
12940     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12941     /**
12942     * @event beforeload
12943     * Fires before this node is loaded, return false to cancel
12944     * @param {Node} this This node
12945     */
12946     this.addEvents({'beforeload':true, 'load': true});
12947     /**
12948     * @event load
12949     * Fires when this node is loaded
12950     * @param {Node} this This node
12951     */
12952     /**
12953      * The loader used by this node (defaults to using the tree's defined loader)
12954      * @type TreeLoader
12955      * @property loader
12956      */
12957 };
12958 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12959     expand : function(deep, anim, callback){
12960         if(this.loading){ // if an async load is already running, waiting til it's done
12961             var timer;
12962             var f = function(){
12963                 if(!this.loading){ // done loading
12964                     clearInterval(timer);
12965                     this.expand(deep, anim, callback);
12966                 }
12967             }.createDelegate(this);
12968             timer = setInterval(f, 200);
12969             return;
12970         }
12971         if(!this.loaded){
12972             if(this.fireEvent("beforeload", this) === false){
12973                 return;
12974             }
12975             this.loading = true;
12976             this.ui.beforeLoad(this);
12977             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12978             if(loader){
12979                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12980                 return;
12981             }
12982         }
12983         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12984     },
12985     
12986     /**
12987      * Returns true if this node is currently loading
12988      * @return {Boolean}
12989      */
12990     isLoading : function(){
12991         return this.loading;  
12992     },
12993     
12994     loadComplete : function(deep, anim, callback){
12995         this.loading = false;
12996         this.loaded = true;
12997         this.ui.afterLoad(this);
12998         this.fireEvent("load", this);
12999         this.expand(deep, anim, callback);
13000     },
13001     
13002     /**
13003      * Returns true if this node has been loaded
13004      * @return {Boolean}
13005      */
13006     isLoaded : function(){
13007         return this.loaded;
13008     },
13009     
13010     hasChildNodes : function(){
13011         if(!this.isLeaf() && !this.loaded){
13012             return true;
13013         }else{
13014             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13015         }
13016     },
13017
13018     /**
13019      * Trigger a reload for this node
13020      * @param {Function} callback
13021      */
13022     reload : function(callback){
13023         this.collapse(false, false);
13024         while(this.firstChild){
13025             this.removeChild(this.firstChild);
13026         }
13027         this.childrenRendered = false;
13028         this.loaded = false;
13029         if(this.isHiddenRoot()){
13030             this.expanded = false;
13031         }
13032         this.expand(false, false, callback);
13033     }
13034 });/*
13035  * Based on:
13036  * Ext JS Library 1.1.1
13037  * Copyright(c) 2006-2007, Ext JS, LLC.
13038  *
13039  * Originally Released Under LGPL - original licence link has changed is not relivant.
13040  *
13041  * Fork - LGPL
13042  * <script type="text/javascript">
13043  */
13044  
13045 /**
13046  * @class Roo.tree.TreeNodeUI
13047  * @constructor
13048  * @param {Object} node The node to render
13049  * The TreeNode UI implementation is separate from the
13050  * tree implementation. Unless you are customizing the tree UI,
13051  * you should never have to use this directly.
13052  */
13053 Roo.tree.TreeNodeUI = function(node){
13054     this.node = node;
13055     this.rendered = false;
13056     this.animating = false;
13057     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13058 };
13059
13060 Roo.tree.TreeNodeUI.prototype = {
13061     removeChild : function(node){
13062         if(this.rendered){
13063             this.ctNode.removeChild(node.ui.getEl());
13064         }
13065     },
13066
13067     beforeLoad : function(){
13068          this.addClass("x-tree-node-loading");
13069     },
13070
13071     afterLoad : function(){
13072          this.removeClass("x-tree-node-loading");
13073     },
13074
13075     onTextChange : function(node, text, oldText){
13076         if(this.rendered){
13077             this.textNode.innerHTML = text;
13078         }
13079     },
13080
13081     onDisableChange : function(node, state){
13082         this.disabled = state;
13083         if(state){
13084             this.addClass("x-tree-node-disabled");
13085         }else{
13086             this.removeClass("x-tree-node-disabled");
13087         }
13088     },
13089
13090     onSelectedChange : function(state){
13091         if(state){
13092             this.focus();
13093             this.addClass("x-tree-selected");
13094         }else{
13095             //this.blur();
13096             this.removeClass("x-tree-selected");
13097         }
13098     },
13099
13100     onMove : function(tree, node, oldParent, newParent, index, refNode){
13101         this.childIndent = null;
13102         if(this.rendered){
13103             var targetNode = newParent.ui.getContainer();
13104             if(!targetNode){//target not rendered
13105                 this.holder = document.createElement("div");
13106                 this.holder.appendChild(this.wrap);
13107                 return;
13108             }
13109             var insertBefore = refNode ? refNode.ui.getEl() : null;
13110             if(insertBefore){
13111                 targetNode.insertBefore(this.wrap, insertBefore);
13112             }else{
13113                 targetNode.appendChild(this.wrap);
13114             }
13115             this.node.renderIndent(true);
13116         }
13117     },
13118
13119     addClass : function(cls){
13120         if(this.elNode){
13121             Roo.fly(this.elNode).addClass(cls);
13122         }
13123     },
13124
13125     removeClass : function(cls){
13126         if(this.elNode){
13127             Roo.fly(this.elNode).removeClass(cls);
13128         }
13129     },
13130
13131     remove : function(){
13132         if(this.rendered){
13133             this.holder = document.createElement("div");
13134             this.holder.appendChild(this.wrap);
13135         }
13136     },
13137
13138     fireEvent : function(){
13139         return this.node.fireEvent.apply(this.node, arguments);
13140     },
13141
13142     initEvents : function(){
13143         this.node.on("move", this.onMove, this);
13144         var E = Roo.EventManager;
13145         var a = this.anchor;
13146
13147         var el = Roo.fly(a, '_treeui');
13148
13149         if(Roo.isOpera){ // opera render bug ignores the CSS
13150             el.setStyle("text-decoration", "none");
13151         }
13152
13153         el.on("click", this.onClick, this);
13154         el.on("dblclick", this.onDblClick, this);
13155
13156         if(this.checkbox){
13157             Roo.EventManager.on(this.checkbox,
13158                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13159         }
13160
13161         el.on("contextmenu", this.onContextMenu, this);
13162
13163         var icon = Roo.fly(this.iconNode);
13164         icon.on("click", this.onClick, this);
13165         icon.on("dblclick", this.onDblClick, this);
13166         icon.on("contextmenu", this.onContextMenu, this);
13167         E.on(this.ecNode, "click", this.ecClick, this, true);
13168
13169         if(this.node.disabled){
13170             this.addClass("x-tree-node-disabled");
13171         }
13172         if(this.node.hidden){
13173             this.addClass("x-tree-node-disabled");
13174         }
13175         var ot = this.node.getOwnerTree();
13176         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13177         if(dd && (!this.node.isRoot || ot.rootVisible)){
13178             Roo.dd.Registry.register(this.elNode, {
13179                 node: this.node,
13180                 handles: this.getDDHandles(),
13181                 isHandle: false
13182             });
13183         }
13184     },
13185
13186     getDDHandles : function(){
13187         return [this.iconNode, this.textNode];
13188     },
13189
13190     hide : function(){
13191         if(this.rendered){
13192             this.wrap.style.display = "none";
13193         }
13194     },
13195
13196     show : function(){
13197         if(this.rendered){
13198             this.wrap.style.display = "";
13199         }
13200     },
13201
13202     onContextMenu : function(e){
13203         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13204             e.preventDefault();
13205             this.focus();
13206             this.fireEvent("contextmenu", this.node, e);
13207         }
13208     },
13209
13210     onClick : function(e){
13211         if(this.dropping){
13212             e.stopEvent();
13213             return;
13214         }
13215         if(this.fireEvent("beforeclick", this.node, e) !== false){
13216             if(!this.disabled && this.node.attributes.href){
13217                 this.fireEvent("click", this.node, e);
13218                 return;
13219             }
13220             e.preventDefault();
13221             if(this.disabled){
13222                 return;
13223             }
13224
13225             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13226                 this.node.toggle();
13227             }
13228
13229             this.fireEvent("click", this.node, e);
13230         }else{
13231             e.stopEvent();
13232         }
13233     },
13234
13235     onDblClick : function(e){
13236         e.preventDefault();
13237         if(this.disabled){
13238             return;
13239         }
13240         if(this.checkbox){
13241             this.toggleCheck();
13242         }
13243         if(!this.animating && this.node.hasChildNodes()){
13244             this.node.toggle();
13245         }
13246         this.fireEvent("dblclick", this.node, e);
13247     },
13248
13249     onCheckChange : function(){
13250         var checked = this.checkbox.checked;
13251         this.node.attributes.checked = checked;
13252         this.fireEvent('checkchange', this.node, checked);
13253     },
13254
13255     ecClick : function(e){
13256         if(!this.animating && this.node.hasChildNodes()){
13257             this.node.toggle();
13258         }
13259     },
13260
13261     startDrop : function(){
13262         this.dropping = true;
13263     },
13264
13265     // delayed drop so the click event doesn't get fired on a drop
13266     endDrop : function(){
13267        setTimeout(function(){
13268            this.dropping = false;
13269        }.createDelegate(this), 50);
13270     },
13271
13272     expand : function(){
13273         this.updateExpandIcon();
13274         this.ctNode.style.display = "";
13275     },
13276
13277     focus : function(){
13278         if(!this.node.preventHScroll){
13279             try{this.anchor.focus();
13280             }catch(e){}
13281         }else if(!Roo.isIE){
13282             try{
13283                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13284                 var l = noscroll.scrollLeft;
13285                 this.anchor.focus();
13286                 noscroll.scrollLeft = l;
13287             }catch(e){}
13288         }
13289     },
13290
13291     toggleCheck : function(value){
13292         var cb = this.checkbox;
13293         if(cb){
13294             cb.checked = (value === undefined ? !cb.checked : value);
13295         }
13296     },
13297
13298     blur : function(){
13299         try{
13300             this.anchor.blur();
13301         }catch(e){}
13302     },
13303
13304     animExpand : function(callback){
13305         var ct = Roo.get(this.ctNode);
13306         ct.stopFx();
13307         if(!this.node.hasChildNodes()){
13308             this.updateExpandIcon();
13309             this.ctNode.style.display = "";
13310             Roo.callback(callback);
13311             return;
13312         }
13313         this.animating = true;
13314         this.updateExpandIcon();
13315
13316         ct.slideIn('t', {
13317            callback : function(){
13318                this.animating = false;
13319                Roo.callback(callback);
13320             },
13321             scope: this,
13322             duration: this.node.ownerTree.duration || .25
13323         });
13324     },
13325
13326     highlight : function(){
13327         var tree = this.node.getOwnerTree();
13328         Roo.fly(this.wrap).highlight(
13329             tree.hlColor || "C3DAF9",
13330             {endColor: tree.hlBaseColor}
13331         );
13332     },
13333
13334     collapse : function(){
13335         this.updateExpandIcon();
13336         this.ctNode.style.display = "none";
13337     },
13338
13339     animCollapse : function(callback){
13340         var ct = Roo.get(this.ctNode);
13341         ct.enableDisplayMode('block');
13342         ct.stopFx();
13343
13344         this.animating = true;
13345         this.updateExpandIcon();
13346
13347         ct.slideOut('t', {
13348             callback : function(){
13349                this.animating = false;
13350                Roo.callback(callback);
13351             },
13352             scope: this,
13353             duration: this.node.ownerTree.duration || .25
13354         });
13355     },
13356
13357     getContainer : function(){
13358         return this.ctNode;
13359     },
13360
13361     getEl : function(){
13362         return this.wrap;
13363     },
13364
13365     appendDDGhost : function(ghostNode){
13366         ghostNode.appendChild(this.elNode.cloneNode(true));
13367     },
13368
13369     getDDRepairXY : function(){
13370         return Roo.lib.Dom.getXY(this.iconNode);
13371     },
13372
13373     onRender : function(){
13374         this.render();
13375     },
13376
13377     render : function(bulkRender){
13378         var n = this.node, a = n.attributes;
13379         var targetNode = n.parentNode ?
13380               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13381
13382         if(!this.rendered){
13383             this.rendered = true;
13384
13385             this.renderElements(n, a, targetNode, bulkRender);
13386
13387             if(a.qtip){
13388                if(this.textNode.setAttributeNS){
13389                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13390                    if(a.qtipTitle){
13391                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13392                    }
13393                }else{
13394                    this.textNode.setAttribute("ext:qtip", a.qtip);
13395                    if(a.qtipTitle){
13396                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13397                    }
13398                }
13399             }else if(a.qtipCfg){
13400                 a.qtipCfg.target = Roo.id(this.textNode);
13401                 Roo.QuickTips.register(a.qtipCfg);
13402             }
13403             this.initEvents();
13404             if(!this.node.expanded){
13405                 this.updateExpandIcon();
13406             }
13407         }else{
13408             if(bulkRender === true) {
13409                 targetNode.appendChild(this.wrap);
13410             }
13411         }
13412     },
13413
13414     renderElements : function(n, a, targetNode, bulkRender)
13415     {
13416         // add some indent caching, this helps performance when rendering a large tree
13417         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13418         var t = n.getOwnerTree();
13419         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13420         if (typeof(n.attributes.html) != 'undefined') {
13421             txt = n.attributes.html;
13422         }
13423         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13424         var cb = typeof a.checked == 'boolean';
13425         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13426         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13427             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13428             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13429             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13430             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13431             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13432              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13433                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13434             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13435             "</li>"];
13436
13437         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13438             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13439                                 n.nextSibling.ui.getEl(), buf.join(""));
13440         }else{
13441             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13442         }
13443
13444         this.elNode = this.wrap.childNodes[0];
13445         this.ctNode = this.wrap.childNodes[1];
13446         var cs = this.elNode.childNodes;
13447         this.indentNode = cs[0];
13448         this.ecNode = cs[1];
13449         this.iconNode = cs[2];
13450         var index = 3;
13451         if(cb){
13452             this.checkbox = cs[3];
13453             index++;
13454         }
13455         this.anchor = cs[index];
13456         this.textNode = cs[index].firstChild;
13457     },
13458
13459     getAnchor : function(){
13460         return this.anchor;
13461     },
13462
13463     getTextEl : function(){
13464         return this.textNode;
13465     },
13466
13467     getIconEl : function(){
13468         return this.iconNode;
13469     },
13470
13471     isChecked : function(){
13472         return this.checkbox ? this.checkbox.checked : false;
13473     },
13474
13475     updateExpandIcon : function(){
13476         if(this.rendered){
13477             var n = this.node, c1, c2;
13478             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13479             var hasChild = n.hasChildNodes();
13480             if(hasChild){
13481                 if(n.expanded){
13482                     cls += "-minus";
13483                     c1 = "x-tree-node-collapsed";
13484                     c2 = "x-tree-node-expanded";
13485                 }else{
13486                     cls += "-plus";
13487                     c1 = "x-tree-node-expanded";
13488                     c2 = "x-tree-node-collapsed";
13489                 }
13490                 if(this.wasLeaf){
13491                     this.removeClass("x-tree-node-leaf");
13492                     this.wasLeaf = false;
13493                 }
13494                 if(this.c1 != c1 || this.c2 != c2){
13495                     Roo.fly(this.elNode).replaceClass(c1, c2);
13496                     this.c1 = c1; this.c2 = c2;
13497                 }
13498             }else{
13499                 // this changes non-leafs into leafs if they have no children.
13500                 // it's not very rational behaviour..
13501                 
13502                 if(!this.wasLeaf && this.node.leaf){
13503                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13504                     delete this.c1;
13505                     delete this.c2;
13506                     this.wasLeaf = true;
13507                 }
13508             }
13509             var ecc = "x-tree-ec-icon "+cls;
13510             if(this.ecc != ecc){
13511                 this.ecNode.className = ecc;
13512                 this.ecc = ecc;
13513             }
13514         }
13515     },
13516
13517     getChildIndent : function(){
13518         if(!this.childIndent){
13519             var buf = [];
13520             var p = this.node;
13521             while(p){
13522                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13523                     if(!p.isLast()) {
13524                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13525                     } else {
13526                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13527                     }
13528                 }
13529                 p = p.parentNode;
13530             }
13531             this.childIndent = buf.join("");
13532         }
13533         return this.childIndent;
13534     },
13535
13536     renderIndent : function(){
13537         if(this.rendered){
13538             var indent = "";
13539             var p = this.node.parentNode;
13540             if(p){
13541                 indent = p.ui.getChildIndent();
13542             }
13543             if(this.indentMarkup != indent){ // don't rerender if not required
13544                 this.indentNode.innerHTML = indent;
13545                 this.indentMarkup = indent;
13546             }
13547             this.updateExpandIcon();
13548         }
13549     }
13550 };
13551
13552 Roo.tree.RootTreeNodeUI = function(){
13553     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13554 };
13555 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13556     render : function(){
13557         if(!this.rendered){
13558             var targetNode = this.node.ownerTree.innerCt.dom;
13559             this.node.expanded = true;
13560             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13561             this.wrap = this.ctNode = targetNode.firstChild;
13562         }
13563     },
13564     collapse : function(){
13565     },
13566     expand : function(){
13567     }
13568 });/*
13569  * Based on:
13570  * Ext JS Library 1.1.1
13571  * Copyright(c) 2006-2007, Ext JS, LLC.
13572  *
13573  * Originally Released Under LGPL - original licence link has changed is not relivant.
13574  *
13575  * Fork - LGPL
13576  * <script type="text/javascript">
13577  */
13578 /**
13579  * @class Roo.tree.TreeLoader
13580  * @extends Roo.util.Observable
13581  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13582  * nodes from a specified URL. The response must be a javascript Array definition
13583  * who's elements are node definition objects. eg:
13584  * <pre><code>
13585 {  success : true,
13586    data :      [
13587    
13588     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13589     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13590     ]
13591 }
13592
13593
13594 </code></pre>
13595  * <br><br>
13596  * The old style respose with just an array is still supported, but not recommended.
13597  * <br><br>
13598  *
13599  * A server request is sent, and child nodes are loaded only when a node is expanded.
13600  * The loading node's id is passed to the server under the parameter name "node" to
13601  * enable the server to produce the correct child nodes.
13602  * <br><br>
13603  * To pass extra parameters, an event handler may be attached to the "beforeload"
13604  * event, and the parameters specified in the TreeLoader's baseParams property:
13605  * <pre><code>
13606     myTreeLoader.on("beforeload", function(treeLoader, node) {
13607         this.baseParams.category = node.attributes.category;
13608     }, this);
13609     
13610 </code></pre>
13611  *
13612  * This would pass an HTTP parameter called "category" to the server containing
13613  * the value of the Node's "category" attribute.
13614  * @constructor
13615  * Creates a new Treeloader.
13616  * @param {Object} config A config object containing config properties.
13617  */
13618 Roo.tree.TreeLoader = function(config){
13619     this.baseParams = {};
13620     this.requestMethod = "POST";
13621     Roo.apply(this, config);
13622
13623     this.addEvents({
13624     
13625         /**
13626          * @event beforeload
13627          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13628          * @param {Object} This TreeLoader object.
13629          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13630          * @param {Object} callback The callback function specified in the {@link #load} call.
13631          */
13632         beforeload : true,
13633         /**
13634          * @event load
13635          * Fires when the node has been successfuly loaded.
13636          * @param {Object} This TreeLoader object.
13637          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13638          * @param {Object} response The response object containing the data from the server.
13639          */
13640         load : true,
13641         /**
13642          * @event loadexception
13643          * Fires if the network request failed.
13644          * @param {Object} This TreeLoader object.
13645          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13646          * @param {Object} response The response object containing the data from the server.
13647          */
13648         loadexception : true,
13649         /**
13650          * @event create
13651          * Fires before a node is created, enabling you to return custom Node types 
13652          * @param {Object} This TreeLoader object.
13653          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13654          */
13655         create : true
13656     });
13657
13658     Roo.tree.TreeLoader.superclass.constructor.call(this);
13659 };
13660
13661 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13662     /**
13663     * @cfg {String} dataUrl The URL from which to request a Json string which
13664     * specifies an array of node definition object representing the child nodes
13665     * to be loaded.
13666     */
13667     /**
13668     * @cfg {String} requestMethod either GET or POST
13669     * defaults to POST (due to BC)
13670     * to be loaded.
13671     */
13672     /**
13673     * @cfg {Object} baseParams (optional) An object containing properties which
13674     * specify HTTP parameters to be passed to each request for child nodes.
13675     */
13676     /**
13677     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13678     * created by this loader. If the attributes sent by the server have an attribute in this object,
13679     * they take priority.
13680     */
13681     /**
13682     * @cfg {Object} uiProviders (optional) An object containing properties which
13683     * 
13684     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13685     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13686     * <i>uiProvider</i> attribute of a returned child node is a string rather
13687     * than a reference to a TreeNodeUI implementation, this that string value
13688     * is used as a property name in the uiProviders object. You can define the provider named
13689     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13690     */
13691     uiProviders : {},
13692
13693     /**
13694     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13695     * child nodes before loading.
13696     */
13697     clearOnLoad : true,
13698
13699     /**
13700     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13701     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13702     * Grid query { data : [ .....] }
13703     */
13704     
13705     root : false,
13706      /**
13707     * @cfg {String} queryParam (optional) 
13708     * Name of the query as it will be passed on the querystring (defaults to 'node')
13709     * eg. the request will be ?node=[id]
13710     */
13711     
13712     
13713     queryParam: false,
13714     
13715     /**
13716      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13717      * This is called automatically when a node is expanded, but may be used to reload
13718      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13719      * @param {Roo.tree.TreeNode} node
13720      * @param {Function} callback
13721      */
13722     load : function(node, callback){
13723         if(this.clearOnLoad){
13724             while(node.firstChild){
13725                 node.removeChild(node.firstChild);
13726             }
13727         }
13728         if(node.attributes.children){ // preloaded json children
13729             var cs = node.attributes.children;
13730             for(var i = 0, len = cs.length; i < len; i++){
13731                 node.appendChild(this.createNode(cs[i]));
13732             }
13733             if(typeof callback == "function"){
13734                 callback();
13735             }
13736         }else if(this.dataUrl){
13737             this.requestData(node, callback);
13738         }
13739     },
13740
13741     getParams: function(node){
13742         var buf = [], bp = this.baseParams;
13743         for(var key in bp){
13744             if(typeof bp[key] != "function"){
13745                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13746             }
13747         }
13748         var n = this.queryParam === false ? 'node' : this.queryParam;
13749         buf.push(n + "=", encodeURIComponent(node.id));
13750         return buf.join("");
13751     },
13752
13753     requestData : function(node, callback){
13754         if(this.fireEvent("beforeload", this, node, callback) !== false){
13755             this.transId = Roo.Ajax.request({
13756                 method:this.requestMethod,
13757                 url: this.dataUrl||this.url,
13758                 success: this.handleResponse,
13759                 failure: this.handleFailure,
13760                 scope: this,
13761                 argument: {callback: callback, node: node},
13762                 params: this.getParams(node)
13763             });
13764         }else{
13765             // if the load is cancelled, make sure we notify
13766             // the node that we are done
13767             if(typeof callback == "function"){
13768                 callback();
13769             }
13770         }
13771     },
13772
13773     isLoading : function(){
13774         return this.transId ? true : false;
13775     },
13776
13777     abort : function(){
13778         if(this.isLoading()){
13779             Roo.Ajax.abort(this.transId);
13780         }
13781     },
13782
13783     // private
13784     createNode : function(attr)
13785     {
13786         // apply baseAttrs, nice idea Corey!
13787         if(this.baseAttrs){
13788             Roo.applyIf(attr, this.baseAttrs);
13789         }
13790         if(this.applyLoader !== false){
13791             attr.loader = this;
13792         }
13793         // uiProvider = depreciated..
13794         
13795         if(typeof(attr.uiProvider) == 'string'){
13796            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13797                 /**  eval:var:attr */ eval(attr.uiProvider);
13798         }
13799         if(typeof(this.uiProviders['default']) != 'undefined') {
13800             attr.uiProvider = this.uiProviders['default'];
13801         }
13802         
13803         this.fireEvent('create', this, attr);
13804         
13805         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13806         return(attr.leaf ?
13807                         new Roo.tree.TreeNode(attr) :
13808                         new Roo.tree.AsyncTreeNode(attr));
13809     },
13810
13811     processResponse : function(response, node, callback)
13812     {
13813         var json = response.responseText;
13814         try {
13815             
13816             var o = Roo.decode(json);
13817             
13818             if (this.root === false && typeof(o.success) != undefined) {
13819                 this.root = 'data'; // the default behaviour for list like data..
13820                 }
13821                 
13822             if (this.root !== false &&  !o.success) {
13823                 // it's a failure condition.
13824                 var a = response.argument;
13825                 this.fireEvent("loadexception", this, a.node, response);
13826                 Roo.log("Load failed - should have a handler really");
13827                 return;
13828             }
13829             
13830             
13831             
13832             if (this.root !== false) {
13833                  o = o[this.root];
13834             }
13835             
13836             for(var i = 0, len = o.length; i < len; i++){
13837                 var n = this.createNode(o[i]);
13838                 if(n){
13839                     node.appendChild(n);
13840                 }
13841             }
13842             if(typeof callback == "function"){
13843                 callback(this, node);
13844             }
13845         }catch(e){
13846             this.handleFailure(response);
13847         }
13848     },
13849
13850     handleResponse : function(response){
13851         this.transId = false;
13852         var a = response.argument;
13853         this.processResponse(response, a.node, a.callback);
13854         this.fireEvent("load", this, a.node, response);
13855     },
13856
13857     handleFailure : function(response)
13858     {
13859         // should handle failure better..
13860         this.transId = false;
13861         var a = response.argument;
13862         this.fireEvent("loadexception", this, a.node, response);
13863         if(typeof a.callback == "function"){
13864             a.callback(this, a.node);
13865         }
13866     }
13867 });/*
13868  * Based on:
13869  * Ext JS Library 1.1.1
13870  * Copyright(c) 2006-2007, Ext JS, LLC.
13871  *
13872  * Originally Released Under LGPL - original licence link has changed is not relivant.
13873  *
13874  * Fork - LGPL
13875  * <script type="text/javascript">
13876  */
13877
13878 /**
13879 * @class Roo.tree.TreeFilter
13880 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13881 * @param {TreePanel} tree
13882 * @param {Object} config (optional)
13883  */
13884 Roo.tree.TreeFilter = function(tree, config){
13885     this.tree = tree;
13886     this.filtered = {};
13887     Roo.apply(this, config);
13888 };
13889
13890 Roo.tree.TreeFilter.prototype = {
13891     clearBlank:false,
13892     reverse:false,
13893     autoClear:false,
13894     remove:false,
13895
13896      /**
13897      * Filter the data by a specific attribute.
13898      * @param {String/RegExp} value Either string that the attribute value
13899      * should start with or a RegExp to test against the attribute
13900      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13901      * @param {TreeNode} startNode (optional) The node to start the filter at.
13902      */
13903     filter : function(value, attr, startNode){
13904         attr = attr || "text";
13905         var f;
13906         if(typeof value == "string"){
13907             var vlen = value.length;
13908             // auto clear empty filter
13909             if(vlen == 0 && this.clearBlank){
13910                 this.clear();
13911                 return;
13912             }
13913             value = value.toLowerCase();
13914             f = function(n){
13915                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13916             };
13917         }else if(value.exec){ // regex?
13918             f = function(n){
13919                 return value.test(n.attributes[attr]);
13920             };
13921         }else{
13922             throw 'Illegal filter type, must be string or regex';
13923         }
13924         this.filterBy(f, null, startNode);
13925         },
13926
13927     /**
13928      * Filter by a function. The passed function will be called with each
13929      * node in the tree (or from the startNode). If the function returns true, the node is kept
13930      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13931      * @param {Function} fn The filter function
13932      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13933      */
13934     filterBy : function(fn, scope, startNode){
13935         startNode = startNode || this.tree.root;
13936         if(this.autoClear){
13937             this.clear();
13938         }
13939         var af = this.filtered, rv = this.reverse;
13940         var f = function(n){
13941             if(n == startNode){
13942                 return true;
13943             }
13944             if(af[n.id]){
13945                 return false;
13946             }
13947             var m = fn.call(scope || n, n);
13948             if(!m || rv){
13949                 af[n.id] = n;
13950                 n.ui.hide();
13951                 return false;
13952             }
13953             return true;
13954         };
13955         startNode.cascade(f);
13956         if(this.remove){
13957            for(var id in af){
13958                if(typeof id != "function"){
13959                    var n = af[id];
13960                    if(n && n.parentNode){
13961                        n.parentNode.removeChild(n);
13962                    }
13963                }
13964            }
13965         }
13966     },
13967
13968     /**
13969      * Clears the current filter. Note: with the "remove" option
13970      * set a filter cannot be cleared.
13971      */
13972     clear : function(){
13973         var t = this.tree;
13974         var af = this.filtered;
13975         for(var id in af){
13976             if(typeof id != "function"){
13977                 var n = af[id];
13978                 if(n){
13979                     n.ui.show();
13980                 }
13981             }
13982         }
13983         this.filtered = {};
13984     }
13985 };
13986 /*
13987  * Based on:
13988  * Ext JS Library 1.1.1
13989  * Copyright(c) 2006-2007, Ext JS, LLC.
13990  *
13991  * Originally Released Under LGPL - original licence link has changed is not relivant.
13992  *
13993  * Fork - LGPL
13994  * <script type="text/javascript">
13995  */
13996  
13997
13998 /**
13999  * @class Roo.tree.TreeSorter
14000  * Provides sorting of nodes in a TreePanel
14001  * 
14002  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14003  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14004  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14005  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14006  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14007  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14008  * @constructor
14009  * @param {TreePanel} tree
14010  * @param {Object} config
14011  */
14012 Roo.tree.TreeSorter = function(tree, config){
14013     Roo.apply(this, config);
14014     tree.on("beforechildrenrendered", this.doSort, this);
14015     tree.on("append", this.updateSort, this);
14016     tree.on("insert", this.updateSort, this);
14017     
14018     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14019     var p = this.property || "text";
14020     var sortType = this.sortType;
14021     var fs = this.folderSort;
14022     var cs = this.caseSensitive === true;
14023     var leafAttr = this.leafAttr || 'leaf';
14024
14025     this.sortFn = function(n1, n2){
14026         if(fs){
14027             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14028                 return 1;
14029             }
14030             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14031                 return -1;
14032             }
14033         }
14034         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14035         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14036         if(v1 < v2){
14037                         return dsc ? +1 : -1;
14038                 }else if(v1 > v2){
14039                         return dsc ? -1 : +1;
14040         }else{
14041                 return 0;
14042         }
14043     };
14044 };
14045
14046 Roo.tree.TreeSorter.prototype = {
14047     doSort : function(node){
14048         node.sort(this.sortFn);
14049     },
14050     
14051     compareNodes : function(n1, n2){
14052         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14053     },
14054     
14055     updateSort : function(tree, node){
14056         if(node.childrenRendered){
14057             this.doSort.defer(1, this, [node]);
14058         }
14059     }
14060 };/*
14061  * Based on:
14062  * Ext JS Library 1.1.1
14063  * Copyright(c) 2006-2007, Ext JS, LLC.
14064  *
14065  * Originally Released Under LGPL - original licence link has changed is not relivant.
14066  *
14067  * Fork - LGPL
14068  * <script type="text/javascript">
14069  */
14070
14071 if(Roo.dd.DropZone){
14072     
14073 Roo.tree.TreeDropZone = function(tree, config){
14074     this.allowParentInsert = false;
14075     this.allowContainerDrop = false;
14076     this.appendOnly = false;
14077     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14078     this.tree = tree;
14079     this.lastInsertClass = "x-tree-no-status";
14080     this.dragOverData = {};
14081 };
14082
14083 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14084     ddGroup : "TreeDD",
14085     scroll:  true,
14086     
14087     expandDelay : 1000,
14088     
14089     expandNode : function(node){
14090         if(node.hasChildNodes() && !node.isExpanded()){
14091             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14092         }
14093     },
14094     
14095     queueExpand : function(node){
14096         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14097     },
14098     
14099     cancelExpand : function(){
14100         if(this.expandProcId){
14101             clearTimeout(this.expandProcId);
14102             this.expandProcId = false;
14103         }
14104     },
14105     
14106     isValidDropPoint : function(n, pt, dd, e, data){
14107         if(!n || !data){ return false; }
14108         var targetNode = n.node;
14109         var dropNode = data.node;
14110         // default drop rules
14111         if(!(targetNode && targetNode.isTarget && pt)){
14112             return false;
14113         }
14114         if(pt == "append" && targetNode.allowChildren === false){
14115             return false;
14116         }
14117         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14118             return false;
14119         }
14120         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14121             return false;
14122         }
14123         // reuse the object
14124         var overEvent = this.dragOverData;
14125         overEvent.tree = this.tree;
14126         overEvent.target = targetNode;
14127         overEvent.data = data;
14128         overEvent.point = pt;
14129         overEvent.source = dd;
14130         overEvent.rawEvent = e;
14131         overEvent.dropNode = dropNode;
14132         overEvent.cancel = false;  
14133         var result = this.tree.fireEvent("nodedragover", overEvent);
14134         return overEvent.cancel === false && result !== false;
14135     },
14136     
14137     getDropPoint : function(e, n, dd)
14138     {
14139         var tn = n.node;
14140         if(tn.isRoot){
14141             return tn.allowChildren !== false ? "append" : false; // always append for root
14142         }
14143         var dragEl = n.ddel;
14144         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14145         var y = Roo.lib.Event.getPageY(e);
14146         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14147         
14148         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14149         var noAppend = tn.allowChildren === false;
14150         if(this.appendOnly || tn.parentNode.allowChildren === false){
14151             return noAppend ? false : "append";
14152         }
14153         var noBelow = false;
14154         if(!this.allowParentInsert){
14155             noBelow = tn.hasChildNodes() && tn.isExpanded();
14156         }
14157         var q = (b - t) / (noAppend ? 2 : 3);
14158         if(y >= t && y < (t + q)){
14159             return "above";
14160         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14161             return "below";
14162         }else{
14163             return "append";
14164         }
14165     },
14166     
14167     onNodeEnter : function(n, dd, e, data)
14168     {
14169         this.cancelExpand();
14170     },
14171     
14172     onNodeOver : function(n, dd, e, data)
14173     {
14174        
14175         var pt = this.getDropPoint(e, n, dd);
14176         var node = n.node;
14177         
14178         // auto node expand check
14179         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14180             this.queueExpand(node);
14181         }else if(pt != "append"){
14182             this.cancelExpand();
14183         }
14184         
14185         // set the insert point style on the target node
14186         var returnCls = this.dropNotAllowed;
14187         if(this.isValidDropPoint(n, pt, dd, e, data)){
14188            if(pt){
14189                var el = n.ddel;
14190                var cls;
14191                if(pt == "above"){
14192                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14193                    cls = "x-tree-drag-insert-above";
14194                }else if(pt == "below"){
14195                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14196                    cls = "x-tree-drag-insert-below";
14197                }else{
14198                    returnCls = "x-tree-drop-ok-append";
14199                    cls = "x-tree-drag-append";
14200                }
14201                if(this.lastInsertClass != cls){
14202                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14203                    this.lastInsertClass = cls;
14204                }
14205            }
14206        }
14207        return returnCls;
14208     },
14209     
14210     onNodeOut : function(n, dd, e, data){
14211         
14212         this.cancelExpand();
14213         this.removeDropIndicators(n);
14214     },
14215     
14216     onNodeDrop : function(n, dd, e, data){
14217         var point = this.getDropPoint(e, n, dd);
14218         var targetNode = n.node;
14219         targetNode.ui.startDrop();
14220         if(!this.isValidDropPoint(n, point, dd, e, data)){
14221             targetNode.ui.endDrop();
14222             return false;
14223         }
14224         // first try to find the drop node
14225         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14226         var dropEvent = {
14227             tree : this.tree,
14228             target: targetNode,
14229             data: data,
14230             point: point,
14231             source: dd,
14232             rawEvent: e,
14233             dropNode: dropNode,
14234             cancel: !dropNode   
14235         };
14236         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14237         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14238             targetNode.ui.endDrop();
14239             return false;
14240         }
14241         // allow target changing
14242         targetNode = dropEvent.target;
14243         if(point == "append" && !targetNode.isExpanded()){
14244             targetNode.expand(false, null, function(){
14245                 this.completeDrop(dropEvent);
14246             }.createDelegate(this));
14247         }else{
14248             this.completeDrop(dropEvent);
14249         }
14250         return true;
14251     },
14252     
14253     completeDrop : function(de){
14254         var ns = de.dropNode, p = de.point, t = de.target;
14255         if(!(ns instanceof Array)){
14256             ns = [ns];
14257         }
14258         var n;
14259         for(var i = 0, len = ns.length; i < len; i++){
14260             n = ns[i];
14261             if(p == "above"){
14262                 t.parentNode.insertBefore(n, t);
14263             }else if(p == "below"){
14264                 t.parentNode.insertBefore(n, t.nextSibling);
14265             }else{
14266                 t.appendChild(n);
14267             }
14268         }
14269         n.ui.focus();
14270         if(this.tree.hlDrop){
14271             n.ui.highlight();
14272         }
14273         t.ui.endDrop();
14274         this.tree.fireEvent("nodedrop", de);
14275     },
14276     
14277     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14278         if(this.tree.hlDrop){
14279             dropNode.ui.focus();
14280             dropNode.ui.highlight();
14281         }
14282         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14283     },
14284     
14285     getTree : function(){
14286         return this.tree;
14287     },
14288     
14289     removeDropIndicators : function(n){
14290         if(n && n.ddel){
14291             var el = n.ddel;
14292             Roo.fly(el).removeClass([
14293                     "x-tree-drag-insert-above",
14294                     "x-tree-drag-insert-below",
14295                     "x-tree-drag-append"]);
14296             this.lastInsertClass = "_noclass";
14297         }
14298     },
14299     
14300     beforeDragDrop : function(target, e, id){
14301         this.cancelExpand();
14302         return true;
14303     },
14304     
14305     afterRepair : function(data){
14306         if(data && Roo.enableFx){
14307             data.node.ui.highlight();
14308         }
14309         this.hideProxy();
14310     } 
14311     
14312 });
14313
14314 }
14315 /*
14316  * Based on:
14317  * Ext JS Library 1.1.1
14318  * Copyright(c) 2006-2007, Ext JS, LLC.
14319  *
14320  * Originally Released Under LGPL - original licence link has changed is not relivant.
14321  *
14322  * Fork - LGPL
14323  * <script type="text/javascript">
14324  */
14325  
14326
14327 if(Roo.dd.DragZone){
14328 Roo.tree.TreeDragZone = function(tree, config){
14329     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14330     this.tree = tree;
14331 };
14332
14333 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14334     ddGroup : "TreeDD",
14335    
14336     onBeforeDrag : function(data, e){
14337         var n = data.node;
14338         return n && n.draggable && !n.disabled;
14339     },
14340      
14341     
14342     onInitDrag : function(e){
14343         var data = this.dragData;
14344         this.tree.getSelectionModel().select(data.node);
14345         this.proxy.update("");
14346         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14347         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14348     },
14349     
14350     getRepairXY : function(e, data){
14351         return data.node.ui.getDDRepairXY();
14352     },
14353     
14354     onEndDrag : function(data, e){
14355         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14356         
14357         
14358     },
14359     
14360     onValidDrop : function(dd, e, id){
14361         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14362         this.hideProxy();
14363     },
14364     
14365     beforeInvalidDrop : function(e, id){
14366         // this scrolls the original position back into view
14367         var sm = this.tree.getSelectionModel();
14368         sm.clearSelections();
14369         sm.select(this.dragData.node);
14370     }
14371 });
14372 }/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382 /**
14383  * @class Roo.tree.TreeEditor
14384  * @extends Roo.Editor
14385  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14386  * as the editor field.
14387  * @constructor
14388  * @param {Object} config (used to be the tree panel.)
14389  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14390  * 
14391  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14392  * @cfg {Roo.form.TextField|Object} field The field configuration
14393  *
14394  * 
14395  */
14396 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14397     var tree = config;
14398     var field;
14399     if (oldconfig) { // old style..
14400         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14401     } else {
14402         // new style..
14403         tree = config.tree;
14404         config.field = config.field  || {};
14405         config.field.xtype = 'TextField';
14406         field = Roo.factory(config.field, Roo.form);
14407     }
14408     config = config || {};
14409     
14410     
14411     this.addEvents({
14412         /**
14413          * @event beforenodeedit
14414          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14415          * false from the handler of this event.
14416          * @param {Editor} this
14417          * @param {Roo.tree.Node} node 
14418          */
14419         "beforenodeedit" : true
14420     });
14421     
14422     //Roo.log(config);
14423     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14424
14425     this.tree = tree;
14426
14427     tree.on('beforeclick', this.beforeNodeClick, this);
14428     tree.getTreeEl().on('mousedown', this.hide, this);
14429     this.on('complete', this.updateNode, this);
14430     this.on('beforestartedit', this.fitToTree, this);
14431     this.on('startedit', this.bindScroll, this, {delay:10});
14432     this.on('specialkey', this.onSpecialKey, this);
14433 };
14434
14435 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14436     /**
14437      * @cfg {String} alignment
14438      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14439      */
14440     alignment: "l-l",
14441     // inherit
14442     autoSize: false,
14443     /**
14444      * @cfg {Boolean} hideEl
14445      * True to hide the bound element while the editor is displayed (defaults to false)
14446      */
14447     hideEl : false,
14448     /**
14449      * @cfg {String} cls
14450      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14451      */
14452     cls: "x-small-editor x-tree-editor",
14453     /**
14454      * @cfg {Boolean} shim
14455      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14456      */
14457     shim:false,
14458     // inherit
14459     shadow:"frame",
14460     /**
14461      * @cfg {Number} maxWidth
14462      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14463      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14464      * scroll and client offsets into account prior to each edit.
14465      */
14466     maxWidth: 250,
14467
14468     editDelay : 350,
14469
14470     // private
14471     fitToTree : function(ed, el){
14472         var td = this.tree.getTreeEl().dom, nd = el.dom;
14473         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14474             td.scrollLeft = nd.offsetLeft;
14475         }
14476         var w = Math.min(
14477                 this.maxWidth,
14478                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14479         this.setSize(w, '');
14480         
14481         return this.fireEvent('beforenodeedit', this, this.editNode);
14482         
14483     },
14484
14485     // private
14486     triggerEdit : function(node){
14487         this.completeEdit();
14488         this.editNode = node;
14489         this.startEdit(node.ui.textNode, node.text);
14490     },
14491
14492     // private
14493     bindScroll : function(){
14494         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14495     },
14496
14497     // private
14498     beforeNodeClick : function(node, e){
14499         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14500         this.lastClick = new Date();
14501         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14502             e.stopEvent();
14503             this.triggerEdit(node);
14504             return false;
14505         }
14506         return true;
14507     },
14508
14509     // private
14510     updateNode : function(ed, value){
14511         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14512         this.editNode.setText(value);
14513     },
14514
14515     // private
14516     onHide : function(){
14517         Roo.tree.TreeEditor.superclass.onHide.call(this);
14518         if(this.editNode){
14519             this.editNode.ui.focus();
14520         }
14521     },
14522
14523     // private
14524     onSpecialKey : function(field, e){
14525         var k = e.getKey();
14526         if(k == e.ESC){
14527             e.stopEvent();
14528             this.cancelEdit();
14529         }else if(k == e.ENTER && !e.hasModifier()){
14530             e.stopEvent();
14531             this.completeEdit();
14532         }
14533     }
14534 });//<Script type="text/javascript">
14535 /*
14536  * Based on:
14537  * Ext JS Library 1.1.1
14538  * Copyright(c) 2006-2007, Ext JS, LLC.
14539  *
14540  * Originally Released Under LGPL - original licence link has changed is not relivant.
14541  *
14542  * Fork - LGPL
14543  * <script type="text/javascript">
14544  */
14545  
14546 /**
14547  * Not documented??? - probably should be...
14548  */
14549
14550 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14551     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14552     
14553     renderElements : function(n, a, targetNode, bulkRender){
14554         //consel.log("renderElements?");
14555         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14556
14557         var t = n.getOwnerTree();
14558         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14559         
14560         var cols = t.columns;
14561         var bw = t.borderWidth;
14562         var c = cols[0];
14563         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14564          var cb = typeof a.checked == "boolean";
14565         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14566         var colcls = 'x-t-' + tid + '-c0';
14567         var buf = [
14568             '<li class="x-tree-node">',
14569             
14570                 
14571                 '<div class="x-tree-node-el ', a.cls,'">',
14572                     // extran...
14573                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14574                 
14575                 
14576                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14577                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14578                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14579                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14580                            (a.iconCls ? ' '+a.iconCls : ''),
14581                            '" unselectable="on" />',
14582                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14583                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14584                              
14585                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14586                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14587                             '<span unselectable="on" qtip="' + tx + '">',
14588                              tx,
14589                              '</span></a>' ,
14590                     '</div>',
14591                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14592                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14593                  ];
14594         for(var i = 1, len = cols.length; i < len; i++){
14595             c = cols[i];
14596             colcls = 'x-t-' + tid + '-c' +i;
14597             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14598             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14599                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14600                       "</div>");
14601          }
14602          
14603          buf.push(
14604             '</a>',
14605             '<div class="x-clear"></div></div>',
14606             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14607             "</li>");
14608         
14609         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14610             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14611                                 n.nextSibling.ui.getEl(), buf.join(""));
14612         }else{
14613             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14614         }
14615         var el = this.wrap.firstChild;
14616         this.elRow = el;
14617         this.elNode = el.firstChild;
14618         this.ranchor = el.childNodes[1];
14619         this.ctNode = this.wrap.childNodes[1];
14620         var cs = el.firstChild.childNodes;
14621         this.indentNode = cs[0];
14622         this.ecNode = cs[1];
14623         this.iconNode = cs[2];
14624         var index = 3;
14625         if(cb){
14626             this.checkbox = cs[3];
14627             index++;
14628         }
14629         this.anchor = cs[index];
14630         
14631         this.textNode = cs[index].firstChild;
14632         
14633         //el.on("click", this.onClick, this);
14634         //el.on("dblclick", this.onDblClick, this);
14635         
14636         
14637        // console.log(this);
14638     },
14639     initEvents : function(){
14640         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14641         
14642             
14643         var a = this.ranchor;
14644
14645         var el = Roo.get(a);
14646
14647         if(Roo.isOpera){ // opera render bug ignores the CSS
14648             el.setStyle("text-decoration", "none");
14649         }
14650
14651         el.on("click", this.onClick, this);
14652         el.on("dblclick", this.onDblClick, this);
14653         el.on("contextmenu", this.onContextMenu, this);
14654         
14655     },
14656     
14657     /*onSelectedChange : function(state){
14658         if(state){
14659             this.focus();
14660             this.addClass("x-tree-selected");
14661         }else{
14662             //this.blur();
14663             this.removeClass("x-tree-selected");
14664         }
14665     },*/
14666     addClass : function(cls){
14667         if(this.elRow){
14668             Roo.fly(this.elRow).addClass(cls);
14669         }
14670         
14671     },
14672     
14673     
14674     removeClass : function(cls){
14675         if(this.elRow){
14676             Roo.fly(this.elRow).removeClass(cls);
14677         }
14678     }
14679
14680     
14681     
14682 });//<Script type="text/javascript">
14683
14684 /*
14685  * Based on:
14686  * Ext JS Library 1.1.1
14687  * Copyright(c) 2006-2007, Ext JS, LLC.
14688  *
14689  * Originally Released Under LGPL - original licence link has changed is not relivant.
14690  *
14691  * Fork - LGPL
14692  * <script type="text/javascript">
14693  */
14694  
14695
14696 /**
14697  * @class Roo.tree.ColumnTree
14698  * @extends Roo.data.TreePanel
14699  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14700  * @cfg {int} borderWidth  compined right/left border allowance
14701  * @constructor
14702  * @param {String/HTMLElement/Element} el The container element
14703  * @param {Object} config
14704  */
14705 Roo.tree.ColumnTree =  function(el, config)
14706 {
14707    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14708    this.addEvents({
14709         /**
14710         * @event resize
14711         * Fire this event on a container when it resizes
14712         * @param {int} w Width
14713         * @param {int} h Height
14714         */
14715        "resize" : true
14716     });
14717     this.on('resize', this.onResize, this);
14718 };
14719
14720 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14721     //lines:false,
14722     
14723     
14724     borderWidth: Roo.isBorderBox ? 0 : 2, 
14725     headEls : false,
14726     
14727     render : function(){
14728         // add the header.....
14729        
14730         Roo.tree.ColumnTree.superclass.render.apply(this);
14731         
14732         this.el.addClass('x-column-tree');
14733         
14734         this.headers = this.el.createChild(
14735             {cls:'x-tree-headers'},this.innerCt.dom);
14736    
14737         var cols = this.columns, c;
14738         var totalWidth = 0;
14739         this.headEls = [];
14740         var  len = cols.length;
14741         for(var i = 0; i < len; i++){
14742              c = cols[i];
14743              totalWidth += c.width;
14744             this.headEls.push(this.headers.createChild({
14745                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14746                  cn: {
14747                      cls:'x-tree-hd-text',
14748                      html: c.header
14749                  },
14750                  style:'width:'+(c.width-this.borderWidth)+'px;'
14751              }));
14752         }
14753         this.headers.createChild({cls:'x-clear'});
14754         // prevent floats from wrapping when clipped
14755         this.headers.setWidth(totalWidth);
14756         //this.innerCt.setWidth(totalWidth);
14757         this.innerCt.setStyle({ overflow: 'auto' });
14758         this.onResize(this.width, this.height);
14759              
14760         
14761     },
14762     onResize : function(w,h)
14763     {
14764         this.height = h;
14765         this.width = w;
14766         // resize cols..
14767         this.innerCt.setWidth(this.width);
14768         this.innerCt.setHeight(this.height-20);
14769         
14770         // headers...
14771         var cols = this.columns, c;
14772         var totalWidth = 0;
14773         var expEl = false;
14774         var len = cols.length;
14775         for(var i = 0; i < len; i++){
14776             c = cols[i];
14777             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14778                 // it's the expander..
14779                 expEl  = this.headEls[i];
14780                 continue;
14781             }
14782             totalWidth += c.width;
14783             
14784         }
14785         if (expEl) {
14786             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14787         }
14788         this.headers.setWidth(w-20);
14789
14790         
14791         
14792         
14793     }
14794 });
14795 /*
14796  * Based on:
14797  * Ext JS Library 1.1.1
14798  * Copyright(c) 2006-2007, Ext JS, LLC.
14799  *
14800  * Originally Released Under LGPL - original licence link has changed is not relivant.
14801  *
14802  * Fork - LGPL
14803  * <script type="text/javascript">
14804  */
14805  
14806 /**
14807  * @class Roo.menu.Menu
14808  * @extends Roo.util.Observable
14809  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14810  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14811  * @constructor
14812  * Creates a new Menu
14813  * @param {Object} config Configuration options
14814  */
14815 Roo.menu.Menu = function(config){
14816     
14817     Roo.menu.Menu.superclass.constructor.call(this, config);
14818     
14819     this.id = this.id || Roo.id();
14820     this.addEvents({
14821         /**
14822          * @event beforeshow
14823          * Fires before this menu is displayed
14824          * @param {Roo.menu.Menu} this
14825          */
14826         beforeshow : true,
14827         /**
14828          * @event beforehide
14829          * Fires before this menu is hidden
14830          * @param {Roo.menu.Menu} this
14831          */
14832         beforehide : true,
14833         /**
14834          * @event show
14835          * Fires after this menu is displayed
14836          * @param {Roo.menu.Menu} this
14837          */
14838         show : true,
14839         /**
14840          * @event hide
14841          * Fires after this menu is hidden
14842          * @param {Roo.menu.Menu} this
14843          */
14844         hide : true,
14845         /**
14846          * @event click
14847          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14848          * @param {Roo.menu.Menu} this
14849          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14850          * @param {Roo.EventObject} e
14851          */
14852         click : true,
14853         /**
14854          * @event mouseover
14855          * Fires when the mouse is hovering over this menu
14856          * @param {Roo.menu.Menu} this
14857          * @param {Roo.EventObject} e
14858          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14859          */
14860         mouseover : true,
14861         /**
14862          * @event mouseout
14863          * Fires when the mouse exits this menu
14864          * @param {Roo.menu.Menu} this
14865          * @param {Roo.EventObject} e
14866          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14867          */
14868         mouseout : true,
14869         /**
14870          * @event itemclick
14871          * Fires when a menu item contained in this menu is clicked
14872          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14873          * @param {Roo.EventObject} e
14874          */
14875         itemclick: true
14876     });
14877     if (this.registerMenu) {
14878         Roo.menu.MenuMgr.register(this);
14879     }
14880     
14881     var mis = this.items;
14882     this.items = new Roo.util.MixedCollection();
14883     if(mis){
14884         this.add.apply(this, mis);
14885     }
14886 };
14887
14888 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14889     /**
14890      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14891      */
14892     minWidth : 120,
14893     /**
14894      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14895      * for bottom-right shadow (defaults to "sides")
14896      */
14897     shadow : "sides",
14898     /**
14899      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14900      * this menu (defaults to "tl-tr?")
14901      */
14902     subMenuAlign : "tl-tr?",
14903     /**
14904      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14905      * relative to its element of origin (defaults to "tl-bl?")
14906      */
14907     defaultAlign : "tl-bl?",
14908     /**
14909      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14910      */
14911     allowOtherMenus : false,
14912     /**
14913      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14914      */
14915     registerMenu : true,
14916
14917     hidden:true,
14918
14919     // private
14920     render : function(){
14921         if(this.el){
14922             return;
14923         }
14924         var el = this.el = new Roo.Layer({
14925             cls: "x-menu",
14926             shadow:this.shadow,
14927             constrain: false,
14928             parentEl: this.parentEl || document.body,
14929             zindex:15000
14930         });
14931
14932         this.keyNav = new Roo.menu.MenuNav(this);
14933
14934         if(this.plain){
14935             el.addClass("x-menu-plain");
14936         }
14937         if(this.cls){
14938             el.addClass(this.cls);
14939         }
14940         // generic focus element
14941         this.focusEl = el.createChild({
14942             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14943         });
14944         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14945         //disabling touch- as it's causing issues ..
14946         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14947         ul.on('click'   , this.onClick, this);
14948         
14949         
14950         ul.on("mouseover", this.onMouseOver, this);
14951         ul.on("mouseout", this.onMouseOut, this);
14952         this.items.each(function(item){
14953             if (item.hidden) {
14954                 return;
14955             }
14956             
14957             var li = document.createElement("li");
14958             li.className = "x-menu-list-item";
14959             ul.dom.appendChild(li);
14960             item.render(li, this);
14961         }, this);
14962         this.ul = ul;
14963         this.autoWidth();
14964     },
14965
14966     // private
14967     autoWidth : function(){
14968         var el = this.el, ul = this.ul;
14969         if(!el){
14970             return;
14971         }
14972         var w = this.width;
14973         if(w){
14974             el.setWidth(w);
14975         }else if(Roo.isIE){
14976             el.setWidth(this.minWidth);
14977             var t = el.dom.offsetWidth; // force recalc
14978             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14979         }
14980     },
14981
14982     // private
14983     delayAutoWidth : function(){
14984         if(this.rendered){
14985             if(!this.awTask){
14986                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14987             }
14988             this.awTask.delay(20);
14989         }
14990     },
14991
14992     // private
14993     findTargetItem : function(e){
14994         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14995         if(t && t.menuItemId){
14996             return this.items.get(t.menuItemId);
14997         }
14998     },
14999
15000     // private
15001     onClick : function(e){
15002         Roo.log("menu.onClick");
15003         var t = this.findTargetItem(e);
15004         if(!t){
15005             return;
15006         }
15007         Roo.log(e);
15008         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
15009             if(t == this.activeItem && t.shouldDeactivate(e)){
15010                 this.activeItem.deactivate();
15011                 delete this.activeItem;
15012                 return;
15013             }
15014             if(t.canActivate){
15015                 this.setActiveItem(t, true);
15016             }
15017             return;
15018             
15019             
15020         }
15021         
15022         t.onClick(e);
15023         this.fireEvent("click", this, t, e);
15024     },
15025
15026     // private
15027     setActiveItem : function(item, autoExpand){
15028         if(item != this.activeItem){
15029             if(this.activeItem){
15030                 this.activeItem.deactivate();
15031             }
15032             this.activeItem = item;
15033             item.activate(autoExpand);
15034         }else if(autoExpand){
15035             item.expandMenu();
15036         }
15037     },
15038
15039     // private
15040     tryActivate : function(start, step){
15041         var items = this.items;
15042         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15043             var item = items.get(i);
15044             if(!item.disabled && item.canActivate){
15045                 this.setActiveItem(item, false);
15046                 return item;
15047             }
15048         }
15049         return false;
15050     },
15051
15052     // private
15053     onMouseOver : function(e){
15054         var t;
15055         if(t = this.findTargetItem(e)){
15056             if(t.canActivate && !t.disabled){
15057                 this.setActiveItem(t, true);
15058             }
15059         }
15060         this.fireEvent("mouseover", this, e, t);
15061     },
15062
15063     // private
15064     onMouseOut : function(e){
15065         var t;
15066         if(t = this.findTargetItem(e)){
15067             if(t == this.activeItem && t.shouldDeactivate(e)){
15068                 this.activeItem.deactivate();
15069                 delete this.activeItem;
15070             }
15071         }
15072         this.fireEvent("mouseout", this, e, t);
15073     },
15074
15075     /**
15076      * Read-only.  Returns true if the menu is currently displayed, else false.
15077      * @type Boolean
15078      */
15079     isVisible : function(){
15080         return this.el && !this.hidden;
15081     },
15082
15083     /**
15084      * Displays this menu relative to another element
15085      * @param {String/HTMLElement/Roo.Element} element The element to align to
15086      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15087      * the element (defaults to this.defaultAlign)
15088      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15089      */
15090     show : function(el, pos, parentMenu){
15091         this.parentMenu = parentMenu;
15092         if(!this.el){
15093             this.render();
15094         }
15095         this.fireEvent("beforeshow", this);
15096         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15097     },
15098
15099     /**
15100      * Displays this menu at a specific xy position
15101      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15102      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15103      */
15104     showAt : function(xy, parentMenu, /* private: */_e){
15105         this.parentMenu = parentMenu;
15106         if(!this.el){
15107             this.render();
15108         }
15109         if(_e !== false){
15110             this.fireEvent("beforeshow", this);
15111             xy = this.el.adjustForConstraints(xy);
15112         }
15113         this.el.setXY(xy);
15114         this.el.show();
15115         this.hidden = false;
15116         this.focus();
15117         this.fireEvent("show", this);
15118     },
15119
15120     focus : function(){
15121         if(!this.hidden){
15122             this.doFocus.defer(50, this);
15123         }
15124     },
15125
15126     doFocus : function(){
15127         if(!this.hidden){
15128             this.focusEl.focus();
15129         }
15130     },
15131
15132     /**
15133      * Hides this menu and optionally all parent menus
15134      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15135      */
15136     hide : function(deep){
15137         if(this.el && this.isVisible()){
15138             this.fireEvent("beforehide", this);
15139             if(this.activeItem){
15140                 this.activeItem.deactivate();
15141                 this.activeItem = null;
15142             }
15143             this.el.hide();
15144             this.hidden = true;
15145             this.fireEvent("hide", this);
15146         }
15147         if(deep === true && this.parentMenu){
15148             this.parentMenu.hide(true);
15149         }
15150     },
15151
15152     /**
15153      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15154      * Any of the following are valid:
15155      * <ul>
15156      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15157      * <li>An HTMLElement object which will be converted to a menu item</li>
15158      * <li>A menu item config object that will be created as a new menu item</li>
15159      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15160      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15161      * </ul>
15162      * Usage:
15163      * <pre><code>
15164 // Create the menu
15165 var menu = new Roo.menu.Menu();
15166
15167 // Create a menu item to add by reference
15168 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15169
15170 // Add a bunch of items at once using different methods.
15171 // Only the last item added will be returned.
15172 var item = menu.add(
15173     menuItem,                // add existing item by ref
15174     'Dynamic Item',          // new TextItem
15175     '-',                     // new separator
15176     { text: 'Config Item' }  // new item by config
15177 );
15178 </code></pre>
15179      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15180      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15181      */
15182     add : function(){
15183         var a = arguments, l = a.length, item;
15184         for(var i = 0; i < l; i++){
15185             var el = a[i];
15186             if ((typeof(el) == "object") && el.xtype && el.xns) {
15187                 el = Roo.factory(el, Roo.menu);
15188             }
15189             
15190             if(el.render){ // some kind of Item
15191                 item = this.addItem(el);
15192             }else if(typeof el == "string"){ // string
15193                 if(el == "separator" || el == "-"){
15194                     item = this.addSeparator();
15195                 }else{
15196                     item = this.addText(el);
15197                 }
15198             }else if(el.tagName || el.el){ // element
15199                 item = this.addElement(el);
15200             }else if(typeof el == "object"){ // must be menu item config?
15201                 item = this.addMenuItem(el);
15202             }
15203         }
15204         return item;
15205     },
15206
15207     /**
15208      * Returns this menu's underlying {@link Roo.Element} object
15209      * @return {Roo.Element} The element
15210      */
15211     getEl : function(){
15212         if(!this.el){
15213             this.render();
15214         }
15215         return this.el;
15216     },
15217
15218     /**
15219      * Adds a separator bar to the menu
15220      * @return {Roo.menu.Item} The menu item that was added
15221      */
15222     addSeparator : function(){
15223         return this.addItem(new Roo.menu.Separator());
15224     },
15225
15226     /**
15227      * Adds an {@link Roo.Element} object to the menu
15228      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15229      * @return {Roo.menu.Item} The menu item that was added
15230      */
15231     addElement : function(el){
15232         return this.addItem(new Roo.menu.BaseItem(el));
15233     },
15234
15235     /**
15236      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15237      * @param {Roo.menu.Item} item The menu item to add
15238      * @return {Roo.menu.Item} The menu item that was added
15239      */
15240     addItem : function(item){
15241         this.items.add(item);
15242         if(this.ul){
15243             var li = document.createElement("li");
15244             li.className = "x-menu-list-item";
15245             this.ul.dom.appendChild(li);
15246             item.render(li, this);
15247             this.delayAutoWidth();
15248         }
15249         return item;
15250     },
15251
15252     /**
15253      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15254      * @param {Object} config A MenuItem config object
15255      * @return {Roo.menu.Item} The menu item that was added
15256      */
15257     addMenuItem : function(config){
15258         if(!(config instanceof Roo.menu.Item)){
15259             if(typeof config.checked == "boolean"){ // must be check menu item config?
15260                 config = new Roo.menu.CheckItem(config);
15261             }else{
15262                 config = new Roo.menu.Item(config);
15263             }
15264         }
15265         return this.addItem(config);
15266     },
15267
15268     /**
15269      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15270      * @param {String} text The text to display in the menu item
15271      * @return {Roo.menu.Item} The menu item that was added
15272      */
15273     addText : function(text){
15274         return this.addItem(new Roo.menu.TextItem({ text : text }));
15275     },
15276
15277     /**
15278      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15279      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15280      * @param {Roo.menu.Item} item The menu item to add
15281      * @return {Roo.menu.Item} The menu item that was added
15282      */
15283     insert : function(index, item){
15284         this.items.insert(index, item);
15285         if(this.ul){
15286             var li = document.createElement("li");
15287             li.className = "x-menu-list-item";
15288             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15289             item.render(li, this);
15290             this.delayAutoWidth();
15291         }
15292         return item;
15293     },
15294
15295     /**
15296      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15297      * @param {Roo.menu.Item} item The menu item to remove
15298      */
15299     remove : function(item){
15300         this.items.removeKey(item.id);
15301         item.destroy();
15302     },
15303
15304     /**
15305      * Removes and destroys all items in the menu
15306      */
15307     removeAll : function(){
15308         var f;
15309         while(f = this.items.first()){
15310             this.remove(f);
15311         }
15312     }
15313 });
15314
15315 // MenuNav is a private utility class used internally by the Menu
15316 Roo.menu.MenuNav = function(menu){
15317     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15318     this.scope = this.menu = menu;
15319 };
15320
15321 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15322     doRelay : function(e, h){
15323         var k = e.getKey();
15324         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15325             this.menu.tryActivate(0, 1);
15326             return false;
15327         }
15328         return h.call(this.scope || this, e, this.menu);
15329     },
15330
15331     up : function(e, m){
15332         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15333             m.tryActivate(m.items.length-1, -1);
15334         }
15335     },
15336
15337     down : function(e, m){
15338         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15339             m.tryActivate(0, 1);
15340         }
15341     },
15342
15343     right : function(e, m){
15344         if(m.activeItem){
15345             m.activeItem.expandMenu(true);
15346         }
15347     },
15348
15349     left : function(e, m){
15350         m.hide();
15351         if(m.parentMenu && m.parentMenu.activeItem){
15352             m.parentMenu.activeItem.activate();
15353         }
15354     },
15355
15356     enter : function(e, m){
15357         if(m.activeItem){
15358             e.stopPropagation();
15359             m.activeItem.onClick(e);
15360             m.fireEvent("click", this, m.activeItem);
15361             return true;
15362         }
15363     }
15364 });/*
15365  * Based on:
15366  * Ext JS Library 1.1.1
15367  * Copyright(c) 2006-2007, Ext JS, LLC.
15368  *
15369  * Originally Released Under LGPL - original licence link has changed is not relivant.
15370  *
15371  * Fork - LGPL
15372  * <script type="text/javascript">
15373  */
15374  
15375 /**
15376  * @class Roo.menu.MenuMgr
15377  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15378  * @singleton
15379  */
15380 Roo.menu.MenuMgr = function(){
15381    var menus, active, groups = {}, attached = false, lastShow = new Date();
15382
15383    // private - called when first menu is created
15384    function init(){
15385        menus = {};
15386        active = new Roo.util.MixedCollection();
15387        Roo.get(document).addKeyListener(27, function(){
15388            if(active.length > 0){
15389                hideAll();
15390            }
15391        });
15392    }
15393
15394    // private
15395    function hideAll(){
15396        if(active && active.length > 0){
15397            var c = active.clone();
15398            c.each(function(m){
15399                m.hide();
15400            });
15401        }
15402    }
15403
15404    // private
15405    function onHide(m){
15406        active.remove(m);
15407        if(active.length < 1){
15408            Roo.get(document).un("mousedown", onMouseDown);
15409            attached = false;
15410        }
15411    }
15412
15413    // private
15414    function onShow(m){
15415        var last = active.last();
15416        lastShow = new Date();
15417        active.add(m);
15418        if(!attached){
15419            Roo.get(document).on("mousedown", onMouseDown);
15420            attached = true;
15421        }
15422        if(m.parentMenu){
15423           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15424           m.parentMenu.activeChild = m;
15425        }else if(last && last.isVisible()){
15426           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15427        }
15428    }
15429
15430    // private
15431    function onBeforeHide(m){
15432        if(m.activeChild){
15433            m.activeChild.hide();
15434        }
15435        if(m.autoHideTimer){
15436            clearTimeout(m.autoHideTimer);
15437            delete m.autoHideTimer;
15438        }
15439    }
15440
15441    // private
15442    function onBeforeShow(m){
15443        var pm = m.parentMenu;
15444        if(!pm && !m.allowOtherMenus){
15445            hideAll();
15446        }else if(pm && pm.activeChild && active != m){
15447            pm.activeChild.hide();
15448        }
15449    }
15450
15451    // private
15452    function onMouseDown(e){
15453        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15454            hideAll();
15455        }
15456    }
15457
15458    // private
15459    function onBeforeCheck(mi, state){
15460        if(state){
15461            var g = groups[mi.group];
15462            for(var i = 0, l = g.length; i < l; i++){
15463                if(g[i] != mi){
15464                    g[i].setChecked(false);
15465                }
15466            }
15467        }
15468    }
15469
15470    return {
15471
15472        /**
15473         * Hides all menus that are currently visible
15474         */
15475        hideAll : function(){
15476             hideAll();  
15477        },
15478
15479        // private
15480        register : function(menu){
15481            if(!menus){
15482                init();
15483            }
15484            menus[menu.id] = menu;
15485            menu.on("beforehide", onBeforeHide);
15486            menu.on("hide", onHide);
15487            menu.on("beforeshow", onBeforeShow);
15488            menu.on("show", onShow);
15489            var g = menu.group;
15490            if(g && menu.events["checkchange"]){
15491                if(!groups[g]){
15492                    groups[g] = [];
15493                }
15494                groups[g].push(menu);
15495                menu.on("checkchange", onCheck);
15496            }
15497        },
15498
15499         /**
15500          * Returns a {@link Roo.menu.Menu} object
15501          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15502          * be used to generate and return a new Menu instance.
15503          */
15504        get : function(menu){
15505            if(typeof menu == "string"){ // menu id
15506                return menus[menu];
15507            }else if(menu.events){  // menu instance
15508                return menu;
15509            }else if(typeof menu.length == 'number'){ // array of menu items?
15510                return new Roo.menu.Menu({items:menu});
15511            }else{ // otherwise, must be a config
15512                return new Roo.menu.Menu(menu);
15513            }
15514        },
15515
15516        // private
15517        unregister : function(menu){
15518            delete menus[menu.id];
15519            menu.un("beforehide", onBeforeHide);
15520            menu.un("hide", onHide);
15521            menu.un("beforeshow", onBeforeShow);
15522            menu.un("show", onShow);
15523            var g = menu.group;
15524            if(g && menu.events["checkchange"]){
15525                groups[g].remove(menu);
15526                menu.un("checkchange", onCheck);
15527            }
15528        },
15529
15530        // private
15531        registerCheckable : function(menuItem){
15532            var g = menuItem.group;
15533            if(g){
15534                if(!groups[g]){
15535                    groups[g] = [];
15536                }
15537                groups[g].push(menuItem);
15538                menuItem.on("beforecheckchange", onBeforeCheck);
15539            }
15540        },
15541
15542        // private
15543        unregisterCheckable : function(menuItem){
15544            var g = menuItem.group;
15545            if(g){
15546                groups[g].remove(menuItem);
15547                menuItem.un("beforecheckchange", onBeforeCheck);
15548            }
15549        }
15550    };
15551 }();/*
15552  * Based on:
15553  * Ext JS Library 1.1.1
15554  * Copyright(c) 2006-2007, Ext JS, LLC.
15555  *
15556  * Originally Released Under LGPL - original licence link has changed is not relivant.
15557  *
15558  * Fork - LGPL
15559  * <script type="text/javascript">
15560  */
15561  
15562
15563 /**
15564  * @class Roo.menu.BaseItem
15565  * @extends Roo.Component
15566  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15567  * management and base configuration options shared by all menu components.
15568  * @constructor
15569  * Creates a new BaseItem
15570  * @param {Object} config Configuration options
15571  */
15572 Roo.menu.BaseItem = function(config){
15573     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15574
15575     this.addEvents({
15576         /**
15577          * @event click
15578          * Fires when this item is clicked
15579          * @param {Roo.menu.BaseItem} this
15580          * @param {Roo.EventObject} e
15581          */
15582         click: true,
15583         /**
15584          * @event activate
15585          * Fires when this item is activated
15586          * @param {Roo.menu.BaseItem} this
15587          */
15588         activate : true,
15589         /**
15590          * @event deactivate
15591          * Fires when this item is deactivated
15592          * @param {Roo.menu.BaseItem} this
15593          */
15594         deactivate : true
15595     });
15596
15597     if(this.handler){
15598         this.on("click", this.handler, this.scope, true);
15599     }
15600 };
15601
15602 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15603     /**
15604      * @cfg {Function} handler
15605      * A function that will handle the click event of this menu item (defaults to undefined)
15606      */
15607     /**
15608      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15609      */
15610     canActivate : false,
15611     
15612      /**
15613      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15614      */
15615     hidden: false,
15616     
15617     /**
15618      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15619      */
15620     activeClass : "x-menu-item-active",
15621     /**
15622      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15623      */
15624     hideOnClick : true,
15625     /**
15626      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15627      */
15628     hideDelay : 100,
15629
15630     // private
15631     ctype: "Roo.menu.BaseItem",
15632
15633     // private
15634     actionMode : "container",
15635
15636     // private
15637     render : function(container, parentMenu){
15638         this.parentMenu = parentMenu;
15639         Roo.menu.BaseItem.superclass.render.call(this, container);
15640         this.container.menuItemId = this.id;
15641     },
15642
15643     // private
15644     onRender : function(container, position){
15645         this.el = Roo.get(this.el);
15646         container.dom.appendChild(this.el.dom);
15647     },
15648
15649     // private
15650     onClick : function(e){
15651         if(!this.disabled && this.fireEvent("click", this, e) !== false
15652                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15653             this.handleClick(e);
15654         }else{
15655             e.stopEvent();
15656         }
15657     },
15658
15659     // private
15660     activate : function(){
15661         if(this.disabled){
15662             return false;
15663         }
15664         var li = this.container;
15665         li.addClass(this.activeClass);
15666         this.region = li.getRegion().adjust(2, 2, -2, -2);
15667         this.fireEvent("activate", this);
15668         return true;
15669     },
15670
15671     // private
15672     deactivate : function(){
15673         this.container.removeClass(this.activeClass);
15674         this.fireEvent("deactivate", this);
15675     },
15676
15677     // private
15678     shouldDeactivate : function(e){
15679         return !this.region || !this.region.contains(e.getPoint());
15680     },
15681
15682     // private
15683     handleClick : function(e){
15684         if(this.hideOnClick){
15685             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15686         }
15687     },
15688
15689     // private
15690     expandMenu : function(autoActivate){
15691         // do nothing
15692     },
15693
15694     // private
15695     hideMenu : function(){
15696         // do nothing
15697     }
15698 });/*
15699  * Based on:
15700  * Ext JS Library 1.1.1
15701  * Copyright(c) 2006-2007, Ext JS, LLC.
15702  *
15703  * Originally Released Under LGPL - original licence link has changed is not relivant.
15704  *
15705  * Fork - LGPL
15706  * <script type="text/javascript">
15707  */
15708  
15709 /**
15710  * @class Roo.menu.Adapter
15711  * @extends Roo.menu.BaseItem
15712  * 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.
15713  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15714  * @constructor
15715  * Creates a new Adapter
15716  * @param {Object} config Configuration options
15717  */
15718 Roo.menu.Adapter = function(component, config){
15719     Roo.menu.Adapter.superclass.constructor.call(this, config);
15720     this.component = component;
15721 };
15722 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15723     // private
15724     canActivate : true,
15725
15726     // private
15727     onRender : function(container, position){
15728         this.component.render(container);
15729         this.el = this.component.getEl();
15730     },
15731
15732     // private
15733     activate : function(){
15734         if(this.disabled){
15735             return false;
15736         }
15737         this.component.focus();
15738         this.fireEvent("activate", this);
15739         return true;
15740     },
15741
15742     // private
15743     deactivate : function(){
15744         this.fireEvent("deactivate", this);
15745     },
15746
15747     // private
15748     disable : function(){
15749         this.component.disable();
15750         Roo.menu.Adapter.superclass.disable.call(this);
15751     },
15752
15753     // private
15754     enable : function(){
15755         this.component.enable();
15756         Roo.menu.Adapter.superclass.enable.call(this);
15757     }
15758 });/*
15759  * Based on:
15760  * Ext JS Library 1.1.1
15761  * Copyright(c) 2006-2007, Ext JS, LLC.
15762  *
15763  * Originally Released Under LGPL - original licence link has changed is not relivant.
15764  *
15765  * Fork - LGPL
15766  * <script type="text/javascript">
15767  */
15768
15769 /**
15770  * @class Roo.menu.TextItem
15771  * @extends Roo.menu.BaseItem
15772  * Adds a static text string to a menu, usually used as either a heading or group separator.
15773  * Note: old style constructor with text is still supported.
15774  * 
15775  * @constructor
15776  * Creates a new TextItem
15777  * @param {Object} cfg Configuration
15778  */
15779 Roo.menu.TextItem = function(cfg){
15780     if (typeof(cfg) == 'string') {
15781         this.text = cfg;
15782     } else {
15783         Roo.apply(this,cfg);
15784     }
15785     
15786     Roo.menu.TextItem.superclass.constructor.call(this);
15787 };
15788
15789 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15790     /**
15791      * @cfg {Boolean} text Text to show on item.
15792      */
15793     text : '',
15794     
15795     /**
15796      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15797      */
15798     hideOnClick : false,
15799     /**
15800      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15801      */
15802     itemCls : "x-menu-text",
15803
15804     // private
15805     onRender : function(){
15806         var s = document.createElement("span");
15807         s.className = this.itemCls;
15808         s.innerHTML = this.text;
15809         this.el = s;
15810         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15811     }
15812 });/*
15813  * Based on:
15814  * Ext JS Library 1.1.1
15815  * Copyright(c) 2006-2007, Ext JS, LLC.
15816  *
15817  * Originally Released Under LGPL - original licence link has changed is not relivant.
15818  *
15819  * Fork - LGPL
15820  * <script type="text/javascript">
15821  */
15822
15823 /**
15824  * @class Roo.menu.Separator
15825  * @extends Roo.menu.BaseItem
15826  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15827  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15828  * @constructor
15829  * @param {Object} config Configuration options
15830  */
15831 Roo.menu.Separator = function(config){
15832     Roo.menu.Separator.superclass.constructor.call(this, config);
15833 };
15834
15835 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15836     /**
15837      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15838      */
15839     itemCls : "x-menu-sep",
15840     /**
15841      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15842      */
15843     hideOnClick : false,
15844
15845     // private
15846     onRender : function(li){
15847         var s = document.createElement("span");
15848         s.className = this.itemCls;
15849         s.innerHTML = "&#160;";
15850         this.el = s;
15851         li.addClass("x-menu-sep-li");
15852         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15853     }
15854 });/*
15855  * Based on:
15856  * Ext JS Library 1.1.1
15857  * Copyright(c) 2006-2007, Ext JS, LLC.
15858  *
15859  * Originally Released Under LGPL - original licence link has changed is not relivant.
15860  *
15861  * Fork - LGPL
15862  * <script type="text/javascript">
15863  */
15864 /**
15865  * @class Roo.menu.Item
15866  * @extends Roo.menu.BaseItem
15867  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15868  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15869  * activation and click handling.
15870  * @constructor
15871  * Creates a new Item
15872  * @param {Object} config Configuration options
15873  */
15874 Roo.menu.Item = function(config){
15875     Roo.menu.Item.superclass.constructor.call(this, config);
15876     if(this.menu){
15877         this.menu = Roo.menu.MenuMgr.get(this.menu);
15878     }
15879 };
15880 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15881     
15882     /**
15883      * @cfg {String} text
15884      * The text to show on the menu item.
15885      */
15886     text: '',
15887      /**
15888      * @cfg {String} HTML to render in menu
15889      * The text to show on the menu item (HTML version).
15890      */
15891     html: '',
15892     /**
15893      * @cfg {String} icon
15894      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15895      */
15896     icon: undefined,
15897     /**
15898      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15899      */
15900     itemCls : "x-menu-item",
15901     /**
15902      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15903      */
15904     canActivate : true,
15905     /**
15906      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15907      */
15908     showDelay: 200,
15909     // doc'd in BaseItem
15910     hideDelay: 200,
15911
15912     // private
15913     ctype: "Roo.menu.Item",
15914     
15915     // private
15916     onRender : function(container, position){
15917         var el = document.createElement("a");
15918         el.hideFocus = true;
15919         el.unselectable = "on";
15920         el.href = this.href || "#";
15921         if(this.hrefTarget){
15922             el.target = this.hrefTarget;
15923         }
15924         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15925         
15926         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15927         
15928         el.innerHTML = String.format(
15929                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15930                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15931         this.el = el;
15932         Roo.menu.Item.superclass.onRender.call(this, container, position);
15933     },
15934
15935     /**
15936      * Sets the text to display in this menu item
15937      * @param {String} text The text to display
15938      * @param {Boolean} isHTML true to indicate text is pure html.
15939      */
15940     setText : function(text, isHTML){
15941         if (isHTML) {
15942             this.html = text;
15943         } else {
15944             this.text = text;
15945             this.html = '';
15946         }
15947         if(this.rendered){
15948             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15949      
15950             this.el.update(String.format(
15951                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15952                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15953             this.parentMenu.autoWidth();
15954         }
15955     },
15956
15957     // private
15958     handleClick : function(e){
15959         if(!this.href){ // if no link defined, stop the event automatically
15960             e.stopEvent();
15961         }
15962         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15963     },
15964
15965     // private
15966     activate : function(autoExpand){
15967         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15968             this.focus();
15969             if(autoExpand){
15970                 this.expandMenu();
15971             }
15972         }
15973         return true;
15974     },
15975
15976     // private
15977     shouldDeactivate : function(e){
15978         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15979             if(this.menu && this.menu.isVisible()){
15980                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15981             }
15982             return true;
15983         }
15984         return false;
15985     },
15986
15987     // private
15988     deactivate : function(){
15989         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15990         this.hideMenu();
15991     },
15992
15993     // private
15994     expandMenu : function(autoActivate){
15995         if(!this.disabled && this.menu){
15996             clearTimeout(this.hideTimer);
15997             delete this.hideTimer;
15998             if(!this.menu.isVisible() && !this.showTimer){
15999                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16000             }else if (this.menu.isVisible() && autoActivate){
16001                 this.menu.tryActivate(0, 1);
16002             }
16003         }
16004     },
16005
16006     // private
16007     deferExpand : function(autoActivate){
16008         delete this.showTimer;
16009         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16010         if(autoActivate){
16011             this.menu.tryActivate(0, 1);
16012         }
16013     },
16014
16015     // private
16016     hideMenu : function(){
16017         clearTimeout(this.showTimer);
16018         delete this.showTimer;
16019         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16020             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16021         }
16022     },
16023
16024     // private
16025     deferHide : function(){
16026         delete this.hideTimer;
16027         this.menu.hide();
16028     }
16029 });/*
16030  * Based on:
16031  * Ext JS Library 1.1.1
16032  * Copyright(c) 2006-2007, Ext JS, LLC.
16033  *
16034  * Originally Released Under LGPL - original licence link has changed is not relivant.
16035  *
16036  * Fork - LGPL
16037  * <script type="text/javascript">
16038  */
16039  
16040 /**
16041  * @class Roo.menu.CheckItem
16042  * @extends Roo.menu.Item
16043  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16044  * @constructor
16045  * Creates a new CheckItem
16046  * @param {Object} config Configuration options
16047  */
16048 Roo.menu.CheckItem = function(config){
16049     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16050     this.addEvents({
16051         /**
16052          * @event beforecheckchange
16053          * Fires before the checked value is set, providing an opportunity to cancel if needed
16054          * @param {Roo.menu.CheckItem} this
16055          * @param {Boolean} checked The new checked value that will be set
16056          */
16057         "beforecheckchange" : true,
16058         /**
16059          * @event checkchange
16060          * Fires after the checked value has been set
16061          * @param {Roo.menu.CheckItem} this
16062          * @param {Boolean} checked The checked value that was set
16063          */
16064         "checkchange" : true
16065     });
16066     if(this.checkHandler){
16067         this.on('checkchange', this.checkHandler, this.scope);
16068     }
16069 };
16070 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16071     /**
16072      * @cfg {String} group
16073      * All check items with the same group name will automatically be grouped into a single-select
16074      * radio button group (defaults to '')
16075      */
16076     /**
16077      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16078      */
16079     itemCls : "x-menu-item x-menu-check-item",
16080     /**
16081      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16082      */
16083     groupClass : "x-menu-group-item",
16084
16085     /**
16086      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16087      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16088      * initialized with checked = true will be rendered as checked.
16089      */
16090     checked: false,
16091
16092     // private
16093     ctype: "Roo.menu.CheckItem",
16094
16095     // private
16096     onRender : function(c){
16097         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16098         if(this.group){
16099             this.el.addClass(this.groupClass);
16100         }
16101         Roo.menu.MenuMgr.registerCheckable(this);
16102         if(this.checked){
16103             this.checked = false;
16104             this.setChecked(true, true);
16105         }
16106     },
16107
16108     // private
16109     destroy : function(){
16110         if(this.rendered){
16111             Roo.menu.MenuMgr.unregisterCheckable(this);
16112         }
16113         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16114     },
16115
16116     /**
16117      * Set the checked state of this item
16118      * @param {Boolean} checked The new checked value
16119      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16120      */
16121     setChecked : function(state, suppressEvent){
16122         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16123             if(this.container){
16124                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16125             }
16126             this.checked = state;
16127             if(suppressEvent !== true){
16128                 this.fireEvent("checkchange", this, state);
16129             }
16130         }
16131     },
16132
16133     // private
16134     handleClick : function(e){
16135        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16136            this.setChecked(!this.checked);
16137        }
16138        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16139     }
16140 });/*
16141  * Based on:
16142  * Ext JS Library 1.1.1
16143  * Copyright(c) 2006-2007, Ext JS, LLC.
16144  *
16145  * Originally Released Under LGPL - original licence link has changed is not relivant.
16146  *
16147  * Fork - LGPL
16148  * <script type="text/javascript">
16149  */
16150  
16151 /**
16152  * @class Roo.menu.DateItem
16153  * @extends Roo.menu.Adapter
16154  * A menu item that wraps the {@link Roo.DatPicker} component.
16155  * @constructor
16156  * Creates a new DateItem
16157  * @param {Object} config Configuration options
16158  */
16159 Roo.menu.DateItem = function(config){
16160     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16161     /** The Roo.DatePicker object @type Roo.DatePicker */
16162     this.picker = this.component;
16163     this.addEvents({select: true});
16164     
16165     this.picker.on("render", function(picker){
16166         picker.getEl().swallowEvent("click");
16167         picker.container.addClass("x-menu-date-item");
16168     });
16169
16170     this.picker.on("select", this.onSelect, this);
16171 };
16172
16173 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16174     // private
16175     onSelect : function(picker, date){
16176         this.fireEvent("select", this, date, picker);
16177         Roo.menu.DateItem.superclass.handleClick.call(this);
16178     }
16179 });/*
16180  * Based on:
16181  * Ext JS Library 1.1.1
16182  * Copyright(c) 2006-2007, Ext JS, LLC.
16183  *
16184  * Originally Released Under LGPL - original licence link has changed is not relivant.
16185  *
16186  * Fork - LGPL
16187  * <script type="text/javascript">
16188  */
16189  
16190 /**
16191  * @class Roo.menu.ColorItem
16192  * @extends Roo.menu.Adapter
16193  * A menu item that wraps the {@link Roo.ColorPalette} component.
16194  * @constructor
16195  * Creates a new ColorItem
16196  * @param {Object} config Configuration options
16197  */
16198 Roo.menu.ColorItem = function(config){
16199     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16200     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16201     this.palette = this.component;
16202     this.relayEvents(this.palette, ["select"]);
16203     if(this.selectHandler){
16204         this.on('select', this.selectHandler, this.scope);
16205     }
16206 };
16207 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16208  * Based on:
16209  * Ext JS Library 1.1.1
16210  * Copyright(c) 2006-2007, Ext JS, LLC.
16211  *
16212  * Originally Released Under LGPL - original licence link has changed is not relivant.
16213  *
16214  * Fork - LGPL
16215  * <script type="text/javascript">
16216  */
16217  
16218
16219 /**
16220  * @class Roo.menu.DateMenu
16221  * @extends Roo.menu.Menu
16222  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16223  * @constructor
16224  * Creates a new DateMenu
16225  * @param {Object} config Configuration options
16226  */
16227 Roo.menu.DateMenu = function(config){
16228     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16229     this.plain = true;
16230     var di = new Roo.menu.DateItem(config);
16231     this.add(di);
16232     /**
16233      * The {@link Roo.DatePicker} instance for this DateMenu
16234      * @type DatePicker
16235      */
16236     this.picker = di.picker;
16237     /**
16238      * @event select
16239      * @param {DatePicker} picker
16240      * @param {Date} date
16241      */
16242     this.relayEvents(di, ["select"]);
16243     this.on('beforeshow', function(){
16244         if(this.picker){
16245             this.picker.hideMonthPicker(false);
16246         }
16247     }, this);
16248 };
16249 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16250     cls:'x-date-menu'
16251 });/*
16252  * Based on:
16253  * Ext JS Library 1.1.1
16254  * Copyright(c) 2006-2007, Ext JS, LLC.
16255  *
16256  * Originally Released Under LGPL - original licence link has changed is not relivant.
16257  *
16258  * Fork - LGPL
16259  * <script type="text/javascript">
16260  */
16261  
16262
16263 /**
16264  * @class Roo.menu.ColorMenu
16265  * @extends Roo.menu.Menu
16266  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16267  * @constructor
16268  * Creates a new ColorMenu
16269  * @param {Object} config Configuration options
16270  */
16271 Roo.menu.ColorMenu = function(config){
16272     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16273     this.plain = true;
16274     var ci = new Roo.menu.ColorItem(config);
16275     this.add(ci);
16276     /**
16277      * The {@link Roo.ColorPalette} instance for this ColorMenu
16278      * @type ColorPalette
16279      */
16280     this.palette = ci.palette;
16281     /**
16282      * @event select
16283      * @param {ColorPalette} palette
16284      * @param {String} color
16285      */
16286     this.relayEvents(ci, ["select"]);
16287 };
16288 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16289  * Based on:
16290  * Ext JS Library 1.1.1
16291  * Copyright(c) 2006-2007, Ext JS, LLC.
16292  *
16293  * Originally Released Under LGPL - original licence link has changed is not relivant.
16294  *
16295  * Fork - LGPL
16296  * <script type="text/javascript">
16297  */
16298  
16299 /**
16300  * @class Roo.form.TextItem
16301  * @extends Roo.BoxComponent
16302  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16303  * @constructor
16304  * Creates a new TextItem
16305  * @param {Object} config Configuration options
16306  */
16307 Roo.form.TextItem = function(config){
16308     Roo.form.TextItem.superclass.constructor.call(this, config);
16309 };
16310
16311 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16312     
16313     /**
16314      * @cfg {String} tag the tag for this item (default div)
16315      */
16316     tag : 'div',
16317     /**
16318      * @cfg {String} html the content for this item
16319      */
16320     html : '',
16321     
16322     getAutoCreate : function()
16323     {
16324         var cfg = {
16325             id: this.id,
16326             tag: this.tag,
16327             html: this.html,
16328             cls: 'x-form-item'
16329         };
16330         
16331         return cfg;
16332         
16333     },
16334     
16335     onRender : function(ct, position)
16336     {
16337         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16338         
16339         if(!this.el){
16340             var cfg = this.getAutoCreate();
16341             if(!cfg.name){
16342                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16343             }
16344             if (!cfg.name.length) {
16345                 delete cfg.name;
16346             }
16347             this.el = ct.createChild(cfg, position);
16348         }
16349     }
16350     
16351 });/*
16352  * Based on:
16353  * Ext JS Library 1.1.1
16354  * Copyright(c) 2006-2007, Ext JS, LLC.
16355  *
16356  * Originally Released Under LGPL - original licence link has changed is not relivant.
16357  *
16358  * Fork - LGPL
16359  * <script type="text/javascript">
16360  */
16361  
16362 /**
16363  * @class Roo.form.Field
16364  * @extends Roo.BoxComponent
16365  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16366  * @constructor
16367  * Creates a new Field
16368  * @param {Object} config Configuration options
16369  */
16370 Roo.form.Field = function(config){
16371     Roo.form.Field.superclass.constructor.call(this, config);
16372 };
16373
16374 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16375     /**
16376      * @cfg {String} fieldLabel Label to use when rendering a form.
16377      */
16378        /**
16379      * @cfg {String} qtip Mouse over tip
16380      */
16381      
16382     /**
16383      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16384      */
16385     invalidClass : "x-form-invalid",
16386     /**
16387      * @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")
16388      */
16389     invalidText : "The value in this field is invalid",
16390     /**
16391      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16392      */
16393     focusClass : "x-form-focus",
16394     /**
16395      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16396       automatic validation (defaults to "keyup").
16397      */
16398     validationEvent : "keyup",
16399     /**
16400      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16401      */
16402     validateOnBlur : true,
16403     /**
16404      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16405      */
16406     validationDelay : 250,
16407     /**
16408      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16409      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16410      */
16411     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16412     /**
16413      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16414      */
16415     fieldClass : "x-form-field",
16416     /**
16417      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16418      *<pre>
16419 Value         Description
16420 -----------   ----------------------------------------------------------------------
16421 qtip          Display a quick tip when the user hovers over the field
16422 title         Display a default browser title attribute popup
16423 under         Add a block div beneath the field containing the error text
16424 side          Add an error icon to the right of the field with a popup on hover
16425 [element id]  Add the error text directly to the innerHTML of the specified element
16426 </pre>
16427      */
16428     msgTarget : 'qtip',
16429     /**
16430      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16431      */
16432     msgFx : 'normal',
16433
16434     /**
16435      * @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.
16436      */
16437     readOnly : false,
16438
16439     /**
16440      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16441      */
16442     disabled : false,
16443
16444     /**
16445      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16446      */
16447     inputType : undefined,
16448     
16449     /**
16450      * @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).
16451          */
16452         tabIndex : undefined,
16453         
16454     // private
16455     isFormField : true,
16456
16457     // private
16458     hasFocus : false,
16459     /**
16460      * @property {Roo.Element} fieldEl
16461      * Element Containing the rendered Field (with label etc.)
16462      */
16463     /**
16464      * @cfg {Mixed} value A value to initialize this field with.
16465      */
16466     value : undefined,
16467
16468     /**
16469      * @cfg {String} name The field's HTML name attribute.
16470      */
16471     /**
16472      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16473      */
16474     // private
16475     loadedValue : false,
16476      
16477      
16478         // private ??
16479         initComponent : function(){
16480         Roo.form.Field.superclass.initComponent.call(this);
16481         this.addEvents({
16482             /**
16483              * @event focus
16484              * Fires when this field receives input focus.
16485              * @param {Roo.form.Field} this
16486              */
16487             focus : true,
16488             /**
16489              * @event blur
16490              * Fires when this field loses input focus.
16491              * @param {Roo.form.Field} this
16492              */
16493             blur : true,
16494             /**
16495              * @event specialkey
16496              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16497              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16498              * @param {Roo.form.Field} this
16499              * @param {Roo.EventObject} e The event object
16500              */
16501             specialkey : true,
16502             /**
16503              * @event change
16504              * Fires just before the field blurs if the field value has changed.
16505              * @param {Roo.form.Field} this
16506              * @param {Mixed} newValue The new value
16507              * @param {Mixed} oldValue The original value
16508              */
16509             change : true,
16510             /**
16511              * @event invalid
16512              * Fires after the field has been marked as invalid.
16513              * @param {Roo.form.Field} this
16514              * @param {String} msg The validation message
16515              */
16516             invalid : true,
16517             /**
16518              * @event valid
16519              * Fires after the field has been validated with no errors.
16520              * @param {Roo.form.Field} this
16521              */
16522             valid : true,
16523              /**
16524              * @event keyup
16525              * Fires after the key up
16526              * @param {Roo.form.Field} this
16527              * @param {Roo.EventObject}  e The event Object
16528              */
16529             keyup : true
16530         });
16531     },
16532
16533     /**
16534      * Returns the name attribute of the field if available
16535      * @return {String} name The field name
16536      */
16537     getName: function(){
16538          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16539     },
16540
16541     // private
16542     onRender : function(ct, position){
16543         Roo.form.Field.superclass.onRender.call(this, ct, position);
16544         if(!this.el){
16545             var cfg = this.getAutoCreate();
16546             if(!cfg.name){
16547                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16548             }
16549             if (!cfg.name.length) {
16550                 delete cfg.name;
16551             }
16552             if(this.inputType){
16553                 cfg.type = this.inputType;
16554             }
16555             this.el = ct.createChild(cfg, position);
16556         }
16557         var type = this.el.dom.type;
16558         if(type){
16559             if(type == 'password'){
16560                 type = 'text';
16561             }
16562             this.el.addClass('x-form-'+type);
16563         }
16564         if(this.readOnly){
16565             this.el.dom.readOnly = true;
16566         }
16567         if(this.tabIndex !== undefined){
16568             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16569         }
16570
16571         this.el.addClass([this.fieldClass, this.cls]);
16572         this.initValue();
16573     },
16574
16575     /**
16576      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16577      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16578      * @return {Roo.form.Field} this
16579      */
16580     applyTo : function(target){
16581         this.allowDomMove = false;
16582         this.el = Roo.get(target);
16583         this.render(this.el.dom.parentNode);
16584         return this;
16585     },
16586
16587     // private
16588     initValue : function(){
16589         if(this.value !== undefined){
16590             this.setValue(this.value);
16591         }else if(this.el.dom.value.length > 0){
16592             this.setValue(this.el.dom.value);
16593         }
16594     },
16595
16596     /**
16597      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16598      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16599      */
16600     isDirty : function() {
16601         if(this.disabled) {
16602             return false;
16603         }
16604         return String(this.getValue()) !== String(this.originalValue);
16605     },
16606
16607     /**
16608      * stores the current value in loadedValue
16609      */
16610     resetHasChanged : function()
16611     {
16612         this.loadedValue = String(this.getValue());
16613     },
16614     /**
16615      * checks the current value against the 'loaded' value.
16616      * Note - will return false if 'resetHasChanged' has not been called first.
16617      */
16618     hasChanged : function()
16619     {
16620         if(this.disabled || this.readOnly) {
16621             return false;
16622         }
16623         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16624     },
16625     
16626     
16627     
16628     // private
16629     afterRender : function(){
16630         Roo.form.Field.superclass.afterRender.call(this);
16631         this.initEvents();
16632     },
16633
16634     // private
16635     fireKey : function(e){
16636         //Roo.log('field ' + e.getKey());
16637         if(e.isNavKeyPress()){
16638             this.fireEvent("specialkey", this, e);
16639         }
16640     },
16641
16642     /**
16643      * Resets the current field value to the originally loaded value and clears any validation messages
16644      */
16645     reset : function(){
16646         this.setValue(this.resetValue);
16647         this.originalValue = this.getValue();
16648         this.clearInvalid();
16649     },
16650
16651     // private
16652     initEvents : function(){
16653         // safari killled keypress - so keydown is now used..
16654         this.el.on("keydown" , this.fireKey,  this);
16655         this.el.on("focus", this.onFocus,  this);
16656         this.el.on("blur", this.onBlur,  this);
16657         this.el.relayEvent('keyup', this);
16658
16659         // reference to original value for reset
16660         this.originalValue = this.getValue();
16661         this.resetValue =  this.getValue();
16662     },
16663
16664     // private
16665     onFocus : function(){
16666         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16667             this.el.addClass(this.focusClass);
16668         }
16669         if(!this.hasFocus){
16670             this.hasFocus = true;
16671             this.startValue = this.getValue();
16672             this.fireEvent("focus", this);
16673         }
16674     },
16675
16676     beforeBlur : Roo.emptyFn,
16677
16678     // private
16679     onBlur : function(){
16680         this.beforeBlur();
16681         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16682             this.el.removeClass(this.focusClass);
16683         }
16684         this.hasFocus = false;
16685         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16686             this.validate();
16687         }
16688         var v = this.getValue();
16689         if(String(v) !== String(this.startValue)){
16690             this.fireEvent('change', this, v, this.startValue);
16691         }
16692         this.fireEvent("blur", this);
16693     },
16694
16695     /**
16696      * Returns whether or not the field value is currently valid
16697      * @param {Boolean} preventMark True to disable marking the field invalid
16698      * @return {Boolean} True if the value is valid, else false
16699      */
16700     isValid : function(preventMark){
16701         if(this.disabled){
16702             return true;
16703         }
16704         var restore = this.preventMark;
16705         this.preventMark = preventMark === true;
16706         var v = this.validateValue(this.processValue(this.getRawValue()));
16707         this.preventMark = restore;
16708         return v;
16709     },
16710
16711     /**
16712      * Validates the field value
16713      * @return {Boolean} True if the value is valid, else false
16714      */
16715     validate : function(){
16716         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16717             this.clearInvalid();
16718             return true;
16719         }
16720         return false;
16721     },
16722
16723     processValue : function(value){
16724         return value;
16725     },
16726
16727     // private
16728     // Subclasses should provide the validation implementation by overriding this
16729     validateValue : function(value){
16730         return true;
16731     },
16732
16733     /**
16734      * Mark this field as invalid
16735      * @param {String} msg The validation message
16736      */
16737     markInvalid : function(msg){
16738         if(!this.rendered || this.preventMark){ // not rendered
16739             return;
16740         }
16741         
16742         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16743         
16744         obj.el.addClass(this.invalidClass);
16745         msg = msg || this.invalidText;
16746         switch(this.msgTarget){
16747             case 'qtip':
16748                 obj.el.dom.qtip = msg;
16749                 obj.el.dom.qclass = 'x-form-invalid-tip';
16750                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16751                     Roo.QuickTips.enable();
16752                 }
16753                 break;
16754             case 'title':
16755                 this.el.dom.title = msg;
16756                 break;
16757             case 'under':
16758                 if(!this.errorEl){
16759                     var elp = this.el.findParent('.x-form-element', 5, true);
16760                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16761                     this.errorEl.setWidth(elp.getWidth(true)-20);
16762                 }
16763                 this.errorEl.update(msg);
16764                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16765                 break;
16766             case 'side':
16767                 if(!this.errorIcon){
16768                     var elp = this.el.findParent('.x-form-element', 5, true);
16769                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16770                 }
16771                 this.alignErrorIcon();
16772                 this.errorIcon.dom.qtip = msg;
16773                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16774                 this.errorIcon.show();
16775                 this.on('resize', this.alignErrorIcon, this);
16776                 break;
16777             default:
16778                 var t = Roo.getDom(this.msgTarget);
16779                 t.innerHTML = msg;
16780                 t.style.display = this.msgDisplay;
16781                 break;
16782         }
16783         this.fireEvent('invalid', this, msg);
16784     },
16785
16786     // private
16787     alignErrorIcon : function(){
16788         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16789     },
16790
16791     /**
16792      * Clear any invalid styles/messages for this field
16793      */
16794     clearInvalid : function(){
16795         if(!this.rendered || this.preventMark){ // not rendered
16796             return;
16797         }
16798         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16799         
16800         obj.el.removeClass(this.invalidClass);
16801         switch(this.msgTarget){
16802             case 'qtip':
16803                 obj.el.dom.qtip = '';
16804                 break;
16805             case 'title':
16806                 this.el.dom.title = '';
16807                 break;
16808             case 'under':
16809                 if(this.errorEl){
16810                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16811                 }
16812                 break;
16813             case 'side':
16814                 if(this.errorIcon){
16815                     this.errorIcon.dom.qtip = '';
16816                     this.errorIcon.hide();
16817                     this.un('resize', this.alignErrorIcon, this);
16818                 }
16819                 break;
16820             default:
16821                 var t = Roo.getDom(this.msgTarget);
16822                 t.innerHTML = '';
16823                 t.style.display = 'none';
16824                 break;
16825         }
16826         this.fireEvent('valid', this);
16827     },
16828
16829     /**
16830      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16831      * @return {Mixed} value The field value
16832      */
16833     getRawValue : function(){
16834         var v = this.el.getValue();
16835         
16836         return v;
16837     },
16838
16839     /**
16840      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16841      * @return {Mixed} value The field value
16842      */
16843     getValue : function(){
16844         var v = this.el.getValue();
16845          
16846         return v;
16847     },
16848
16849     /**
16850      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16851      * @param {Mixed} value The value to set
16852      */
16853     setRawValue : function(v){
16854         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16855     },
16856
16857     /**
16858      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16859      * @param {Mixed} value The value to set
16860      */
16861     setValue : function(v){
16862         this.value = v;
16863         if(this.rendered){
16864             this.el.dom.value = (v === null || v === undefined ? '' : v);
16865              this.validate();
16866         }
16867     },
16868
16869     adjustSize : function(w, h){
16870         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16871         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16872         return s;
16873     },
16874
16875     adjustWidth : function(tag, w){
16876         tag = tag.toLowerCase();
16877         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16878             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16879                 if(tag == 'input'){
16880                     return w + 2;
16881                 }
16882                 if(tag == 'textarea'){
16883                     return w-2;
16884                 }
16885             }else if(Roo.isOpera){
16886                 if(tag == 'input'){
16887                     return w + 2;
16888                 }
16889                 if(tag == 'textarea'){
16890                     return w-2;
16891                 }
16892             }
16893         }
16894         return w;
16895     }
16896 });
16897
16898
16899 // anything other than normal should be considered experimental
16900 Roo.form.Field.msgFx = {
16901     normal : {
16902         show: function(msgEl, f){
16903             msgEl.setDisplayed('block');
16904         },
16905
16906         hide : function(msgEl, f){
16907             msgEl.setDisplayed(false).update('');
16908         }
16909     },
16910
16911     slide : {
16912         show: function(msgEl, f){
16913             msgEl.slideIn('t', {stopFx:true});
16914         },
16915
16916         hide : function(msgEl, f){
16917             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16918         }
16919     },
16920
16921     slideRight : {
16922         show: function(msgEl, f){
16923             msgEl.fixDisplay();
16924             msgEl.alignTo(f.el, 'tl-tr');
16925             msgEl.slideIn('l', {stopFx:true});
16926         },
16927
16928         hide : function(msgEl, f){
16929             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16930         }
16931     }
16932 };/*
16933  * Based on:
16934  * Ext JS Library 1.1.1
16935  * Copyright(c) 2006-2007, Ext JS, LLC.
16936  *
16937  * Originally Released Under LGPL - original licence link has changed is not relivant.
16938  *
16939  * Fork - LGPL
16940  * <script type="text/javascript">
16941  */
16942  
16943
16944 /**
16945  * @class Roo.form.TextField
16946  * @extends Roo.form.Field
16947  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16948  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16949  * @constructor
16950  * Creates a new TextField
16951  * @param {Object} config Configuration options
16952  */
16953 Roo.form.TextField = function(config){
16954     Roo.form.TextField.superclass.constructor.call(this, config);
16955     this.addEvents({
16956         /**
16957          * @event autosize
16958          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16959          * according to the default logic, but this event provides a hook for the developer to apply additional
16960          * logic at runtime to resize the field if needed.
16961              * @param {Roo.form.Field} this This text field
16962              * @param {Number} width The new field width
16963              */
16964         autosize : true
16965     });
16966 };
16967
16968 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16969     /**
16970      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16971      */
16972     grow : false,
16973     /**
16974      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16975      */
16976     growMin : 30,
16977     /**
16978      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16979      */
16980     growMax : 800,
16981     /**
16982      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16983      */
16984     vtype : null,
16985     /**
16986      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16987      */
16988     maskRe : null,
16989     /**
16990      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16991      */
16992     disableKeyFilter : false,
16993     /**
16994      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16995      */
16996     allowBlank : true,
16997     /**
16998      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16999      */
17000     minLength : 0,
17001     /**
17002      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17003      */
17004     maxLength : Number.MAX_VALUE,
17005     /**
17006      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17007      */
17008     minLengthText : "The minimum length for this field is {0}",
17009     /**
17010      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17011      */
17012     maxLengthText : "The maximum length for this field is {0}",
17013     /**
17014      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17015      */
17016     selectOnFocus : false,
17017     /**
17018      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17019      */    
17020     allowLeadingSpace : false,
17021     /**
17022      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17023      */
17024     blankText : "This field is required",
17025     /**
17026      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17027      * If available, this function will be called only after the basic validators all return true, and will be passed the
17028      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17029      */
17030     validator : null,
17031     /**
17032      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17033      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17034      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17035      */
17036     regex : null,
17037     /**
17038      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17039      */
17040     regexText : "",
17041     /**
17042      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17043      */
17044     emptyText : null,
17045    
17046
17047     // private
17048     initEvents : function()
17049     {
17050         if (this.emptyText) {
17051             this.el.attr('placeholder', this.emptyText);
17052         }
17053         
17054         Roo.form.TextField.superclass.initEvents.call(this);
17055         if(this.validationEvent == 'keyup'){
17056             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17057             this.el.on('keyup', this.filterValidation, this);
17058         }
17059         else if(this.validationEvent !== false){
17060             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17061         }
17062         
17063         if(this.selectOnFocus){
17064             this.on("focus", this.preFocus, this);
17065         }
17066         if (!this.allowLeadingSpace) {
17067             this.on('blur', this.cleanLeadingSpace, this);
17068         }
17069         
17070         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17071             this.el.on("keypress", this.filterKeys, this);
17072         }
17073         if(this.grow){
17074             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17075             this.el.on("click", this.autoSize,  this);
17076         }
17077         if(this.el.is('input[type=password]') && Roo.isSafari){
17078             this.el.on('keydown', this.SafariOnKeyDown, this);
17079         }
17080     },
17081
17082     processValue : function(value){
17083         if(this.stripCharsRe){
17084             var newValue = value.replace(this.stripCharsRe, '');
17085             if(newValue !== value){
17086                 this.setRawValue(newValue);
17087                 return newValue;
17088             }
17089         }
17090         return value;
17091     },
17092
17093     filterValidation : function(e){
17094         if(!e.isNavKeyPress()){
17095             this.validationTask.delay(this.validationDelay);
17096         }
17097     },
17098
17099     // private
17100     onKeyUp : function(e){
17101         if(!e.isNavKeyPress()){
17102             this.autoSize();
17103         }
17104     },
17105     // private - clean the leading white space
17106     cleanLeadingSpace : function(e)
17107     {
17108         if ( this.inputType == 'file') {
17109             return;
17110         }
17111         
17112         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17113     },
17114     /**
17115      * Resets the current field value to the originally-loaded value and clears any validation messages.
17116      *  
17117      */
17118     reset : function(){
17119         Roo.form.TextField.superclass.reset.call(this);
17120        
17121     }, 
17122     // private
17123     preFocus : function(){
17124         
17125         if(this.selectOnFocus){
17126             this.el.dom.select();
17127         }
17128     },
17129
17130     
17131     // private
17132     filterKeys : function(e){
17133         var k = e.getKey();
17134         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17135             return;
17136         }
17137         var c = e.getCharCode(), cc = String.fromCharCode(c);
17138         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17139             return;
17140         }
17141         if(!this.maskRe.test(cc)){
17142             e.stopEvent();
17143         }
17144     },
17145
17146     setValue : function(v){
17147         
17148         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17149         
17150         this.autoSize();
17151     },
17152
17153     /**
17154      * Validates a value according to the field's validation rules and marks the field as invalid
17155      * if the validation fails
17156      * @param {Mixed} value The value to validate
17157      * @return {Boolean} True if the value is valid, else false
17158      */
17159     validateValue : function(value){
17160         if(value.length < 1)  { // if it's blank
17161              if(this.allowBlank){
17162                 this.clearInvalid();
17163                 return true;
17164              }else{
17165                 this.markInvalid(this.blankText);
17166                 return false;
17167              }
17168         }
17169         if(value.length < this.minLength){
17170             this.markInvalid(String.format(this.minLengthText, this.minLength));
17171             return false;
17172         }
17173         if(value.length > this.maxLength){
17174             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17175             return false;
17176         }
17177         if(this.vtype){
17178             var vt = Roo.form.VTypes;
17179             if(!vt[this.vtype](value, this)){
17180                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17181                 return false;
17182             }
17183         }
17184         if(typeof this.validator == "function"){
17185             var msg = this.validator(value);
17186             if(msg !== true){
17187                 this.markInvalid(msg);
17188                 return false;
17189             }
17190         }
17191         if(this.regex && !this.regex.test(value)){
17192             this.markInvalid(this.regexText);
17193             return false;
17194         }
17195         return true;
17196     },
17197
17198     /**
17199      * Selects text in this field
17200      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17201      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17202      */
17203     selectText : function(start, end){
17204         var v = this.getRawValue();
17205         if(v.length > 0){
17206             start = start === undefined ? 0 : start;
17207             end = end === undefined ? v.length : end;
17208             var d = this.el.dom;
17209             if(d.setSelectionRange){
17210                 d.setSelectionRange(start, end);
17211             }else if(d.createTextRange){
17212                 var range = d.createTextRange();
17213                 range.moveStart("character", start);
17214                 range.moveEnd("character", v.length-end);
17215                 range.select();
17216             }
17217         }
17218     },
17219
17220     /**
17221      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17222      * This only takes effect if grow = true, and fires the autosize event.
17223      */
17224     autoSize : function(){
17225         if(!this.grow || !this.rendered){
17226             return;
17227         }
17228         if(!this.metrics){
17229             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17230         }
17231         var el = this.el;
17232         var v = el.dom.value;
17233         var d = document.createElement('div');
17234         d.appendChild(document.createTextNode(v));
17235         v = d.innerHTML;
17236         d = null;
17237         v += "&#160;";
17238         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17239         this.el.setWidth(w);
17240         this.fireEvent("autosize", this, w);
17241     },
17242     
17243     // private
17244     SafariOnKeyDown : function(event)
17245     {
17246         // this is a workaround for a password hang bug on chrome/ webkit.
17247         
17248         var isSelectAll = false;
17249         
17250         if(this.el.dom.selectionEnd > 0){
17251             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17252         }
17253         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17254             event.preventDefault();
17255             this.setValue('');
17256             return;
17257         }
17258         
17259         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17260             
17261             event.preventDefault();
17262             // this is very hacky as keydown always get's upper case.
17263             
17264             var cc = String.fromCharCode(event.getCharCode());
17265             
17266             
17267             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17268             
17269         }
17270         
17271         
17272     }
17273 });/*
17274  * Based on:
17275  * Ext JS Library 1.1.1
17276  * Copyright(c) 2006-2007, Ext JS, LLC.
17277  *
17278  * Originally Released Under LGPL - original licence link has changed is not relivant.
17279  *
17280  * Fork - LGPL
17281  * <script type="text/javascript">
17282  */
17283  
17284 /**
17285  * @class Roo.form.Hidden
17286  * @extends Roo.form.TextField
17287  * Simple Hidden element used on forms 
17288  * 
17289  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17290  * 
17291  * @constructor
17292  * Creates a new Hidden form element.
17293  * @param {Object} config Configuration options
17294  */
17295
17296
17297
17298 // easy hidden field...
17299 Roo.form.Hidden = function(config){
17300     Roo.form.Hidden.superclass.constructor.call(this, config);
17301 };
17302   
17303 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17304     fieldLabel:      '',
17305     inputType:      'hidden',
17306     width:          50,
17307     allowBlank:     true,
17308     labelSeparator: '',
17309     hidden:         true,
17310     itemCls :       'x-form-item-display-none'
17311
17312
17313 });
17314
17315
17316 /*
17317  * Based on:
17318  * Ext JS Library 1.1.1
17319  * Copyright(c) 2006-2007, Ext JS, LLC.
17320  *
17321  * Originally Released Under LGPL - original licence link has changed is not relivant.
17322  *
17323  * Fork - LGPL
17324  * <script type="text/javascript">
17325  */
17326  
17327 /**
17328  * @class Roo.form.TriggerField
17329  * @extends Roo.form.TextField
17330  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17331  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17332  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17333  * for which you can provide a custom implementation.  For example:
17334  * <pre><code>
17335 var trigger = new Roo.form.TriggerField();
17336 trigger.onTriggerClick = myTriggerFn;
17337 trigger.applyTo('my-field');
17338 </code></pre>
17339  *
17340  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17341  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17342  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17343  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17344  * @constructor
17345  * Create a new TriggerField.
17346  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17347  * to the base TextField)
17348  */
17349 Roo.form.TriggerField = function(config){
17350     this.mimicing = false;
17351     Roo.form.TriggerField.superclass.constructor.call(this, config);
17352 };
17353
17354 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17355     /**
17356      * @cfg {String} triggerClass A CSS class to apply to the trigger
17357      */
17358     /**
17359      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17360      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17361      */
17362     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17363     /**
17364      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17365      */
17366     hideTrigger:false,
17367
17368     /** @cfg {Boolean} grow @hide */
17369     /** @cfg {Number} growMin @hide */
17370     /** @cfg {Number} growMax @hide */
17371
17372     /**
17373      * @hide 
17374      * @method
17375      */
17376     autoSize: Roo.emptyFn,
17377     // private
17378     monitorTab : true,
17379     // private
17380     deferHeight : true,
17381
17382     
17383     actionMode : 'wrap',
17384     // private
17385     onResize : function(w, h){
17386         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17387         if(typeof w == 'number'){
17388             var x = w - this.trigger.getWidth();
17389             this.el.setWidth(this.adjustWidth('input', x));
17390             this.trigger.setStyle('left', x+'px');
17391         }
17392     },
17393
17394     // private
17395     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17396
17397     // private
17398     getResizeEl : function(){
17399         return this.wrap;
17400     },
17401
17402     // private
17403     getPositionEl : function(){
17404         return this.wrap;
17405     },
17406
17407     // private
17408     alignErrorIcon : function(){
17409         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17410     },
17411
17412     // private
17413     onRender : function(ct, position){
17414         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17415         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17416         this.trigger = this.wrap.createChild(this.triggerConfig ||
17417                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17418         if(this.hideTrigger){
17419             this.trigger.setDisplayed(false);
17420         }
17421         this.initTrigger();
17422         if(!this.width){
17423             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17424         }
17425     },
17426
17427     // private
17428     initTrigger : function(){
17429         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17430         this.trigger.addClassOnOver('x-form-trigger-over');
17431         this.trigger.addClassOnClick('x-form-trigger-click');
17432     },
17433
17434     // private
17435     onDestroy : function(){
17436         if(this.trigger){
17437             this.trigger.removeAllListeners();
17438             this.trigger.remove();
17439         }
17440         if(this.wrap){
17441             this.wrap.remove();
17442         }
17443         Roo.form.TriggerField.superclass.onDestroy.call(this);
17444     },
17445
17446     // private
17447     onFocus : function(){
17448         Roo.form.TriggerField.superclass.onFocus.call(this);
17449         if(!this.mimicing){
17450             this.wrap.addClass('x-trigger-wrap-focus');
17451             this.mimicing = true;
17452             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17453             if(this.monitorTab){
17454                 this.el.on("keydown", this.checkTab, this);
17455             }
17456         }
17457     },
17458
17459     // private
17460     checkTab : function(e){
17461         if(e.getKey() == e.TAB){
17462             this.triggerBlur();
17463         }
17464     },
17465
17466     // private
17467     onBlur : function(){
17468         // do nothing
17469     },
17470
17471     // private
17472     mimicBlur : function(e, t){
17473         if(!this.wrap.contains(t) && this.validateBlur()){
17474             this.triggerBlur();
17475         }
17476     },
17477
17478     // private
17479     triggerBlur : function(){
17480         this.mimicing = false;
17481         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17482         if(this.monitorTab){
17483             this.el.un("keydown", this.checkTab, this);
17484         }
17485         this.wrap.removeClass('x-trigger-wrap-focus');
17486         Roo.form.TriggerField.superclass.onBlur.call(this);
17487     },
17488
17489     // private
17490     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17491     validateBlur : function(e, t){
17492         return true;
17493     },
17494
17495     // private
17496     onDisable : function(){
17497         Roo.form.TriggerField.superclass.onDisable.call(this);
17498         if(this.wrap){
17499             this.wrap.addClass('x-item-disabled');
17500         }
17501     },
17502
17503     // private
17504     onEnable : function(){
17505         Roo.form.TriggerField.superclass.onEnable.call(this);
17506         if(this.wrap){
17507             this.wrap.removeClass('x-item-disabled');
17508         }
17509     },
17510
17511     // private
17512     onShow : function(){
17513         var ae = this.getActionEl();
17514         
17515         if(ae){
17516             ae.dom.style.display = '';
17517             ae.dom.style.visibility = 'visible';
17518         }
17519     },
17520
17521     // private
17522     
17523     onHide : function(){
17524         var ae = this.getActionEl();
17525         ae.dom.style.display = 'none';
17526     },
17527
17528     /**
17529      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17530      * by an implementing function.
17531      * @method
17532      * @param {EventObject} e
17533      */
17534     onTriggerClick : Roo.emptyFn
17535 });
17536
17537 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17538 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17539 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17540 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17541     initComponent : function(){
17542         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17543
17544         this.triggerConfig = {
17545             tag:'span', cls:'x-form-twin-triggers', cn:[
17546             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17547             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17548         ]};
17549     },
17550
17551     getTrigger : function(index){
17552         return this.triggers[index];
17553     },
17554
17555     initTrigger : function(){
17556         var ts = this.trigger.select('.x-form-trigger', true);
17557         this.wrap.setStyle('overflow', 'hidden');
17558         var triggerField = this;
17559         ts.each(function(t, all, index){
17560             t.hide = function(){
17561                 var w = triggerField.wrap.getWidth();
17562                 this.dom.style.display = 'none';
17563                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17564             };
17565             t.show = function(){
17566                 var w = triggerField.wrap.getWidth();
17567                 this.dom.style.display = '';
17568                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17569             };
17570             var triggerIndex = 'Trigger'+(index+1);
17571
17572             if(this['hide'+triggerIndex]){
17573                 t.dom.style.display = 'none';
17574             }
17575             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17576             t.addClassOnOver('x-form-trigger-over');
17577             t.addClassOnClick('x-form-trigger-click');
17578         }, this);
17579         this.triggers = ts.elements;
17580     },
17581
17582     onTrigger1Click : Roo.emptyFn,
17583     onTrigger2Click : Roo.emptyFn
17584 });/*
17585  * Based on:
17586  * Ext JS Library 1.1.1
17587  * Copyright(c) 2006-2007, Ext JS, LLC.
17588  *
17589  * Originally Released Under LGPL - original licence link has changed is not relivant.
17590  *
17591  * Fork - LGPL
17592  * <script type="text/javascript">
17593  */
17594  
17595 /**
17596  * @class Roo.form.TextArea
17597  * @extends Roo.form.TextField
17598  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17599  * support for auto-sizing.
17600  * @constructor
17601  * Creates a new TextArea
17602  * @param {Object} config Configuration options
17603  */
17604 Roo.form.TextArea = function(config){
17605     Roo.form.TextArea.superclass.constructor.call(this, config);
17606     // these are provided exchanges for backwards compat
17607     // minHeight/maxHeight were replaced by growMin/growMax to be
17608     // compatible with TextField growing config values
17609     if(this.minHeight !== undefined){
17610         this.growMin = this.minHeight;
17611     }
17612     if(this.maxHeight !== undefined){
17613         this.growMax = this.maxHeight;
17614     }
17615 };
17616
17617 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17618     /**
17619      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17620      */
17621     growMin : 60,
17622     /**
17623      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17624      */
17625     growMax: 1000,
17626     /**
17627      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17628      * in the field (equivalent to setting overflow: hidden, defaults to false)
17629      */
17630     preventScrollbars: false,
17631     /**
17632      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17633      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17634      */
17635
17636     // private
17637     onRender : function(ct, position){
17638         if(!this.el){
17639             this.defaultAutoCreate = {
17640                 tag: "textarea",
17641                 style:"width:300px;height:60px;",
17642                 autocomplete: "new-password"
17643             };
17644         }
17645         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17646         if(this.grow){
17647             this.textSizeEl = Roo.DomHelper.append(document.body, {
17648                 tag: "pre", cls: "x-form-grow-sizer"
17649             });
17650             if(this.preventScrollbars){
17651                 this.el.setStyle("overflow", "hidden");
17652             }
17653             this.el.setHeight(this.growMin);
17654         }
17655     },
17656
17657     onDestroy : function(){
17658         if(this.textSizeEl){
17659             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17660         }
17661         Roo.form.TextArea.superclass.onDestroy.call(this);
17662     },
17663
17664     // private
17665     onKeyUp : function(e){
17666         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17667             this.autoSize();
17668         }
17669     },
17670
17671     /**
17672      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17673      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17674      */
17675     autoSize : function(){
17676         if(!this.grow || !this.textSizeEl){
17677             return;
17678         }
17679         var el = this.el;
17680         var v = el.dom.value;
17681         var ts = this.textSizeEl;
17682
17683         ts.innerHTML = '';
17684         ts.appendChild(document.createTextNode(v));
17685         v = ts.innerHTML;
17686
17687         Roo.fly(ts).setWidth(this.el.getWidth());
17688         if(v.length < 1){
17689             v = "&#160;&#160;";
17690         }else{
17691             if(Roo.isIE){
17692                 v = v.replace(/\n/g, '<p>&#160;</p>');
17693             }
17694             v += "&#160;\n&#160;";
17695         }
17696         ts.innerHTML = v;
17697         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17698         if(h != this.lastHeight){
17699             this.lastHeight = h;
17700             this.el.setHeight(h);
17701             this.fireEvent("autosize", this, h);
17702         }
17703     }
17704 });/*
17705  * Based on:
17706  * Ext JS Library 1.1.1
17707  * Copyright(c) 2006-2007, Ext JS, LLC.
17708  *
17709  * Originally Released Under LGPL - original licence link has changed is not relivant.
17710  *
17711  * Fork - LGPL
17712  * <script type="text/javascript">
17713  */
17714  
17715
17716 /**
17717  * @class Roo.form.NumberField
17718  * @extends Roo.form.TextField
17719  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17720  * @constructor
17721  * Creates a new NumberField
17722  * @param {Object} config Configuration options
17723  */
17724 Roo.form.NumberField = function(config){
17725     Roo.form.NumberField.superclass.constructor.call(this, config);
17726 };
17727
17728 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17729     /**
17730      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17731      */
17732     fieldClass: "x-form-field x-form-num-field",
17733     /**
17734      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17735      */
17736     allowDecimals : true,
17737     /**
17738      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17739      */
17740     decimalSeparator : ".",
17741     /**
17742      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17743      */
17744     decimalPrecision : 2,
17745     /**
17746      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17747      */
17748     allowNegative : true,
17749     /**
17750      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17751      */
17752     minValue : Number.NEGATIVE_INFINITY,
17753     /**
17754      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17755      */
17756     maxValue : Number.MAX_VALUE,
17757     /**
17758      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17759      */
17760     minText : "The minimum value for this field is {0}",
17761     /**
17762      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17763      */
17764     maxText : "The maximum value for this field is {0}",
17765     /**
17766      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17767      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17768      */
17769     nanText : "{0} is not a valid number",
17770
17771     // private
17772     initEvents : function(){
17773         Roo.form.NumberField.superclass.initEvents.call(this);
17774         var allowed = "0123456789";
17775         if(this.allowDecimals){
17776             allowed += this.decimalSeparator;
17777         }
17778         if(this.allowNegative){
17779             allowed += "-";
17780         }
17781         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17782         var keyPress = function(e){
17783             var k = e.getKey();
17784             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17785                 return;
17786             }
17787             var c = e.getCharCode();
17788             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17789                 e.stopEvent();
17790             }
17791         };
17792         this.el.on("keypress", keyPress, this);
17793     },
17794
17795     // private
17796     validateValue : function(value){
17797         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17798             return false;
17799         }
17800         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17801              return true;
17802         }
17803         var num = this.parseValue(value);
17804         if(isNaN(num)){
17805             this.markInvalid(String.format(this.nanText, value));
17806             return false;
17807         }
17808         if(num < this.minValue){
17809             this.markInvalid(String.format(this.minText, this.minValue));
17810             return false;
17811         }
17812         if(num > this.maxValue){
17813             this.markInvalid(String.format(this.maxText, this.maxValue));
17814             return false;
17815         }
17816         return true;
17817     },
17818
17819     getValue : function(){
17820         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17821     },
17822
17823     // private
17824     parseValue : function(value){
17825         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17826         return isNaN(value) ? '' : value;
17827     },
17828
17829     // private
17830     fixPrecision : function(value){
17831         var nan = isNaN(value);
17832         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17833             return nan ? '' : value;
17834         }
17835         return parseFloat(value).toFixed(this.decimalPrecision);
17836     },
17837
17838     setValue : function(v){
17839         v = this.fixPrecision(v);
17840         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17841     },
17842
17843     // private
17844     decimalPrecisionFcn : function(v){
17845         return Math.floor(v);
17846     },
17847
17848     beforeBlur : function(){
17849         var v = this.parseValue(this.getRawValue());
17850         if(v){
17851             this.setValue(v);
17852         }
17853     }
17854 });/*
17855  * Based on:
17856  * Ext JS Library 1.1.1
17857  * Copyright(c) 2006-2007, Ext JS, LLC.
17858  *
17859  * Originally Released Under LGPL - original licence link has changed is not relivant.
17860  *
17861  * Fork - LGPL
17862  * <script type="text/javascript">
17863  */
17864  
17865 /**
17866  * @class Roo.form.DateField
17867  * @extends Roo.form.TriggerField
17868  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17869 * @constructor
17870 * Create a new DateField
17871 * @param {Object} config
17872  */
17873 Roo.form.DateField = function(config)
17874 {
17875     Roo.form.DateField.superclass.constructor.call(this, config);
17876     
17877       this.addEvents({
17878          
17879         /**
17880          * @event select
17881          * Fires when a date is selected
17882              * @param {Roo.form.DateField} combo This combo box
17883              * @param {Date} date The date selected
17884              */
17885         'select' : true
17886          
17887     });
17888     
17889     
17890     if(typeof this.minValue == "string") {
17891         this.minValue = this.parseDate(this.minValue);
17892     }
17893     if(typeof this.maxValue == "string") {
17894         this.maxValue = this.parseDate(this.maxValue);
17895     }
17896     this.ddMatch = null;
17897     if(this.disabledDates){
17898         var dd = this.disabledDates;
17899         var re = "(?:";
17900         for(var i = 0; i < dd.length; i++){
17901             re += dd[i];
17902             if(i != dd.length-1) {
17903                 re += "|";
17904             }
17905         }
17906         this.ddMatch = new RegExp(re + ")");
17907     }
17908 };
17909
17910 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17911     /**
17912      * @cfg {String} format
17913      * The default date format string which can be overriden for localization support.  The format must be
17914      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17915      */
17916     format : "m/d/y",
17917     /**
17918      * @cfg {String} altFormats
17919      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17920      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17921      */
17922     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17923     /**
17924      * @cfg {Array} disabledDays
17925      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17926      */
17927     disabledDays : null,
17928     /**
17929      * @cfg {String} disabledDaysText
17930      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17931      */
17932     disabledDaysText : "Disabled",
17933     /**
17934      * @cfg {Array} disabledDates
17935      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17936      * expression so they are very powerful. Some examples:
17937      * <ul>
17938      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17939      * <li>["03/08", "09/16"] would disable those days for every year</li>
17940      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17941      * <li>["03/../2006"] would disable every day in March 2006</li>
17942      * <li>["^03"] would disable every day in every March</li>
17943      * </ul>
17944      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17945      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17946      */
17947     disabledDates : null,
17948     /**
17949      * @cfg {String} disabledDatesText
17950      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17951      */
17952     disabledDatesText : "Disabled",
17953     /**
17954      * @cfg {Date/String} minValue
17955      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17956      * valid format (defaults to null).
17957      */
17958     minValue : null,
17959     /**
17960      * @cfg {Date/String} maxValue
17961      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17962      * valid format (defaults to null).
17963      */
17964     maxValue : null,
17965     /**
17966      * @cfg {String} minText
17967      * The error text to display when the date in the cell is before minValue (defaults to
17968      * 'The date in this field must be after {minValue}').
17969      */
17970     minText : "The date in this field must be equal to or after {0}",
17971     /**
17972      * @cfg {String} maxText
17973      * The error text to display when the date in the cell is after maxValue (defaults to
17974      * 'The date in this field must be before {maxValue}').
17975      */
17976     maxText : "The date in this field must be equal to or before {0}",
17977     /**
17978      * @cfg {String} invalidText
17979      * The error text to display when the date in the field is invalid (defaults to
17980      * '{value} is not a valid date - it must be in the format {format}').
17981      */
17982     invalidText : "{0} is not a valid date - it must be in the format {1}",
17983     /**
17984      * @cfg {String} triggerClass
17985      * An additional CSS class used to style the trigger button.  The trigger will always get the
17986      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17987      * which displays a calendar icon).
17988      */
17989     triggerClass : 'x-form-date-trigger',
17990     
17991
17992     /**
17993      * @cfg {Boolean} useIso
17994      * if enabled, then the date field will use a hidden field to store the 
17995      * real value as iso formated date. default (false)
17996      */ 
17997     useIso : false,
17998     /**
17999      * @cfg {String/Object} autoCreate
18000      * A DomHelper element spec, or true for a default element spec (defaults to
18001      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18002      */ 
18003     // private
18004     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18005     
18006     // private
18007     hiddenField: false,
18008     
18009     onRender : function(ct, position)
18010     {
18011         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18012         if (this.useIso) {
18013             //this.el.dom.removeAttribute('name'); 
18014             Roo.log("Changing name?");
18015             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18016             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18017                     'before', true);
18018             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18019             // prevent input submission
18020             this.hiddenName = this.name;
18021         }
18022             
18023             
18024     },
18025     
18026     // private
18027     validateValue : function(value)
18028     {
18029         value = this.formatDate(value);
18030         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18031             Roo.log('super failed');
18032             return false;
18033         }
18034         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18035              return true;
18036         }
18037         var svalue = value;
18038         value = this.parseDate(value);
18039         if(!value){
18040             Roo.log('parse date failed' + svalue);
18041             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18042             return false;
18043         }
18044         var time = value.getTime();
18045         if(this.minValue && time < this.minValue.getTime()){
18046             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18047             return false;
18048         }
18049         if(this.maxValue && time > this.maxValue.getTime()){
18050             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18051             return false;
18052         }
18053         if(this.disabledDays){
18054             var day = value.getDay();
18055             for(var i = 0; i < this.disabledDays.length; i++) {
18056                 if(day === this.disabledDays[i]){
18057                     this.markInvalid(this.disabledDaysText);
18058                     return false;
18059                 }
18060             }
18061         }
18062         var fvalue = this.formatDate(value);
18063         if(this.ddMatch && this.ddMatch.test(fvalue)){
18064             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18065             return false;
18066         }
18067         return true;
18068     },
18069
18070     // private
18071     // Provides logic to override the default TriggerField.validateBlur which just returns true
18072     validateBlur : function(){
18073         return !this.menu || !this.menu.isVisible();
18074     },
18075     
18076     getName: function()
18077     {
18078         // returns hidden if it's set..
18079         if (!this.rendered) {return ''};
18080         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18081         
18082     },
18083
18084     /**
18085      * Returns the current date value of the date field.
18086      * @return {Date} The date value
18087      */
18088     getValue : function(){
18089         
18090         return  this.hiddenField ?
18091                 this.hiddenField.value :
18092                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18093     },
18094
18095     /**
18096      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18097      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18098      * (the default format used is "m/d/y").
18099      * <br />Usage:
18100      * <pre><code>
18101 //All of these calls set the same date value (May 4, 2006)
18102
18103 //Pass a date object:
18104 var dt = new Date('5/4/06');
18105 dateField.setValue(dt);
18106
18107 //Pass a date string (default format):
18108 dateField.setValue('5/4/06');
18109
18110 //Pass a date string (custom format):
18111 dateField.format = 'Y-m-d';
18112 dateField.setValue('2006-5-4');
18113 </code></pre>
18114      * @param {String/Date} date The date or valid date string
18115      */
18116     setValue : function(date){
18117         if (this.hiddenField) {
18118             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18119         }
18120         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18121         // make sure the value field is always stored as a date..
18122         this.value = this.parseDate(date);
18123         
18124         
18125     },
18126
18127     // private
18128     parseDate : function(value){
18129         if(!value || value instanceof Date){
18130             return value;
18131         }
18132         var v = Date.parseDate(value, this.format);
18133          if (!v && this.useIso) {
18134             v = Date.parseDate(value, 'Y-m-d');
18135         }
18136         if(!v && this.altFormats){
18137             if(!this.altFormatsArray){
18138                 this.altFormatsArray = this.altFormats.split("|");
18139             }
18140             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18141                 v = Date.parseDate(value, this.altFormatsArray[i]);
18142             }
18143         }
18144         return v;
18145     },
18146
18147     // private
18148     formatDate : function(date, fmt){
18149         return (!date || !(date instanceof Date)) ?
18150                date : date.dateFormat(fmt || this.format);
18151     },
18152
18153     // private
18154     menuListeners : {
18155         select: function(m, d){
18156             
18157             this.setValue(d);
18158             this.fireEvent('select', this, d);
18159         },
18160         show : function(){ // retain focus styling
18161             this.onFocus();
18162         },
18163         hide : function(){
18164             this.focus.defer(10, this);
18165             var ml = this.menuListeners;
18166             this.menu.un("select", ml.select,  this);
18167             this.menu.un("show", ml.show,  this);
18168             this.menu.un("hide", ml.hide,  this);
18169         }
18170     },
18171
18172     // private
18173     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18174     onTriggerClick : function(){
18175         if(this.disabled){
18176             return;
18177         }
18178         if(this.menu == null){
18179             this.menu = new Roo.menu.DateMenu();
18180         }
18181         Roo.apply(this.menu.picker,  {
18182             showClear: this.allowBlank,
18183             minDate : this.minValue,
18184             maxDate : this.maxValue,
18185             disabledDatesRE : this.ddMatch,
18186             disabledDatesText : this.disabledDatesText,
18187             disabledDays : this.disabledDays,
18188             disabledDaysText : this.disabledDaysText,
18189             format : this.useIso ? 'Y-m-d' : this.format,
18190             minText : String.format(this.minText, this.formatDate(this.minValue)),
18191             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18192         });
18193         this.menu.on(Roo.apply({}, this.menuListeners, {
18194             scope:this
18195         }));
18196         this.menu.picker.setValue(this.getValue() || new Date());
18197         this.menu.show(this.el, "tl-bl?");
18198     },
18199
18200     beforeBlur : function(){
18201         var v = this.parseDate(this.getRawValue());
18202         if(v){
18203             this.setValue(v);
18204         }
18205     },
18206
18207     /*@
18208      * overide
18209      * 
18210      */
18211     isDirty : function() {
18212         if(this.disabled) {
18213             return false;
18214         }
18215         
18216         if(typeof(this.startValue) === 'undefined'){
18217             return false;
18218         }
18219         
18220         return String(this.getValue()) !== String(this.startValue);
18221         
18222     },
18223     // @overide
18224     cleanLeadingSpace : function(e)
18225     {
18226        return;
18227     }
18228     
18229 });/*
18230  * Based on:
18231  * Ext JS Library 1.1.1
18232  * Copyright(c) 2006-2007, Ext JS, LLC.
18233  *
18234  * Originally Released Under LGPL - original licence link has changed is not relivant.
18235  *
18236  * Fork - LGPL
18237  * <script type="text/javascript">
18238  */
18239  
18240 /**
18241  * @class Roo.form.MonthField
18242  * @extends Roo.form.TriggerField
18243  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18244 * @constructor
18245 * Create a new MonthField
18246 * @param {Object} config
18247  */
18248 Roo.form.MonthField = function(config){
18249     
18250     Roo.form.MonthField.superclass.constructor.call(this, config);
18251     
18252       this.addEvents({
18253          
18254         /**
18255          * @event select
18256          * Fires when a date is selected
18257              * @param {Roo.form.MonthFieeld} combo This combo box
18258              * @param {Date} date The date selected
18259              */
18260         'select' : true
18261          
18262     });
18263     
18264     
18265     if(typeof this.minValue == "string") {
18266         this.minValue = this.parseDate(this.minValue);
18267     }
18268     if(typeof this.maxValue == "string") {
18269         this.maxValue = this.parseDate(this.maxValue);
18270     }
18271     this.ddMatch = null;
18272     if(this.disabledDates){
18273         var dd = this.disabledDates;
18274         var re = "(?:";
18275         for(var i = 0; i < dd.length; i++){
18276             re += dd[i];
18277             if(i != dd.length-1) {
18278                 re += "|";
18279             }
18280         }
18281         this.ddMatch = new RegExp(re + ")");
18282     }
18283 };
18284
18285 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18286     /**
18287      * @cfg {String} format
18288      * The default date format string which can be overriden for localization support.  The format must be
18289      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18290      */
18291     format : "M Y",
18292     /**
18293      * @cfg {String} altFormats
18294      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18295      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18296      */
18297     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18298     /**
18299      * @cfg {Array} disabledDays
18300      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18301      */
18302     disabledDays : [0,1,2,3,4,5,6],
18303     /**
18304      * @cfg {String} disabledDaysText
18305      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18306      */
18307     disabledDaysText : "Disabled",
18308     /**
18309      * @cfg {Array} disabledDates
18310      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18311      * expression so they are very powerful. Some examples:
18312      * <ul>
18313      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18314      * <li>["03/08", "09/16"] would disable those days for every year</li>
18315      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18316      * <li>["03/../2006"] would disable every day in March 2006</li>
18317      * <li>["^03"] would disable every day in every March</li>
18318      * </ul>
18319      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18320      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18321      */
18322     disabledDates : null,
18323     /**
18324      * @cfg {String} disabledDatesText
18325      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18326      */
18327     disabledDatesText : "Disabled",
18328     /**
18329      * @cfg {Date/String} minValue
18330      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18331      * valid format (defaults to null).
18332      */
18333     minValue : null,
18334     /**
18335      * @cfg {Date/String} maxValue
18336      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18337      * valid format (defaults to null).
18338      */
18339     maxValue : null,
18340     /**
18341      * @cfg {String} minText
18342      * The error text to display when the date in the cell is before minValue (defaults to
18343      * 'The date in this field must be after {minValue}').
18344      */
18345     minText : "The date in this field must be equal to or after {0}",
18346     /**
18347      * @cfg {String} maxTextf
18348      * The error text to display when the date in the cell is after maxValue (defaults to
18349      * 'The date in this field must be before {maxValue}').
18350      */
18351     maxText : "The date in this field must be equal to or before {0}",
18352     /**
18353      * @cfg {String} invalidText
18354      * The error text to display when the date in the field is invalid (defaults to
18355      * '{value} is not a valid date - it must be in the format {format}').
18356      */
18357     invalidText : "{0} is not a valid date - it must be in the format {1}",
18358     /**
18359      * @cfg {String} triggerClass
18360      * An additional CSS class used to style the trigger button.  The trigger will always get the
18361      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18362      * which displays a calendar icon).
18363      */
18364     triggerClass : 'x-form-date-trigger',
18365     
18366
18367     /**
18368      * @cfg {Boolean} useIso
18369      * if enabled, then the date field will use a hidden field to store the 
18370      * real value as iso formated date. default (true)
18371      */ 
18372     useIso : true,
18373     /**
18374      * @cfg {String/Object} autoCreate
18375      * A DomHelper element spec, or true for a default element spec (defaults to
18376      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18377      */ 
18378     // private
18379     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18380     
18381     // private
18382     hiddenField: false,
18383     
18384     hideMonthPicker : false,
18385     
18386     onRender : function(ct, position)
18387     {
18388         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18389         if (this.useIso) {
18390             this.el.dom.removeAttribute('name'); 
18391             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18392                     'before', true);
18393             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18394             // prevent input submission
18395             this.hiddenName = this.name;
18396         }
18397             
18398             
18399     },
18400     
18401     // private
18402     validateValue : function(value)
18403     {
18404         value = this.formatDate(value);
18405         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18406             return false;
18407         }
18408         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18409              return true;
18410         }
18411         var svalue = value;
18412         value = this.parseDate(value);
18413         if(!value){
18414             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18415             return false;
18416         }
18417         var time = value.getTime();
18418         if(this.minValue && time < this.minValue.getTime()){
18419             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18420             return false;
18421         }
18422         if(this.maxValue && time > this.maxValue.getTime()){
18423             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18424             return false;
18425         }
18426         /*if(this.disabledDays){
18427             var day = value.getDay();
18428             for(var i = 0; i < this.disabledDays.length; i++) {
18429                 if(day === this.disabledDays[i]){
18430                     this.markInvalid(this.disabledDaysText);
18431                     return false;
18432                 }
18433             }
18434         }
18435         */
18436         var fvalue = this.formatDate(value);
18437         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18438             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18439             return false;
18440         }
18441         */
18442         return true;
18443     },
18444
18445     // private
18446     // Provides logic to override the default TriggerField.validateBlur which just returns true
18447     validateBlur : function(){
18448         return !this.menu || !this.menu.isVisible();
18449     },
18450
18451     /**
18452      * Returns the current date value of the date field.
18453      * @return {Date} The date value
18454      */
18455     getValue : function(){
18456         
18457         
18458         
18459         return  this.hiddenField ?
18460                 this.hiddenField.value :
18461                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18462     },
18463
18464     /**
18465      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18466      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18467      * (the default format used is "m/d/y").
18468      * <br />Usage:
18469      * <pre><code>
18470 //All of these calls set the same date value (May 4, 2006)
18471
18472 //Pass a date object:
18473 var dt = new Date('5/4/06');
18474 monthField.setValue(dt);
18475
18476 //Pass a date string (default format):
18477 monthField.setValue('5/4/06');
18478
18479 //Pass a date string (custom format):
18480 monthField.format = 'Y-m-d';
18481 monthField.setValue('2006-5-4');
18482 </code></pre>
18483      * @param {String/Date} date The date or valid date string
18484      */
18485     setValue : function(date){
18486         Roo.log('month setValue' + date);
18487         // can only be first of month..
18488         
18489         var val = this.parseDate(date);
18490         
18491         if (this.hiddenField) {
18492             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18493         }
18494         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18495         this.value = this.parseDate(date);
18496     },
18497
18498     // private
18499     parseDate : function(value){
18500         if(!value || value instanceof Date){
18501             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18502             return value;
18503         }
18504         var v = Date.parseDate(value, this.format);
18505         if (!v && this.useIso) {
18506             v = Date.parseDate(value, 'Y-m-d');
18507         }
18508         if (v) {
18509             // 
18510             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18511         }
18512         
18513         
18514         if(!v && this.altFormats){
18515             if(!this.altFormatsArray){
18516                 this.altFormatsArray = this.altFormats.split("|");
18517             }
18518             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18519                 v = Date.parseDate(value, this.altFormatsArray[i]);
18520             }
18521         }
18522         return v;
18523     },
18524
18525     // private
18526     formatDate : function(date, fmt){
18527         return (!date || !(date instanceof Date)) ?
18528                date : date.dateFormat(fmt || this.format);
18529     },
18530
18531     // private
18532     menuListeners : {
18533         select: function(m, d){
18534             this.setValue(d);
18535             this.fireEvent('select', this, d);
18536         },
18537         show : function(){ // retain focus styling
18538             this.onFocus();
18539         },
18540         hide : function(){
18541             this.focus.defer(10, this);
18542             var ml = this.menuListeners;
18543             this.menu.un("select", ml.select,  this);
18544             this.menu.un("show", ml.show,  this);
18545             this.menu.un("hide", ml.hide,  this);
18546         }
18547     },
18548     // private
18549     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18550     onTriggerClick : function(){
18551         if(this.disabled){
18552             return;
18553         }
18554         if(this.menu == null){
18555             this.menu = new Roo.menu.DateMenu();
18556            
18557         }
18558         
18559         Roo.apply(this.menu.picker,  {
18560             
18561             showClear: this.allowBlank,
18562             minDate : this.minValue,
18563             maxDate : this.maxValue,
18564             disabledDatesRE : this.ddMatch,
18565             disabledDatesText : this.disabledDatesText,
18566             
18567             format : this.useIso ? 'Y-m-d' : this.format,
18568             minText : String.format(this.minText, this.formatDate(this.minValue)),
18569             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18570             
18571         });
18572          this.menu.on(Roo.apply({}, this.menuListeners, {
18573             scope:this
18574         }));
18575        
18576         
18577         var m = this.menu;
18578         var p = m.picker;
18579         
18580         // hide month picker get's called when we called by 'before hide';
18581         
18582         var ignorehide = true;
18583         p.hideMonthPicker  = function(disableAnim){
18584             if (ignorehide) {
18585                 return;
18586             }
18587              if(this.monthPicker){
18588                 Roo.log("hideMonthPicker called");
18589                 if(disableAnim === true){
18590                     this.monthPicker.hide();
18591                 }else{
18592                     this.monthPicker.slideOut('t', {duration:.2});
18593                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18594                     p.fireEvent("select", this, this.value);
18595                     m.hide();
18596                 }
18597             }
18598         }
18599         
18600         Roo.log('picker set value');
18601         Roo.log(this.getValue());
18602         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18603         m.show(this.el, 'tl-bl?');
18604         ignorehide  = false;
18605         // this will trigger hideMonthPicker..
18606         
18607         
18608         // hidden the day picker
18609         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18610         
18611         
18612         
18613       
18614         
18615         p.showMonthPicker.defer(100, p);
18616     
18617         
18618        
18619     },
18620
18621     beforeBlur : function(){
18622         var v = this.parseDate(this.getRawValue());
18623         if(v){
18624             this.setValue(v);
18625         }
18626     }
18627
18628     /** @cfg {Boolean} grow @hide */
18629     /** @cfg {Number} growMin @hide */
18630     /** @cfg {Number} growMax @hide */
18631     /**
18632      * @hide
18633      * @method autoSize
18634      */
18635 });/*
18636  * Based on:
18637  * Ext JS Library 1.1.1
18638  * Copyright(c) 2006-2007, Ext JS, LLC.
18639  *
18640  * Originally Released Under LGPL - original licence link has changed is not relivant.
18641  *
18642  * Fork - LGPL
18643  * <script type="text/javascript">
18644  */
18645  
18646
18647 /**
18648  * @class Roo.form.ComboBox
18649  * @extends Roo.form.TriggerField
18650  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18651  * @constructor
18652  * Create a new ComboBox.
18653  * @param {Object} config Configuration options
18654  */
18655 Roo.form.ComboBox = function(config){
18656     Roo.form.ComboBox.superclass.constructor.call(this, config);
18657     this.addEvents({
18658         /**
18659          * @event expand
18660          * Fires when the dropdown list is expanded
18661              * @param {Roo.form.ComboBox} combo This combo box
18662              */
18663         'expand' : true,
18664         /**
18665          * @event collapse
18666          * Fires when the dropdown list is collapsed
18667              * @param {Roo.form.ComboBox} combo This combo box
18668              */
18669         'collapse' : true,
18670         /**
18671          * @event beforeselect
18672          * Fires before a list item is selected. Return false to cancel the selection.
18673              * @param {Roo.form.ComboBox} combo This combo box
18674              * @param {Roo.data.Record} record The data record returned from the underlying store
18675              * @param {Number} index The index of the selected item in the dropdown list
18676              */
18677         'beforeselect' : true,
18678         /**
18679          * @event select
18680          * Fires when a list item is selected
18681              * @param {Roo.form.ComboBox} combo This combo box
18682              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18683              * @param {Number} index The index of the selected item in the dropdown list
18684              */
18685         'select' : true,
18686         /**
18687          * @event beforequery
18688          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18689          * The event object passed has these properties:
18690              * @param {Roo.form.ComboBox} combo This combo box
18691              * @param {String} query The query
18692              * @param {Boolean} forceAll true to force "all" query
18693              * @param {Boolean} cancel true to cancel the query
18694              * @param {Object} e The query event object
18695              */
18696         'beforequery': true,
18697          /**
18698          * @event add
18699          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18700              * @param {Roo.form.ComboBox} combo This combo box
18701              */
18702         'add' : true,
18703         /**
18704          * @event edit
18705          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18706              * @param {Roo.form.ComboBox} combo This combo box
18707              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18708              */
18709         'edit' : true
18710         
18711         
18712     });
18713     if(this.transform){
18714         this.allowDomMove = false;
18715         var s = Roo.getDom(this.transform);
18716         if(!this.hiddenName){
18717             this.hiddenName = s.name;
18718         }
18719         if(!this.store){
18720             this.mode = 'local';
18721             var d = [], opts = s.options;
18722             for(var i = 0, len = opts.length;i < len; i++){
18723                 var o = opts[i];
18724                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18725                 if(o.selected) {
18726                     this.value = value;
18727                 }
18728                 d.push([value, o.text]);
18729             }
18730             this.store = new Roo.data.SimpleStore({
18731                 'id': 0,
18732                 fields: ['value', 'text'],
18733                 data : d
18734             });
18735             this.valueField = 'value';
18736             this.displayField = 'text';
18737         }
18738         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18739         if(!this.lazyRender){
18740             this.target = true;
18741             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18742             s.parentNode.removeChild(s); // remove it
18743             this.render(this.el.parentNode);
18744         }else{
18745             s.parentNode.removeChild(s); // remove it
18746         }
18747
18748     }
18749     if (this.store) {
18750         this.store = Roo.factory(this.store, Roo.data);
18751     }
18752     
18753     this.selectedIndex = -1;
18754     if(this.mode == 'local'){
18755         if(config.queryDelay === undefined){
18756             this.queryDelay = 10;
18757         }
18758         if(config.minChars === undefined){
18759             this.minChars = 0;
18760         }
18761     }
18762 };
18763
18764 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18765     /**
18766      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18767      */
18768     /**
18769      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18770      * rendering into an Roo.Editor, defaults to false)
18771      */
18772     /**
18773      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18774      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18775      */
18776     /**
18777      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18778      */
18779     /**
18780      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18781      * the dropdown list (defaults to undefined, with no header element)
18782      */
18783
18784      /**
18785      * @cfg {String/Roo.Template} tpl The template to use to render the output
18786      */
18787      
18788     // private
18789     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18790     /**
18791      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18792      */
18793     listWidth: undefined,
18794     /**
18795      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18796      * mode = 'remote' or 'text' if mode = 'local')
18797      */
18798     displayField: undefined,
18799     /**
18800      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18801      * mode = 'remote' or 'value' if mode = 'local'). 
18802      * Note: use of a valueField requires the user make a selection
18803      * in order for a value to be mapped.
18804      */
18805     valueField: undefined,
18806     
18807     
18808     /**
18809      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18810      * field's data value (defaults to the underlying DOM element's name)
18811      */
18812     hiddenName: undefined,
18813     /**
18814      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18815      */
18816     listClass: '',
18817     /**
18818      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18819      */
18820     selectedClass: 'x-combo-selected',
18821     /**
18822      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18823      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18824      * which displays a downward arrow icon).
18825      */
18826     triggerClass : 'x-form-arrow-trigger',
18827     /**
18828      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18829      */
18830     shadow:'sides',
18831     /**
18832      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18833      * anchor positions (defaults to 'tl-bl')
18834      */
18835     listAlign: 'tl-bl?',
18836     /**
18837      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18838      */
18839     maxHeight: 300,
18840     /**
18841      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18842      * query specified by the allQuery config option (defaults to 'query')
18843      */
18844     triggerAction: 'query',
18845     /**
18846      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18847      * (defaults to 4, does not apply if editable = false)
18848      */
18849     minChars : 4,
18850     /**
18851      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18852      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18853      */
18854     typeAhead: false,
18855     /**
18856      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18857      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18858      */
18859     queryDelay: 500,
18860     /**
18861      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18862      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18863      */
18864     pageSize: 0,
18865     /**
18866      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18867      * when editable = true (defaults to false)
18868      */
18869     selectOnFocus:false,
18870     /**
18871      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18872      */
18873     queryParam: 'query',
18874     /**
18875      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18876      * when mode = 'remote' (defaults to 'Loading...')
18877      */
18878     loadingText: 'Loading...',
18879     /**
18880      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18881      */
18882     resizable: false,
18883     /**
18884      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18885      */
18886     handleHeight : 8,
18887     /**
18888      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18889      * traditional select (defaults to true)
18890      */
18891     editable: true,
18892     /**
18893      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18894      */
18895     allQuery: '',
18896     /**
18897      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18898      */
18899     mode: 'remote',
18900     /**
18901      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18902      * listWidth has a higher value)
18903      */
18904     minListWidth : 70,
18905     /**
18906      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18907      * allow the user to set arbitrary text into the field (defaults to false)
18908      */
18909     forceSelection:false,
18910     /**
18911      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18912      * if typeAhead = true (defaults to 250)
18913      */
18914     typeAheadDelay : 250,
18915     /**
18916      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18917      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18918      */
18919     valueNotFoundText : undefined,
18920     /**
18921      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18922      */
18923     blockFocus : false,
18924     
18925     /**
18926      * @cfg {Boolean} disableClear Disable showing of clear button.
18927      */
18928     disableClear : false,
18929     /**
18930      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18931      */
18932     alwaysQuery : false,
18933     
18934     //private
18935     addicon : false,
18936     editicon: false,
18937     
18938     // element that contains real text value.. (when hidden is used..)
18939      
18940     // private
18941     onRender : function(ct, position)
18942     {
18943         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18944         
18945         if(this.hiddenName){
18946             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18947                     'before', true);
18948             this.hiddenField.value =
18949                 this.hiddenValue !== undefined ? this.hiddenValue :
18950                 this.value !== undefined ? this.value : '';
18951
18952             // prevent input submission
18953             this.el.dom.removeAttribute('name');
18954              
18955              
18956         }
18957         
18958         if(Roo.isGecko){
18959             this.el.dom.setAttribute('autocomplete', 'off');
18960         }
18961
18962         var cls = 'x-combo-list';
18963
18964         this.list = new Roo.Layer({
18965             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18966         });
18967
18968         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18969         this.list.setWidth(lw);
18970         this.list.swallowEvent('mousewheel');
18971         this.assetHeight = 0;
18972
18973         if(this.title){
18974             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18975             this.assetHeight += this.header.getHeight();
18976         }
18977
18978         this.innerList = this.list.createChild({cls:cls+'-inner'});
18979         this.innerList.on('mouseover', this.onViewOver, this);
18980         this.innerList.on('mousemove', this.onViewMove, this);
18981         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18982         
18983         if(this.allowBlank && !this.pageSize && !this.disableClear){
18984             this.footer = this.list.createChild({cls:cls+'-ft'});
18985             this.pageTb = new Roo.Toolbar(this.footer);
18986            
18987         }
18988         if(this.pageSize){
18989             this.footer = this.list.createChild({cls:cls+'-ft'});
18990             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18991                     {pageSize: this.pageSize});
18992             
18993         }
18994         
18995         if (this.pageTb && this.allowBlank && !this.disableClear) {
18996             var _this = this;
18997             this.pageTb.add(new Roo.Toolbar.Fill(), {
18998                 cls: 'x-btn-icon x-btn-clear',
18999                 text: '&#160;',
19000                 handler: function()
19001                 {
19002                     _this.collapse();
19003                     _this.clearValue();
19004                     _this.onSelect(false, -1);
19005                 }
19006             });
19007         }
19008         if (this.footer) {
19009             this.assetHeight += this.footer.getHeight();
19010         }
19011         
19012
19013         if(!this.tpl){
19014             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19015         }
19016
19017         this.view = new Roo.View(this.innerList, this.tpl, {
19018             singleSelect:true,
19019             store: this.store,
19020             selectedClass: this.selectedClass
19021         });
19022
19023         this.view.on('click', this.onViewClick, this);
19024
19025         this.store.on('beforeload', this.onBeforeLoad, this);
19026         this.store.on('load', this.onLoad, this);
19027         this.store.on('loadexception', this.onLoadException, this);
19028
19029         if(this.resizable){
19030             this.resizer = new Roo.Resizable(this.list,  {
19031                pinned:true, handles:'se'
19032             });
19033             this.resizer.on('resize', function(r, w, h){
19034                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19035                 this.listWidth = w;
19036                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19037                 this.restrictHeight();
19038             }, this);
19039             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19040         }
19041         if(!this.editable){
19042             this.editable = true;
19043             this.setEditable(false);
19044         }  
19045         
19046         
19047         if (typeof(this.events.add.listeners) != 'undefined') {
19048             
19049             this.addicon = this.wrap.createChild(
19050                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19051        
19052             this.addicon.on('click', function(e) {
19053                 this.fireEvent('add', this);
19054             }, this);
19055         }
19056         if (typeof(this.events.edit.listeners) != 'undefined') {
19057             
19058             this.editicon = this.wrap.createChild(
19059                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19060             if (this.addicon) {
19061                 this.editicon.setStyle('margin-left', '40px');
19062             }
19063             this.editicon.on('click', function(e) {
19064                 
19065                 // we fire even  if inothing is selected..
19066                 this.fireEvent('edit', this, this.lastData );
19067                 
19068             }, this);
19069         }
19070         
19071         
19072         
19073     },
19074
19075     // private
19076     initEvents : function(){
19077         Roo.form.ComboBox.superclass.initEvents.call(this);
19078
19079         this.keyNav = new Roo.KeyNav(this.el, {
19080             "up" : function(e){
19081                 this.inKeyMode = true;
19082                 this.selectPrev();
19083             },
19084
19085             "down" : function(e){
19086                 if(!this.isExpanded()){
19087                     this.onTriggerClick();
19088                 }else{
19089                     this.inKeyMode = true;
19090                     this.selectNext();
19091                 }
19092             },
19093
19094             "enter" : function(e){
19095                 this.onViewClick();
19096                 //return true;
19097             },
19098
19099             "esc" : function(e){
19100                 this.collapse();
19101             },
19102
19103             "tab" : function(e){
19104                 this.onViewClick(false);
19105                 this.fireEvent("specialkey", this, e);
19106                 return true;
19107             },
19108
19109             scope : this,
19110
19111             doRelay : function(foo, bar, hname){
19112                 if(hname == 'down' || this.scope.isExpanded()){
19113                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19114                 }
19115                 return true;
19116             },
19117
19118             forceKeyDown: true
19119         });
19120         this.queryDelay = Math.max(this.queryDelay || 10,
19121                 this.mode == 'local' ? 10 : 250);
19122         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19123         if(this.typeAhead){
19124             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19125         }
19126         if(this.editable !== false){
19127             this.el.on("keyup", this.onKeyUp, this);
19128         }
19129         if(this.forceSelection){
19130             this.on('blur', this.doForce, this);
19131         }
19132     },
19133
19134     onDestroy : function(){
19135         if(this.view){
19136             this.view.setStore(null);
19137             this.view.el.removeAllListeners();
19138             this.view.el.remove();
19139             this.view.purgeListeners();
19140         }
19141         if(this.list){
19142             this.list.destroy();
19143         }
19144         if(this.store){
19145             this.store.un('beforeload', this.onBeforeLoad, this);
19146             this.store.un('load', this.onLoad, this);
19147             this.store.un('loadexception', this.onLoadException, this);
19148         }
19149         Roo.form.ComboBox.superclass.onDestroy.call(this);
19150     },
19151
19152     // private
19153     fireKey : function(e){
19154         if(e.isNavKeyPress() && !this.list.isVisible()){
19155             this.fireEvent("specialkey", this, e);
19156         }
19157     },
19158
19159     // private
19160     onResize: function(w, h){
19161         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19162         
19163         if(typeof w != 'number'){
19164             // we do not handle it!?!?
19165             return;
19166         }
19167         var tw = this.trigger.getWidth();
19168         tw += this.addicon ? this.addicon.getWidth() : 0;
19169         tw += this.editicon ? this.editicon.getWidth() : 0;
19170         var x = w - tw;
19171         this.el.setWidth( this.adjustWidth('input', x));
19172             
19173         this.trigger.setStyle('left', x+'px');
19174         
19175         if(this.list && this.listWidth === undefined){
19176             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19177             this.list.setWidth(lw);
19178             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19179         }
19180         
19181     
19182         
19183     },
19184
19185     /**
19186      * Allow or prevent the user from directly editing the field text.  If false is passed,
19187      * the user will only be able to select from the items defined in the dropdown list.  This method
19188      * is the runtime equivalent of setting the 'editable' config option at config time.
19189      * @param {Boolean} value True to allow the user to directly edit the field text
19190      */
19191     setEditable : function(value){
19192         if(value == this.editable){
19193             return;
19194         }
19195         this.editable = value;
19196         if(!value){
19197             this.el.dom.setAttribute('readOnly', true);
19198             this.el.on('mousedown', this.onTriggerClick,  this);
19199             this.el.addClass('x-combo-noedit');
19200         }else{
19201             this.el.dom.setAttribute('readOnly', false);
19202             this.el.un('mousedown', this.onTriggerClick,  this);
19203             this.el.removeClass('x-combo-noedit');
19204         }
19205     },
19206
19207     // private
19208     onBeforeLoad : function(){
19209         if(!this.hasFocus){
19210             return;
19211         }
19212         this.innerList.update(this.loadingText ?
19213                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19214         this.restrictHeight();
19215         this.selectedIndex = -1;
19216     },
19217
19218     // private
19219     onLoad : function(){
19220         if(!this.hasFocus){
19221             return;
19222         }
19223         if(this.store.getCount() > 0){
19224             this.expand();
19225             this.restrictHeight();
19226             if(this.lastQuery == this.allQuery){
19227                 if(this.editable){
19228                     this.el.dom.select();
19229                 }
19230                 if(!this.selectByValue(this.value, true)){
19231                     this.select(0, true);
19232                 }
19233             }else{
19234                 this.selectNext();
19235                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19236                     this.taTask.delay(this.typeAheadDelay);
19237                 }
19238             }
19239         }else{
19240             this.onEmptyResults();
19241         }
19242         //this.el.focus();
19243     },
19244     // private
19245     onLoadException : function()
19246     {
19247         this.collapse();
19248         Roo.log(this.store.reader.jsonData);
19249         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19250             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19251         }
19252         
19253         
19254     },
19255     // private
19256     onTypeAhead : function(){
19257         if(this.store.getCount() > 0){
19258             var r = this.store.getAt(0);
19259             var newValue = r.data[this.displayField];
19260             var len = newValue.length;
19261             var selStart = this.getRawValue().length;
19262             if(selStart != len){
19263                 this.setRawValue(newValue);
19264                 this.selectText(selStart, newValue.length);
19265             }
19266         }
19267     },
19268
19269     // private
19270     onSelect : function(record, index){
19271         if(this.fireEvent('beforeselect', this, record, index) !== false){
19272             this.setFromData(index > -1 ? record.data : false);
19273             this.collapse();
19274             this.fireEvent('select', this, record, index);
19275         }
19276     },
19277
19278     /**
19279      * Returns the currently selected field value or empty string if no value is set.
19280      * @return {String} value The selected value
19281      */
19282     getValue : function(){
19283         if(this.valueField){
19284             return typeof this.value != 'undefined' ? this.value : '';
19285         }
19286         return Roo.form.ComboBox.superclass.getValue.call(this);
19287     },
19288
19289     /**
19290      * Clears any text/value currently set in the field
19291      */
19292     clearValue : function(){
19293         if(this.hiddenField){
19294             this.hiddenField.value = '';
19295         }
19296         this.value = '';
19297         this.setRawValue('');
19298         this.lastSelectionText = '';
19299         
19300     },
19301
19302     /**
19303      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19304      * will be displayed in the field.  If the value does not match the data value of an existing item,
19305      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19306      * Otherwise the field will be blank (although the value will still be set).
19307      * @param {String} value The value to match
19308      */
19309     setValue : function(v){
19310         var text = v;
19311         if(this.valueField){
19312             var r = this.findRecord(this.valueField, v);
19313             if(r){
19314                 text = r.data[this.displayField];
19315             }else if(this.valueNotFoundText !== undefined){
19316                 text = this.valueNotFoundText;
19317             }
19318         }
19319         this.lastSelectionText = text;
19320         if(this.hiddenField){
19321             this.hiddenField.value = v;
19322         }
19323         Roo.form.ComboBox.superclass.setValue.call(this, text);
19324         this.value = v;
19325     },
19326     /**
19327      * @property {Object} the last set data for the element
19328      */
19329     
19330     lastData : false,
19331     /**
19332      * Sets the value of the field based on a object which is related to the record format for the store.
19333      * @param {Object} value the value to set as. or false on reset?
19334      */
19335     setFromData : function(o){
19336         var dv = ''; // display value
19337         var vv = ''; // value value..
19338         this.lastData = o;
19339         if (this.displayField) {
19340             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19341         } else {
19342             // this is an error condition!!!
19343             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19344         }
19345         
19346         if(this.valueField){
19347             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19348         }
19349         if(this.hiddenField){
19350             this.hiddenField.value = vv;
19351             
19352             this.lastSelectionText = dv;
19353             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19354             this.value = vv;
19355             return;
19356         }
19357         // no hidden field.. - we store the value in 'value', but still display
19358         // display field!!!!
19359         this.lastSelectionText = dv;
19360         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19361         this.value = vv;
19362         
19363         
19364     },
19365     // private
19366     reset : function(){
19367         // overridden so that last data is reset..
19368         this.setValue(this.resetValue);
19369         this.originalValue = this.getValue();
19370         this.clearInvalid();
19371         this.lastData = false;
19372         if (this.view) {
19373             this.view.clearSelections();
19374         }
19375     },
19376     // private
19377     findRecord : function(prop, value){
19378         var record;
19379         if(this.store.getCount() > 0){
19380             this.store.each(function(r){
19381                 if(r.data[prop] == value){
19382                     record = r;
19383                     return false;
19384                 }
19385                 return true;
19386             });
19387         }
19388         return record;
19389     },
19390     
19391     getName: function()
19392     {
19393         // returns hidden if it's set..
19394         if (!this.rendered) {return ''};
19395         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19396         
19397     },
19398     // private
19399     onViewMove : function(e, t){
19400         this.inKeyMode = false;
19401     },
19402
19403     // private
19404     onViewOver : function(e, t){
19405         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19406             return;
19407         }
19408         var item = this.view.findItemFromChild(t);
19409         if(item){
19410             var index = this.view.indexOf(item);
19411             this.select(index, false);
19412         }
19413     },
19414
19415     // private
19416     onViewClick : function(doFocus)
19417     {
19418         var index = this.view.getSelectedIndexes()[0];
19419         var r = this.store.getAt(index);
19420         if(r){
19421             this.onSelect(r, index);
19422         }
19423         if(doFocus !== false && !this.blockFocus){
19424             this.el.focus();
19425         }
19426     },
19427
19428     // private
19429     restrictHeight : function(){
19430         this.innerList.dom.style.height = '';
19431         var inner = this.innerList.dom;
19432         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19433         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19434         this.list.beginUpdate();
19435         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19436         this.list.alignTo(this.el, this.listAlign);
19437         this.list.endUpdate();
19438     },
19439
19440     // private
19441     onEmptyResults : function(){
19442         this.collapse();
19443     },
19444
19445     /**
19446      * Returns true if the dropdown list is expanded, else false.
19447      */
19448     isExpanded : function(){
19449         return this.list.isVisible();
19450     },
19451
19452     /**
19453      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19455      * @param {String} value The data value of the item to select
19456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19457      * selected item if it is not currently in view (defaults to true)
19458      * @return {Boolean} True if the value matched an item in the list, else false
19459      */
19460     selectByValue : function(v, scrollIntoView){
19461         if(v !== undefined && v !== null){
19462             var r = this.findRecord(this.valueField || this.displayField, v);
19463             if(r){
19464                 this.select(this.store.indexOf(r), scrollIntoView);
19465                 return true;
19466             }
19467         }
19468         return false;
19469     },
19470
19471     /**
19472      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19473      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19474      * @param {Number} index The zero-based index of the list item to select
19475      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19476      * selected item if it is not currently in view (defaults to true)
19477      */
19478     select : function(index, scrollIntoView){
19479         this.selectedIndex = index;
19480         this.view.select(index);
19481         if(scrollIntoView !== false){
19482             var el = this.view.getNode(index);
19483             if(el){
19484                 this.innerList.scrollChildIntoView(el, false);
19485             }
19486         }
19487     },
19488
19489     // private
19490     selectNext : function(){
19491         var ct = this.store.getCount();
19492         if(ct > 0){
19493             if(this.selectedIndex == -1){
19494                 this.select(0);
19495             }else if(this.selectedIndex < ct-1){
19496                 this.select(this.selectedIndex+1);
19497             }
19498         }
19499     },
19500
19501     // private
19502     selectPrev : function(){
19503         var ct = this.store.getCount();
19504         if(ct > 0){
19505             if(this.selectedIndex == -1){
19506                 this.select(0);
19507             }else if(this.selectedIndex != 0){
19508                 this.select(this.selectedIndex-1);
19509             }
19510         }
19511     },
19512
19513     // private
19514     onKeyUp : function(e){
19515         if(this.editable !== false && !e.isSpecialKey()){
19516             this.lastKey = e.getKey();
19517             this.dqTask.delay(this.queryDelay);
19518         }
19519     },
19520
19521     // private
19522     validateBlur : function(){
19523         return !this.list || !this.list.isVisible();   
19524     },
19525
19526     // private
19527     initQuery : function(){
19528         this.doQuery(this.getRawValue());
19529     },
19530
19531     // private
19532     doForce : function(){
19533         if(this.el.dom.value.length > 0){
19534             this.el.dom.value =
19535                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19536              
19537         }
19538     },
19539
19540     /**
19541      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19542      * query allowing the query action to be canceled if needed.
19543      * @param {String} query The SQL query to execute
19544      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19545      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19546      * saved in the current store (defaults to false)
19547      */
19548     doQuery : function(q, forceAll){
19549         if(q === undefined || q === null){
19550             q = '';
19551         }
19552         var qe = {
19553             query: q,
19554             forceAll: forceAll,
19555             combo: this,
19556             cancel:false
19557         };
19558         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19559             return false;
19560         }
19561         q = qe.query;
19562         forceAll = qe.forceAll;
19563         if(forceAll === true || (q.length >= this.minChars)){
19564             if(this.lastQuery != q || this.alwaysQuery){
19565                 this.lastQuery = q;
19566                 if(this.mode == 'local'){
19567                     this.selectedIndex = -1;
19568                     if(forceAll){
19569                         this.store.clearFilter();
19570                     }else{
19571                         this.store.filter(this.displayField, q);
19572                     }
19573                     this.onLoad();
19574                 }else{
19575                     this.store.baseParams[this.queryParam] = q;
19576                     this.store.load({
19577                         params: this.getParams(q)
19578                     });
19579                     this.expand();
19580                 }
19581             }else{
19582                 this.selectedIndex = -1;
19583                 this.onLoad();   
19584             }
19585         }
19586     },
19587
19588     // private
19589     getParams : function(q){
19590         var p = {};
19591         //p[this.queryParam] = q;
19592         if(this.pageSize){
19593             p.start = 0;
19594             p.limit = this.pageSize;
19595         }
19596         return p;
19597     },
19598
19599     /**
19600      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19601      */
19602     collapse : function(){
19603         if(!this.isExpanded()){
19604             return;
19605         }
19606         this.list.hide();
19607         Roo.get(document).un('mousedown', this.collapseIf, this);
19608         Roo.get(document).un('mousewheel', this.collapseIf, this);
19609         if (!this.editable) {
19610             Roo.get(document).un('keydown', this.listKeyPress, this);
19611         }
19612         this.fireEvent('collapse', this);
19613     },
19614
19615     // private
19616     collapseIf : function(e){
19617         if(!e.within(this.wrap) && !e.within(this.list)){
19618             this.collapse();
19619         }
19620     },
19621
19622     /**
19623      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19624      */
19625     expand : function(){
19626         if(this.isExpanded() || !this.hasFocus){
19627             return;
19628         }
19629         this.list.alignTo(this.el, this.listAlign);
19630         this.list.show();
19631         Roo.get(document).on('mousedown', this.collapseIf, this);
19632         Roo.get(document).on('mousewheel', this.collapseIf, this);
19633         if (!this.editable) {
19634             Roo.get(document).on('keydown', this.listKeyPress, this);
19635         }
19636         
19637         this.fireEvent('expand', this);
19638     },
19639
19640     // private
19641     // Implements the default empty TriggerField.onTriggerClick function
19642     onTriggerClick : function(){
19643         if(this.disabled){
19644             return;
19645         }
19646         if(this.isExpanded()){
19647             this.collapse();
19648             if (!this.blockFocus) {
19649                 this.el.focus();
19650             }
19651             
19652         }else {
19653             this.hasFocus = true;
19654             if(this.triggerAction == 'all') {
19655                 this.doQuery(this.allQuery, true);
19656             } else {
19657                 this.doQuery(this.getRawValue());
19658             }
19659             if (!this.blockFocus) {
19660                 this.el.focus();
19661             }
19662         }
19663     },
19664     listKeyPress : function(e)
19665     {
19666         //Roo.log('listkeypress');
19667         // scroll to first matching element based on key pres..
19668         if (e.isSpecialKey()) {
19669             return false;
19670         }
19671         var k = String.fromCharCode(e.getKey()).toUpperCase();
19672         //Roo.log(k);
19673         var match  = false;
19674         var csel = this.view.getSelectedNodes();
19675         var cselitem = false;
19676         if (csel.length) {
19677             var ix = this.view.indexOf(csel[0]);
19678             cselitem  = this.store.getAt(ix);
19679             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19680                 cselitem = false;
19681             }
19682             
19683         }
19684         
19685         this.store.each(function(v) { 
19686             if (cselitem) {
19687                 // start at existing selection.
19688                 if (cselitem.id == v.id) {
19689                     cselitem = false;
19690                 }
19691                 return;
19692             }
19693                 
19694             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19695                 match = this.store.indexOf(v);
19696                 return false;
19697             }
19698         }, this);
19699         
19700         if (match === false) {
19701             return true; // no more action?
19702         }
19703         // scroll to?
19704         this.view.select(match);
19705         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19706         sn.scrollIntoView(sn.dom.parentNode, false);
19707     } 
19708
19709     /** 
19710     * @cfg {Boolean} grow 
19711     * @hide 
19712     */
19713     /** 
19714     * @cfg {Number} growMin 
19715     * @hide 
19716     */
19717     /** 
19718     * @cfg {Number} growMax 
19719     * @hide 
19720     */
19721     /**
19722      * @hide
19723      * @method autoSize
19724      */
19725 });/*
19726  * Copyright(c) 2010-2012, Roo J Solutions Limited
19727  *
19728  * Licence LGPL
19729  *
19730  */
19731
19732 /**
19733  * @class Roo.form.ComboBoxArray
19734  * @extends Roo.form.TextField
19735  * A facebook style adder... for lists of email / people / countries  etc...
19736  * pick multiple items from a combo box, and shows each one.
19737  *
19738  *  Fred [x]  Brian [x]  [Pick another |v]
19739  *
19740  *
19741  *  For this to work: it needs various extra information
19742  *    - normal combo problay has
19743  *      name, hiddenName
19744  *    + displayField, valueField
19745  *
19746  *    For our purpose...
19747  *
19748  *
19749  *   If we change from 'extends' to wrapping...
19750  *   
19751  *  
19752  *
19753  
19754  
19755  * @constructor
19756  * Create a new ComboBoxArray.
19757  * @param {Object} config Configuration options
19758  */
19759  
19760
19761 Roo.form.ComboBoxArray = function(config)
19762 {
19763     this.addEvents({
19764         /**
19765          * @event beforeremove
19766          * Fires before remove the value from the list
19767              * @param {Roo.form.ComboBoxArray} _self This combo box array
19768              * @param {Roo.form.ComboBoxArray.Item} item removed item
19769              */
19770         'beforeremove' : true,
19771         /**
19772          * @event remove
19773          * Fires when remove the value from the list
19774              * @param {Roo.form.ComboBoxArray} _self This combo box array
19775              * @param {Roo.form.ComboBoxArray.Item} item removed item
19776              */
19777         'remove' : true
19778         
19779         
19780     });
19781     
19782     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19783     
19784     this.items = new Roo.util.MixedCollection(false);
19785     
19786     // construct the child combo...
19787     
19788     
19789     
19790     
19791    
19792     
19793 }
19794
19795  
19796 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19797
19798     /**
19799      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19800      */
19801     
19802     lastData : false,
19803     
19804     // behavies liek a hiddne field
19805     inputType:      'hidden',
19806     /**
19807      * @cfg {Number} width The width of the box that displays the selected element
19808      */ 
19809     width:          300,
19810
19811     
19812     
19813     /**
19814      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19815      */
19816     name : false,
19817     /**
19818      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19819      */
19820     hiddenName : false,
19821     
19822     
19823     // private the array of items that are displayed..
19824     items  : false,
19825     // private - the hidden field el.
19826     hiddenEl : false,
19827     // private - the filed el..
19828     el : false,
19829     
19830     //validateValue : function() { return true; }, // all values are ok!
19831     //onAddClick: function() { },
19832     
19833     onRender : function(ct, position) 
19834     {
19835         
19836         // create the standard hidden element
19837         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19838         
19839         
19840         // give fake names to child combo;
19841         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19842         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19843         
19844         this.combo = Roo.factory(this.combo, Roo.form);
19845         this.combo.onRender(ct, position);
19846         if (typeof(this.combo.width) != 'undefined') {
19847             this.combo.onResize(this.combo.width,0);
19848         }
19849         
19850         this.combo.initEvents();
19851         
19852         // assigned so form know we need to do this..
19853         this.store          = this.combo.store;
19854         this.valueField     = this.combo.valueField;
19855         this.displayField   = this.combo.displayField ;
19856         
19857         
19858         this.combo.wrap.addClass('x-cbarray-grp');
19859         
19860         var cbwrap = this.combo.wrap.createChild(
19861             {tag: 'div', cls: 'x-cbarray-cb'},
19862             this.combo.el.dom
19863         );
19864         
19865              
19866         this.hiddenEl = this.combo.wrap.createChild({
19867             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19868         });
19869         this.el = this.combo.wrap.createChild({
19870             tag: 'input',  type:'hidden' , name: this.name, value : ''
19871         });
19872          //   this.el.dom.removeAttribute("name");
19873         
19874         
19875         this.outerWrap = this.combo.wrap;
19876         this.wrap = cbwrap;
19877         
19878         this.outerWrap.setWidth(this.width);
19879         this.outerWrap.dom.removeChild(this.el.dom);
19880         
19881         this.wrap.dom.appendChild(this.el.dom);
19882         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19883         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19884         
19885         this.combo.trigger.setStyle('position','relative');
19886         this.combo.trigger.setStyle('left', '0px');
19887         this.combo.trigger.setStyle('top', '2px');
19888         
19889         this.combo.el.setStyle('vertical-align', 'text-bottom');
19890         
19891         //this.trigger.setStyle('vertical-align', 'top');
19892         
19893         // this should use the code from combo really... on('add' ....)
19894         if (this.adder) {
19895             
19896         
19897             this.adder = this.outerWrap.createChild(
19898                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19899             var _t = this;
19900             this.adder.on('click', function(e) {
19901                 _t.fireEvent('adderclick', this, e);
19902             }, _t);
19903         }
19904         //var _t = this;
19905         //this.adder.on('click', this.onAddClick, _t);
19906         
19907         
19908         this.combo.on('select', function(cb, rec, ix) {
19909             this.addItem(rec.data);
19910             
19911             cb.setValue('');
19912             cb.el.dom.value = '';
19913             //cb.lastData = rec.data;
19914             // add to list
19915             
19916         }, this);
19917         
19918         
19919     },
19920     
19921     
19922     getName: function()
19923     {
19924         // returns hidden if it's set..
19925         if (!this.rendered) {return ''};
19926         return  this.hiddenName ? this.hiddenName : this.name;
19927         
19928     },
19929     
19930     
19931     onResize: function(w, h){
19932         
19933         return;
19934         // not sure if this is needed..
19935         //this.combo.onResize(w,h);
19936         
19937         if(typeof w != 'number'){
19938             // we do not handle it!?!?
19939             return;
19940         }
19941         var tw = this.combo.trigger.getWidth();
19942         tw += this.addicon ? this.addicon.getWidth() : 0;
19943         tw += this.editicon ? this.editicon.getWidth() : 0;
19944         var x = w - tw;
19945         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19946             
19947         this.combo.trigger.setStyle('left', '0px');
19948         
19949         if(this.list && this.listWidth === undefined){
19950             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19951             this.list.setWidth(lw);
19952             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19953         }
19954         
19955     
19956         
19957     },
19958     
19959     addItem: function(rec)
19960     {
19961         var valueField = this.combo.valueField;
19962         var displayField = this.combo.displayField;
19963         
19964         if (this.items.indexOfKey(rec[valueField]) > -1) {
19965             //console.log("GOT " + rec.data.id);
19966             return;
19967         }
19968         
19969         var x = new Roo.form.ComboBoxArray.Item({
19970             //id : rec[this.idField],
19971             data : rec,
19972             displayField : displayField ,
19973             tipField : displayField ,
19974             cb : this
19975         });
19976         // use the 
19977         this.items.add(rec[valueField],x);
19978         // add it before the element..
19979         this.updateHiddenEl();
19980         x.render(this.outerWrap, this.wrap.dom);
19981         // add the image handler..
19982     },
19983     
19984     updateHiddenEl : function()
19985     {
19986         this.validate();
19987         if (!this.hiddenEl) {
19988             return;
19989         }
19990         var ar = [];
19991         var idField = this.combo.valueField;
19992         
19993         this.items.each(function(f) {
19994             ar.push(f.data[idField]);
19995         });
19996         this.hiddenEl.dom.value = ar.join(',');
19997         this.validate();
19998     },
19999     
20000     reset : function()
20001     {
20002         this.items.clear();
20003         
20004         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20005            el.remove();
20006         });
20007         
20008         this.el.dom.value = '';
20009         if (this.hiddenEl) {
20010             this.hiddenEl.dom.value = '';
20011         }
20012         
20013     },
20014     getValue: function()
20015     {
20016         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20017     },
20018     setValue: function(v) // not a valid action - must use addItems..
20019     {
20020         
20021         this.reset();
20022          
20023         if (this.store.isLocal && (typeof(v) == 'string')) {
20024             // then we can use the store to find the values..
20025             // comma seperated at present.. this needs to allow JSON based encoding..
20026             this.hiddenEl.value  = v;
20027             var v_ar = [];
20028             Roo.each(v.split(','), function(k) {
20029                 Roo.log("CHECK " + this.valueField + ',' + k);
20030                 var li = this.store.query(this.valueField, k);
20031                 if (!li.length) {
20032                     return;
20033                 }
20034                 var add = {};
20035                 add[this.valueField] = k;
20036                 add[this.displayField] = li.item(0).data[this.displayField];
20037                 
20038                 this.addItem(add);
20039             }, this) 
20040              
20041         }
20042         if (typeof(v) == 'object' ) {
20043             // then let's assume it's an array of objects..
20044             Roo.each(v, function(l) {
20045                 this.addItem(l);
20046             }, this);
20047              
20048         }
20049         
20050         
20051     },
20052     setFromData: function(v)
20053     {
20054         // this recieves an object, if setValues is called.
20055         this.reset();
20056         this.el.dom.value = v[this.displayField];
20057         this.hiddenEl.dom.value = v[this.valueField];
20058         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20059             return;
20060         }
20061         var kv = v[this.valueField];
20062         var dv = v[this.displayField];
20063         kv = typeof(kv) != 'string' ? '' : kv;
20064         dv = typeof(dv) != 'string' ? '' : dv;
20065         
20066         
20067         var keys = kv.split(',');
20068         var display = dv.split(',');
20069         for (var i = 0 ; i < keys.length; i++) {
20070             
20071             add = {};
20072             add[this.valueField] = keys[i];
20073             add[this.displayField] = display[i];
20074             this.addItem(add);
20075         }
20076       
20077         
20078     },
20079     
20080     /**
20081      * Validates the combox array value
20082      * @return {Boolean} True if the value is valid, else false
20083      */
20084     validate : function(){
20085         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20086             this.clearInvalid();
20087             return true;
20088         }
20089         return false;
20090     },
20091     
20092     validateValue : function(value){
20093         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20094         
20095     },
20096     
20097     /*@
20098      * overide
20099      * 
20100      */
20101     isDirty : function() {
20102         if(this.disabled) {
20103             return false;
20104         }
20105         
20106         try {
20107             var d = Roo.decode(String(this.originalValue));
20108         } catch (e) {
20109             return String(this.getValue()) !== String(this.originalValue);
20110         }
20111         
20112         var originalValue = [];
20113         
20114         for (var i = 0; i < d.length; i++){
20115             originalValue.push(d[i][this.valueField]);
20116         }
20117         
20118         return String(this.getValue()) !== String(originalValue.join(','));
20119         
20120     }
20121     
20122 });
20123
20124
20125
20126 /**
20127  * @class Roo.form.ComboBoxArray.Item
20128  * @extends Roo.BoxComponent
20129  * A selected item in the list
20130  *  Fred [x]  Brian [x]  [Pick another |v]
20131  * 
20132  * @constructor
20133  * Create a new item.
20134  * @param {Object} config Configuration options
20135  */
20136  
20137 Roo.form.ComboBoxArray.Item = function(config) {
20138     config.id = Roo.id();
20139     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20140 }
20141
20142 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20143     data : {},
20144     cb: false,
20145     displayField : false,
20146     tipField : false,
20147     
20148     
20149     defaultAutoCreate : {
20150         tag: 'div',
20151         cls: 'x-cbarray-item',
20152         cn : [ 
20153             { tag: 'div' },
20154             {
20155                 tag: 'img',
20156                 width:16,
20157                 height : 16,
20158                 src : Roo.BLANK_IMAGE_URL ,
20159                 align: 'center'
20160             }
20161         ]
20162         
20163     },
20164     
20165  
20166     onRender : function(ct, position)
20167     {
20168         Roo.form.Field.superclass.onRender.call(this, ct, position);
20169         
20170         if(!this.el){
20171             var cfg = this.getAutoCreate();
20172             this.el = ct.createChild(cfg, position);
20173         }
20174         
20175         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20176         
20177         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20178             this.cb.renderer(this.data) :
20179             String.format('{0}',this.data[this.displayField]);
20180         
20181             
20182         this.el.child('div').dom.setAttribute('qtip',
20183                         String.format('{0}',this.data[this.tipField])
20184         );
20185         
20186         this.el.child('img').on('click', this.remove, this);
20187         
20188     },
20189    
20190     remove : function()
20191     {
20192         if(this.cb.disabled){
20193             return;
20194         }
20195         
20196         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20197             this.cb.items.remove(this);
20198             this.el.child('img').un('click', this.remove, this);
20199             this.el.remove();
20200             this.cb.updateHiddenEl();
20201
20202             this.cb.fireEvent('remove', this.cb, this);
20203         }
20204         
20205     }
20206 });/*
20207  * RooJS Library 1.1.1
20208  * Copyright(c) 2008-2011  Alan Knowles
20209  *
20210  * License - LGPL
20211  */
20212  
20213
20214 /**
20215  * @class Roo.form.ComboNested
20216  * @extends Roo.form.ComboBox
20217  * A combobox for that allows selection of nested items in a list,
20218  * eg.
20219  *
20220  *  Book
20221  *    -> red
20222  *    -> green
20223  *  Table
20224  *    -> square
20225  *      ->red
20226  *      ->green
20227  *    -> rectangle
20228  *      ->green
20229  *      
20230  * 
20231  * @constructor
20232  * Create a new ComboNested
20233  * @param {Object} config Configuration options
20234  */
20235 Roo.form.ComboNested = function(config){
20236     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20237     // should verify some data...
20238     // like
20239     // hiddenName = required..
20240     // displayField = required
20241     // valudField == required
20242     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20243     var _t = this;
20244     Roo.each(req, function(e) {
20245         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20246             throw "Roo.form.ComboNested : missing value for: " + e;
20247         }
20248     });
20249      
20250     
20251 };
20252
20253 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20254    
20255     /*
20256      * @config {Number} max Number of columns to show
20257      */
20258     
20259     maxColumns : 3,
20260    
20261     list : null, // the outermost div..
20262     innerLists : null, // the
20263     views : null,
20264     stores : null,
20265     // private
20266     onRender : function(ct, position)
20267     {
20268         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20269         
20270         if(this.hiddenName){
20271             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20272                     'before', true);
20273             this.hiddenField.value =
20274                 this.hiddenValue !== undefined ? this.hiddenValue :
20275                 this.value !== undefined ? this.value : '';
20276
20277             // prevent input submission
20278             this.el.dom.removeAttribute('name');
20279              
20280              
20281         }
20282         
20283         if(Roo.isGecko){
20284             this.el.dom.setAttribute('autocomplete', 'off');
20285         }
20286
20287         var cls = 'x-combo-list';
20288
20289         this.list = new Roo.Layer({
20290             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20291         });
20292
20293         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20294         this.list.setWidth(lw);
20295         this.list.swallowEvent('mousewheel');
20296         this.assetHeight = 0;
20297
20298         if(this.title){
20299             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20300             this.assetHeight += this.header.getHeight();
20301         }
20302         this.innerLists = [];
20303         this.views = [];
20304         this.stores = [];
20305         for (var i =0 ; i < this.maxColumns; i++) {
20306             this.onRenderList( cls, i);
20307         }
20308         
20309         // always needs footer, as we are going to have an 'OK' button.
20310         this.footer = this.list.createChild({cls:cls+'-ft'});
20311         this.pageTb = new Roo.Toolbar(this.footer);  
20312         var _this = this;
20313         this.pageTb.add(  {
20314             
20315             text: 'Done',
20316             handler: function()
20317             {
20318                 _this.collapse();
20319             }
20320         });
20321         
20322         if ( this.allowBlank && !this.disableClear) {
20323             
20324             this.pageTb.add(new Roo.Toolbar.Fill(), {
20325                 cls: 'x-btn-icon x-btn-clear',
20326                 text: '&#160;',
20327                 handler: function()
20328                 {
20329                     _this.collapse();
20330                     _this.clearValue();
20331                     _this.onSelect(false, -1);
20332                 }
20333             });
20334         }
20335         if (this.footer) {
20336             this.assetHeight += this.footer.getHeight();
20337         }
20338         
20339     },
20340     onRenderList : function (  cls, i)
20341     {
20342         
20343         var lw = Math.floor(
20344                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20345         );
20346         
20347         this.list.setWidth(lw); // default to '1'
20348
20349         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20350         //il.on('mouseover', this.onViewOver, this, { list:  i });
20351         //il.on('mousemove', this.onViewMove, this, { list:  i });
20352         il.setWidth(lw);
20353         il.setStyle({ 'overflow-x' : 'hidden'});
20354
20355         if(!this.tpl){
20356             this.tpl = new Roo.Template({
20357                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20358                 isEmpty: function (value, allValues) {
20359                     //Roo.log(value);
20360                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20361                     return dl ? 'has-children' : 'no-children'
20362                 }
20363             });
20364         }
20365         
20366         var store  = this.store;
20367         if (i > 0) {
20368             store  = new Roo.data.SimpleStore({
20369                 //fields : this.store.reader.meta.fields,
20370                 reader : this.store.reader,
20371                 data : [ ]
20372             });
20373         }
20374         this.stores[i]  = store;
20375                 
20376         
20377         
20378         var view = this.views[i] = new Roo.View(
20379             il,
20380             this.tpl,
20381             {
20382                 singleSelect:true,
20383                 store: store,
20384                 selectedClass: this.selectedClass
20385             }
20386         );
20387         view.getEl().setWidth(lw);
20388         view.getEl().setStyle({
20389             position: i < 1 ? 'relative' : 'absolute',
20390             top: 0,
20391             left: (i * lw ) + 'px',
20392             display : i > 0 ? 'none' : 'block'
20393         });
20394         view.on('selectionchange', this.onSelectChange, this, {list : i });
20395         view.on('dblclick', this.onDoubleClick, this, {list : i });
20396         //view.on('click', this.onViewClick, this, { list : i });
20397
20398         store.on('beforeload', this.onBeforeLoad, this);
20399         store.on('load',  this.onLoad, this, { list  : i});
20400         store.on('loadexception', this.onLoadException, this);
20401
20402         // hide the other vies..
20403         
20404         
20405         
20406     },
20407     onResize : function()  {},
20408     
20409     restrictHeight : function()
20410     {
20411         var mh = 0;
20412         Roo.each(this.innerLists, function(il,i) {
20413             var el = this.views[i].getEl();
20414             el.dom.style.height = '';
20415             var inner = el.dom;
20416             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20417             // only adjust heights on other ones..
20418             if (i < 1) {
20419                 
20420                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20421                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20422                 mh = Math.max(el.getHeight(), mh);
20423             }
20424             
20425             
20426         }, this);
20427         
20428         this.list.beginUpdate();
20429         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20430         this.list.alignTo(this.el, this.listAlign);
20431         this.list.endUpdate();
20432         
20433     },
20434      
20435     
20436     // -- store handlers..
20437     // private
20438     onBeforeLoad : function()
20439     {
20440         if(!this.hasFocus){
20441             return;
20442         }
20443         this.innerLists[0].update(this.loadingText ?
20444                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20445         this.restrictHeight();
20446         this.selectedIndex = -1;
20447     },
20448     // private
20449     onLoad : function(a,b,c,d)
20450     {
20451         
20452         if(!this.hasFocus){
20453             return;
20454         }
20455         
20456         if(this.store.getCount() > 0) {
20457             this.expand();
20458             this.restrictHeight();   
20459         } else {
20460             this.onEmptyResults();
20461         }
20462         /*
20463         this.stores[1].loadData([]);
20464         this.stores[2].loadData([]);
20465         this.views
20466         */    
20467     
20468         //this.el.focus();
20469     },
20470     
20471     
20472     // private
20473     onLoadException : function()
20474     {
20475         this.collapse();
20476         Roo.log(this.store.reader.jsonData);
20477         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20478             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20479         }
20480         
20481         
20482     } ,
20483      
20484      
20485
20486     onSelectChange : function (view, sels, opts )
20487     {
20488         var ix = view.getSelectedIndexes();
20489         
20490         
20491         if (opts.list > this.maxColumns - 2) {
20492              
20493             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20494             return;
20495         }
20496         
20497         if (!ix.length) {
20498             this.setFromData({});
20499             var str = this.stores[opts.list+1];
20500             str.removeAll();
20501             return;
20502         }
20503         
20504         var rec = view.store.getAt(ix[0]);
20505         this.setFromData(rec.data);
20506         
20507         var lw = Math.floor(
20508                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20509         );
20510         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20511         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20512         this.stores[opts.list+1].loadData( data );
20513         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20514         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20515         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20516         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20517     },
20518     onDoubleClick : function()
20519     {
20520         this.collapse(); //??
20521     },
20522     
20523      
20524     
20525     findRecord : function (prop,value)
20526     {
20527         return this.findRecordInStore(this.store, prop,value);
20528     },
20529     
20530      // private
20531     findRecordInStore : function(store, prop, value)
20532     {
20533         var cstore = new Roo.data.SimpleStore({
20534             //fields : this.store.reader.meta.fields, // we need array reader.. for
20535             reader : this.store.reader,
20536             data : [ ]
20537         });
20538         var _this = this;
20539         var record  = false;
20540         if(store.getCount() > 0){
20541            store.each(function(r){
20542                 if(r.data[prop] == value){
20543                     record = r;
20544                     return false;
20545                 }
20546                 if (r.data.cn && r.data.cn.length) {
20547                     cstore.loadData( r.data.cn);
20548                     var cret = _this.findRecordInStore(cstore, prop, value);
20549                     if (cret !== false) {
20550                         record = cret;
20551                         return false;
20552                     }
20553                 }
20554                 
20555                 return true;
20556             });
20557         }
20558         return record;
20559     }
20560     
20561     
20562     
20563     
20564 });/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574 /**
20575  * @class Roo.form.Checkbox
20576  * @extends Roo.form.Field
20577  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20578  * @constructor
20579  * Creates a new Checkbox
20580  * @param {Object} config Configuration options
20581  */
20582 Roo.form.Checkbox = function(config){
20583     Roo.form.Checkbox.superclass.constructor.call(this, config);
20584     this.addEvents({
20585         /**
20586          * @event check
20587          * Fires when the checkbox is checked or unchecked.
20588              * @param {Roo.form.Checkbox} this This checkbox
20589              * @param {Boolean} checked The new checked value
20590              */
20591         check : true
20592     });
20593 };
20594
20595 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20596     /**
20597      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20598      */
20599     focusClass : undefined,
20600     /**
20601      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20602      */
20603     fieldClass: "x-form-field",
20604     /**
20605      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20606      */
20607     checked: false,
20608     /**
20609      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20610      * {tag: "input", type: "checkbox", autocomplete: "off"})
20611      */
20612     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20613     /**
20614      * @cfg {String} boxLabel The text that appears beside the checkbox
20615      */
20616     boxLabel : "",
20617     /**
20618      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20619      */  
20620     inputValue : '1',
20621     /**
20622      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20623      */
20624      valueOff: '0', // value when not checked..
20625
20626     actionMode : 'viewEl', 
20627     //
20628     // private
20629     itemCls : 'x-menu-check-item x-form-item',
20630     groupClass : 'x-menu-group-item',
20631     inputType : 'hidden',
20632     
20633     
20634     inSetChecked: false, // check that we are not calling self...
20635     
20636     inputElement: false, // real input element?
20637     basedOn: false, // ????
20638     
20639     isFormField: true, // not sure where this is needed!!!!
20640
20641     onResize : function(){
20642         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20643         if(!this.boxLabel){
20644             this.el.alignTo(this.wrap, 'c-c');
20645         }
20646     },
20647
20648     initEvents : function(){
20649         Roo.form.Checkbox.superclass.initEvents.call(this);
20650         this.el.on("click", this.onClick,  this);
20651         this.el.on("change", this.onClick,  this);
20652     },
20653
20654
20655     getResizeEl : function(){
20656         return this.wrap;
20657     },
20658
20659     getPositionEl : function(){
20660         return this.wrap;
20661     },
20662
20663     // private
20664     onRender : function(ct, position){
20665         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20666         /*
20667         if(this.inputValue !== undefined){
20668             this.el.dom.value = this.inputValue;
20669         }
20670         */
20671         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20672         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20673         var viewEl = this.wrap.createChild({ 
20674             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20675         this.viewEl = viewEl;   
20676         this.wrap.on('click', this.onClick,  this); 
20677         
20678         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20679         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20680         
20681         
20682         
20683         if(this.boxLabel){
20684             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20685         //    viewEl.on('click', this.onClick,  this); 
20686         }
20687         //if(this.checked){
20688             this.setChecked(this.checked);
20689         //}else{
20690             //this.checked = this.el.dom;
20691         //}
20692
20693     },
20694
20695     // private
20696     initValue : Roo.emptyFn,
20697
20698     /**
20699      * Returns the checked state of the checkbox.
20700      * @return {Boolean} True if checked, else false
20701      */
20702     getValue : function(){
20703         if(this.el){
20704             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20705         }
20706         return this.valueOff;
20707         
20708     },
20709
20710         // private
20711     onClick : function(){ 
20712         if (this.disabled) {
20713             return;
20714         }
20715         this.setChecked(!this.checked);
20716
20717         //if(this.el.dom.checked != this.checked){
20718         //    this.setValue(this.el.dom.checked);
20719        // }
20720     },
20721
20722     /**
20723      * Sets the checked state of the checkbox.
20724      * On is always based on a string comparison between inputValue and the param.
20725      * @param {Boolean/String} value - the value to set 
20726      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20727      */
20728     setValue : function(v,suppressEvent){
20729         
20730         
20731         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20732         //if(this.el && this.el.dom){
20733         //    this.el.dom.checked = this.checked;
20734         //    this.el.dom.defaultChecked = this.checked;
20735         //}
20736         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20737         //this.fireEvent("check", this, this.checked);
20738     },
20739     // private..
20740     setChecked : function(state,suppressEvent)
20741     {
20742         if (this.inSetChecked) {
20743             this.checked = state;
20744             return;
20745         }
20746         
20747     
20748         if(this.wrap){
20749             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20750         }
20751         this.checked = state;
20752         if(suppressEvent !== true){
20753             this.fireEvent('check', this, state);
20754         }
20755         this.inSetChecked = true;
20756         this.el.dom.value = state ? this.inputValue : this.valueOff;
20757         this.inSetChecked = false;
20758         
20759     },
20760     // handle setting of hidden value by some other method!!?!?
20761     setFromHidden: function()
20762     {
20763         if(!this.el){
20764             return;
20765         }
20766         //console.log("SET FROM HIDDEN");
20767         //alert('setFrom hidden');
20768         this.setValue(this.el.dom.value);
20769     },
20770     
20771     onDestroy : function()
20772     {
20773         if(this.viewEl){
20774             Roo.get(this.viewEl).remove();
20775         }
20776          
20777         Roo.form.Checkbox.superclass.onDestroy.call(this);
20778     },
20779     
20780     setBoxLabel : function(str)
20781     {
20782         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20783     }
20784
20785 });/*
20786  * Based on:
20787  * Ext JS Library 1.1.1
20788  * Copyright(c) 2006-2007, Ext JS, LLC.
20789  *
20790  * Originally Released Under LGPL - original licence link has changed is not relivant.
20791  *
20792  * Fork - LGPL
20793  * <script type="text/javascript">
20794  */
20795  
20796 /**
20797  * @class Roo.form.Radio
20798  * @extends Roo.form.Checkbox
20799  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20800  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20801  * @constructor
20802  * Creates a new Radio
20803  * @param {Object} config Configuration options
20804  */
20805 Roo.form.Radio = function(){
20806     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20807 };
20808 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20809     inputType: 'radio',
20810
20811     /**
20812      * If this radio is part of a group, it will return the selected value
20813      * @return {String}
20814      */
20815     getGroupValue : function(){
20816         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20817     },
20818     
20819     
20820     onRender : function(ct, position){
20821         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20822         
20823         if(this.inputValue !== undefined){
20824             this.el.dom.value = this.inputValue;
20825         }
20826          
20827         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20828         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20829         //var viewEl = this.wrap.createChild({ 
20830         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20831         //this.viewEl = viewEl;   
20832         //this.wrap.on('click', this.onClick,  this); 
20833         
20834         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20835         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20836         
20837         
20838         
20839         if(this.boxLabel){
20840             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20841         //    viewEl.on('click', this.onClick,  this); 
20842         }
20843          if(this.checked){
20844             this.el.dom.checked =   'checked' ;
20845         }
20846          
20847     } 
20848     
20849     
20850 });//<script type="text/javascript">
20851
20852 /*
20853  * Based  Ext JS Library 1.1.1
20854  * Copyright(c) 2006-2007, Ext JS, LLC.
20855  * LGPL
20856  *
20857  */
20858  
20859 /**
20860  * @class Roo.HtmlEditorCore
20861  * @extends Roo.Component
20862  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20863  *
20864  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20865  */
20866
20867 Roo.HtmlEditorCore = function(config){
20868     
20869     
20870     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20871     
20872     
20873     this.addEvents({
20874         /**
20875          * @event initialize
20876          * Fires when the editor is fully initialized (including the iframe)
20877          * @param {Roo.HtmlEditorCore} this
20878          */
20879         initialize: true,
20880         /**
20881          * @event activate
20882          * Fires when the editor is first receives the focus. Any insertion must wait
20883          * until after this event.
20884          * @param {Roo.HtmlEditorCore} this
20885          */
20886         activate: true,
20887          /**
20888          * @event beforesync
20889          * Fires before the textarea is updated with content from the editor iframe. Return false
20890          * to cancel the sync.
20891          * @param {Roo.HtmlEditorCore} this
20892          * @param {String} html
20893          */
20894         beforesync: true,
20895          /**
20896          * @event beforepush
20897          * Fires before the iframe editor is updated with content from the textarea. Return false
20898          * to cancel the push.
20899          * @param {Roo.HtmlEditorCore} this
20900          * @param {String} html
20901          */
20902         beforepush: true,
20903          /**
20904          * @event sync
20905          * Fires when the textarea is updated with content from the editor iframe.
20906          * @param {Roo.HtmlEditorCore} this
20907          * @param {String} html
20908          */
20909         sync: true,
20910          /**
20911          * @event push
20912          * Fires when the iframe editor is updated with content from the textarea.
20913          * @param {Roo.HtmlEditorCore} this
20914          * @param {String} html
20915          */
20916         push: true,
20917         
20918         /**
20919          * @event editorevent
20920          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20921          * @param {Roo.HtmlEditorCore} this
20922          */
20923         editorevent: true
20924         
20925     });
20926     
20927     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20928     
20929     // defaults : white / black...
20930     this.applyBlacklists();
20931     
20932     
20933     
20934 };
20935
20936
20937 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20938
20939
20940      /**
20941      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20942      */
20943     
20944     owner : false,
20945     
20946      /**
20947      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20948      *                        Roo.resizable.
20949      */
20950     resizable : false,
20951      /**
20952      * @cfg {Number} height (in pixels)
20953      */   
20954     height: 300,
20955    /**
20956      * @cfg {Number} width (in pixels)
20957      */   
20958     width: 500,
20959     
20960     /**
20961      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20962      * 
20963      */
20964     stylesheets: false,
20965     
20966     // id of frame..
20967     frameId: false,
20968     
20969     // private properties
20970     validationEvent : false,
20971     deferHeight: true,
20972     initialized : false,
20973     activated : false,
20974     sourceEditMode : false,
20975     onFocus : Roo.emptyFn,
20976     iframePad:3,
20977     hideMode:'offsets',
20978     
20979     clearUp: true,
20980     
20981     // blacklist + whitelisted elements..
20982     black: false,
20983     white: false,
20984      
20985     bodyCls : '',
20986
20987     /**
20988      * Protected method that will not generally be called directly. It
20989      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20990      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20991      */
20992     getDocMarkup : function(){
20993         // body styles..
20994         var st = '';
20995         
20996         // inherit styels from page...?? 
20997         if (this.stylesheets === false) {
20998             
20999             Roo.get(document.head).select('style').each(function(node) {
21000                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21001             });
21002             
21003             Roo.get(document.head).select('link').each(function(node) { 
21004                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21005             });
21006             
21007         } else if (!this.stylesheets.length) {
21008                 // simple..
21009                 st = '<style type="text/css">' +
21010                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21011                    '</style>';
21012         } else { 
21013             st = '<style type="text/css">' +
21014                     this.stylesheets +
21015                 '</style>';
21016         }
21017         
21018         st +=  '<style type="text/css">' +
21019             'IMG { cursor: pointer } ' +
21020         '</style>';
21021
21022         var cls = 'roo-htmleditor-body';
21023         
21024         if(this.bodyCls.length){
21025             cls += ' ' + this.bodyCls;
21026         }
21027         
21028         return '<html><head>' + st  +
21029             //<style type="text/css">' +
21030             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21031             //'</style>' +
21032             ' </head><body class="' +  cls + '"></body></html>';
21033     },
21034
21035     // private
21036     onRender : function(ct, position)
21037     {
21038         var _t = this;
21039         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21040         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21041         
21042         
21043         this.el.dom.style.border = '0 none';
21044         this.el.dom.setAttribute('tabIndex', -1);
21045         this.el.addClass('x-hidden hide');
21046         
21047         
21048         
21049         if(Roo.isIE){ // fix IE 1px bogus margin
21050             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21051         }
21052        
21053         
21054         this.frameId = Roo.id();
21055         
21056          
21057         
21058         var iframe = this.owner.wrap.createChild({
21059             tag: 'iframe',
21060             cls: 'form-control', // bootstrap..
21061             id: this.frameId,
21062             name: this.frameId,
21063             frameBorder : 'no',
21064             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21065         }, this.el
21066         );
21067         
21068         
21069         this.iframe = iframe.dom;
21070
21071          this.assignDocWin();
21072         
21073         this.doc.designMode = 'on';
21074        
21075         this.doc.open();
21076         this.doc.write(this.getDocMarkup());
21077         this.doc.close();
21078
21079         
21080         var task = { // must defer to wait for browser to be ready
21081             run : function(){
21082                 //console.log("run task?" + this.doc.readyState);
21083                 this.assignDocWin();
21084                 if(this.doc.body || this.doc.readyState == 'complete'){
21085                     try {
21086                         this.doc.designMode="on";
21087                     } catch (e) {
21088                         return;
21089                     }
21090                     Roo.TaskMgr.stop(task);
21091                     this.initEditor.defer(10, this);
21092                 }
21093             },
21094             interval : 10,
21095             duration: 10000,
21096             scope: this
21097         };
21098         Roo.TaskMgr.start(task);
21099
21100     },
21101
21102     // private
21103     onResize : function(w, h)
21104     {
21105          Roo.log('resize: ' +w + ',' + h );
21106         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21107         if(!this.iframe){
21108             return;
21109         }
21110         if(typeof w == 'number'){
21111             
21112             this.iframe.style.width = w + 'px';
21113         }
21114         if(typeof h == 'number'){
21115             
21116             this.iframe.style.height = h + 'px';
21117             if(this.doc){
21118                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21119             }
21120         }
21121         
21122     },
21123
21124     /**
21125      * Toggles the editor between standard and source edit mode.
21126      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21127      */
21128     toggleSourceEdit : function(sourceEditMode){
21129         
21130         this.sourceEditMode = sourceEditMode === true;
21131         
21132         if(this.sourceEditMode){
21133  
21134             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21135             
21136         }else{
21137             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21138             //this.iframe.className = '';
21139             this.deferFocus();
21140         }
21141         //this.setSize(this.owner.wrap.getSize());
21142         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21143     },
21144
21145     
21146   
21147
21148     /**
21149      * Protected method that will not generally be called directly. If you need/want
21150      * custom HTML cleanup, this is the method you should override.
21151      * @param {String} html The HTML to be cleaned
21152      * return {String} The cleaned HTML
21153      */
21154     cleanHtml : function(html){
21155         html = String(html);
21156         if(html.length > 5){
21157             if(Roo.isSafari){ // strip safari nonsense
21158                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21159             }
21160         }
21161         if(html == '&nbsp;'){
21162             html = '';
21163         }
21164         return html;
21165     },
21166
21167     /**
21168      * HTML Editor -> Textarea
21169      * Protected method that will not generally be called directly. Syncs the contents
21170      * of the editor iframe with the textarea.
21171      */
21172     syncValue : function(){
21173         if(this.initialized){
21174             var bd = (this.doc.body || this.doc.documentElement);
21175             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21176             var html = bd.innerHTML;
21177             if(Roo.isSafari){
21178                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21179                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21180                 if(m && m[1]){
21181                     html = '<div style="'+m[0]+'">' + html + '</div>';
21182                 }
21183             }
21184             html = this.cleanHtml(html);
21185             // fix up the special chars.. normaly like back quotes in word...
21186             // however we do not want to do this with chinese..
21187             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21188                 
21189                 var cc = match.charCodeAt();
21190
21191                 // Get the character value, handling surrogate pairs
21192                 if (match.length == 2) {
21193                     // It's a surrogate pair, calculate the Unicode code point
21194                     var high = match.charCodeAt(0) - 0xD800;
21195                     var low  = match.charCodeAt(1) - 0xDC00;
21196                     cc = (high * 0x400) + low + 0x10000;
21197                 }  else if (
21198                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21199                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21200                     (cc >= 0xf900 && cc < 0xfb00 )
21201                 ) {
21202                         return match;
21203                 }  
21204          
21205                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21206                 return "&#" + cc + ";";
21207                 
21208                 
21209             });
21210             
21211             
21212              
21213             if(this.owner.fireEvent('beforesync', this, html) !== false){
21214                 this.el.dom.value = html;
21215                 this.owner.fireEvent('sync', this, html);
21216             }
21217         }
21218     },
21219
21220     /**
21221      * Protected method that will not generally be called directly. Pushes the value of the textarea
21222      * into the iframe editor.
21223      */
21224     pushValue : function(){
21225         if(this.initialized){
21226             var v = this.el.dom.value.trim();
21227             
21228 //            if(v.length < 1){
21229 //                v = '&#160;';
21230 //            }
21231             
21232             if(this.owner.fireEvent('beforepush', this, v) !== false){
21233                 var d = (this.doc.body || this.doc.documentElement);
21234                 d.innerHTML = v;
21235                 this.cleanUpPaste();
21236                 this.el.dom.value = d.innerHTML;
21237                 this.owner.fireEvent('push', this, v);
21238             }
21239         }
21240     },
21241
21242     // private
21243     deferFocus : function(){
21244         this.focus.defer(10, this);
21245     },
21246
21247     // doc'ed in Field
21248     focus : function(){
21249         if(this.win && !this.sourceEditMode){
21250             this.win.focus();
21251         }else{
21252             this.el.focus();
21253         }
21254     },
21255     
21256     assignDocWin: function()
21257     {
21258         var iframe = this.iframe;
21259         
21260          if(Roo.isIE){
21261             this.doc = iframe.contentWindow.document;
21262             this.win = iframe.contentWindow;
21263         } else {
21264 //            if (!Roo.get(this.frameId)) {
21265 //                return;
21266 //            }
21267 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21268 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21269             
21270             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21271                 return;
21272             }
21273             
21274             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21275             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21276         }
21277     },
21278     
21279     // private
21280     initEditor : function(){
21281         //console.log("INIT EDITOR");
21282         this.assignDocWin();
21283         
21284         
21285         
21286         this.doc.designMode="on";
21287         this.doc.open();
21288         this.doc.write(this.getDocMarkup());
21289         this.doc.close();
21290         
21291         var dbody = (this.doc.body || this.doc.documentElement);
21292         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21293         // this copies styles from the containing element into thsi one..
21294         // not sure why we need all of this..
21295         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21296         
21297         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21298         //ss['background-attachment'] = 'fixed'; // w3c
21299         dbody.bgProperties = 'fixed'; // ie
21300         //Roo.DomHelper.applyStyles(dbody, ss);
21301         Roo.EventManager.on(this.doc, {
21302             //'mousedown': this.onEditorEvent,
21303             'mouseup': this.onEditorEvent,
21304             'dblclick': this.onEditorEvent,
21305             'click': this.onEditorEvent,
21306             'keyup': this.onEditorEvent,
21307             buffer:100,
21308             scope: this
21309         });
21310         if(Roo.isGecko){
21311             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21312         }
21313         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21314             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21315         }
21316         this.initialized = true;
21317
21318         this.owner.fireEvent('initialize', this);
21319         this.pushValue();
21320     },
21321
21322     // private
21323     onDestroy : function(){
21324         
21325         
21326         
21327         if(this.rendered){
21328             
21329             //for (var i =0; i < this.toolbars.length;i++) {
21330             //    // fixme - ask toolbars for heights?
21331             //    this.toolbars[i].onDestroy();
21332            // }
21333             
21334             //this.wrap.dom.innerHTML = '';
21335             //this.wrap.remove();
21336         }
21337     },
21338
21339     // private
21340     onFirstFocus : function(){
21341         
21342         this.assignDocWin();
21343         
21344         
21345         this.activated = true;
21346          
21347     
21348         if(Roo.isGecko){ // prevent silly gecko errors
21349             this.win.focus();
21350             var s = this.win.getSelection();
21351             if(!s.focusNode || s.focusNode.nodeType != 3){
21352                 var r = s.getRangeAt(0);
21353                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21354                 r.collapse(true);
21355                 this.deferFocus();
21356             }
21357             try{
21358                 this.execCmd('useCSS', true);
21359                 this.execCmd('styleWithCSS', false);
21360             }catch(e){}
21361         }
21362         this.owner.fireEvent('activate', this);
21363     },
21364
21365     // private
21366     adjustFont: function(btn){
21367         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21368         //if(Roo.isSafari){ // safari
21369         //    adjust *= 2;
21370        // }
21371         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21372         if(Roo.isSafari){ // safari
21373             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21374             v =  (v < 10) ? 10 : v;
21375             v =  (v > 48) ? 48 : v;
21376             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21377             
21378         }
21379         
21380         
21381         v = Math.max(1, v+adjust);
21382         
21383         this.execCmd('FontSize', v  );
21384     },
21385
21386     onEditorEvent : function(e)
21387     {
21388         this.owner.fireEvent('editorevent', this, e);
21389       //  this.updateToolbar();
21390         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21391     },
21392
21393     insertTag : function(tg)
21394     {
21395         // could be a bit smarter... -> wrap the current selected tRoo..
21396         if (tg.toLowerCase() == 'span' ||
21397             tg.toLowerCase() == 'code' ||
21398             tg.toLowerCase() == 'sup' ||
21399             tg.toLowerCase() == 'sub' 
21400             ) {
21401             
21402             range = this.createRange(this.getSelection());
21403             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21404             wrappingNode.appendChild(range.extractContents());
21405             range.insertNode(wrappingNode);
21406
21407             return;
21408             
21409             
21410             
21411         }
21412         this.execCmd("formatblock",   tg);
21413         
21414     },
21415     
21416     insertText : function(txt)
21417     {
21418         
21419         
21420         var range = this.createRange();
21421         range.deleteContents();
21422                //alert(Sender.getAttribute('label'));
21423                
21424         range.insertNode(this.doc.createTextNode(txt));
21425     } ,
21426     
21427      
21428
21429     /**
21430      * Executes a Midas editor command on the editor document and performs necessary focus and
21431      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21432      * @param {String} cmd The Midas command
21433      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21434      */
21435     relayCmd : function(cmd, value){
21436         this.win.focus();
21437         this.execCmd(cmd, value);
21438         this.owner.fireEvent('editorevent', this);
21439         //this.updateToolbar();
21440         this.owner.deferFocus();
21441     },
21442
21443     /**
21444      * Executes a Midas editor command directly on the editor document.
21445      * For visual commands, you should use {@link #relayCmd} instead.
21446      * <b>This should only be called after the editor is initialized.</b>
21447      * @param {String} cmd The Midas command
21448      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21449      */
21450     execCmd : function(cmd, value){
21451         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21452         this.syncValue();
21453     },
21454  
21455  
21456    
21457     /**
21458      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21459      * to insert tRoo.
21460      * @param {String} text | dom node.. 
21461      */
21462     insertAtCursor : function(text)
21463     {
21464         
21465         if(!this.activated){
21466             return;
21467         }
21468         /*
21469         if(Roo.isIE){
21470             this.win.focus();
21471             var r = this.doc.selection.createRange();
21472             if(r){
21473                 r.collapse(true);
21474                 r.pasteHTML(text);
21475                 this.syncValue();
21476                 this.deferFocus();
21477             
21478             }
21479             return;
21480         }
21481         */
21482         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21483             this.win.focus();
21484             
21485             
21486             // from jquery ui (MIT licenced)
21487             var range, node;
21488             var win = this.win;
21489             
21490             if (win.getSelection && win.getSelection().getRangeAt) {
21491                 range = win.getSelection().getRangeAt(0);
21492                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21493                 range.insertNode(node);
21494             } else if (win.document.selection && win.document.selection.createRange) {
21495                 // no firefox support
21496                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21497                 win.document.selection.createRange().pasteHTML(txt);
21498             } else {
21499                 // no firefox support
21500                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21501                 this.execCmd('InsertHTML', txt);
21502             } 
21503             
21504             this.syncValue();
21505             
21506             this.deferFocus();
21507         }
21508     },
21509  // private
21510     mozKeyPress : function(e){
21511         if(e.ctrlKey){
21512             var c = e.getCharCode(), cmd;
21513           
21514             if(c > 0){
21515                 c = String.fromCharCode(c).toLowerCase();
21516                 switch(c){
21517                     case 'b':
21518                         cmd = 'bold';
21519                         break;
21520                     case 'i':
21521                         cmd = 'italic';
21522                         break;
21523                     
21524                     case 'u':
21525                         cmd = 'underline';
21526                         break;
21527                     
21528                     case 'v':
21529                         this.cleanUpPaste.defer(100, this);
21530                         return;
21531                         
21532                 }
21533                 if(cmd){
21534                     this.win.focus();
21535                     this.execCmd(cmd);
21536                     this.deferFocus();
21537                     e.preventDefault();
21538                 }
21539                 
21540             }
21541         }
21542     },
21543
21544     // private
21545     fixKeys : function(){ // load time branching for fastest keydown performance
21546         if(Roo.isIE){
21547             return function(e){
21548                 var k = e.getKey(), r;
21549                 if(k == e.TAB){
21550                     e.stopEvent();
21551                     r = this.doc.selection.createRange();
21552                     if(r){
21553                         r.collapse(true);
21554                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21555                         this.deferFocus();
21556                     }
21557                     return;
21558                 }
21559                 
21560                 if(k == e.ENTER){
21561                     r = this.doc.selection.createRange();
21562                     if(r){
21563                         var target = r.parentElement();
21564                         if(!target || target.tagName.toLowerCase() != 'li'){
21565                             e.stopEvent();
21566                             r.pasteHTML('<br />');
21567                             r.collapse(false);
21568                             r.select();
21569                         }
21570                     }
21571                 }
21572                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21573                     this.cleanUpPaste.defer(100, this);
21574                     return;
21575                 }
21576                 
21577                 
21578             };
21579         }else if(Roo.isOpera){
21580             return function(e){
21581                 var k = e.getKey();
21582                 if(k == e.TAB){
21583                     e.stopEvent();
21584                     this.win.focus();
21585                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21586                     this.deferFocus();
21587                 }
21588                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21589                     this.cleanUpPaste.defer(100, this);
21590                     return;
21591                 }
21592                 
21593             };
21594         }else if(Roo.isSafari){
21595             return function(e){
21596                 var k = e.getKey();
21597                 
21598                 if(k == e.TAB){
21599                     e.stopEvent();
21600                     this.execCmd('InsertText','\t');
21601                     this.deferFocus();
21602                     return;
21603                 }
21604                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21605                     this.cleanUpPaste.defer(100, this);
21606                     return;
21607                 }
21608                 
21609              };
21610         }
21611     }(),
21612     
21613     getAllAncestors: function()
21614     {
21615         var p = this.getSelectedNode();
21616         var a = [];
21617         if (!p) {
21618             a.push(p); // push blank onto stack..
21619             p = this.getParentElement();
21620         }
21621         
21622         
21623         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21624             a.push(p);
21625             p = p.parentNode;
21626         }
21627         a.push(this.doc.body);
21628         return a;
21629     },
21630     lastSel : false,
21631     lastSelNode : false,
21632     
21633     
21634     getSelection : function() 
21635     {
21636         this.assignDocWin();
21637         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21638     },
21639     
21640     getSelectedNode: function() 
21641     {
21642         // this may only work on Gecko!!!
21643         
21644         // should we cache this!!!!
21645         
21646         
21647         
21648          
21649         var range = this.createRange(this.getSelection()).cloneRange();
21650         
21651         if (Roo.isIE) {
21652             var parent = range.parentElement();
21653             while (true) {
21654                 var testRange = range.duplicate();
21655                 testRange.moveToElementText(parent);
21656                 if (testRange.inRange(range)) {
21657                     break;
21658                 }
21659                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21660                     break;
21661                 }
21662                 parent = parent.parentElement;
21663             }
21664             return parent;
21665         }
21666         
21667         // is ancestor a text element.
21668         var ac =  range.commonAncestorContainer;
21669         if (ac.nodeType == 3) {
21670             ac = ac.parentNode;
21671         }
21672         
21673         var ar = ac.childNodes;
21674          
21675         var nodes = [];
21676         var other_nodes = [];
21677         var has_other_nodes = false;
21678         for (var i=0;i<ar.length;i++) {
21679             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21680                 continue;
21681             }
21682             // fullly contained node.
21683             
21684             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21685                 nodes.push(ar[i]);
21686                 continue;
21687             }
21688             
21689             // probably selected..
21690             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21691                 other_nodes.push(ar[i]);
21692                 continue;
21693             }
21694             // outer..
21695             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21696                 continue;
21697             }
21698             
21699             
21700             has_other_nodes = true;
21701         }
21702         if (!nodes.length && other_nodes.length) {
21703             nodes= other_nodes;
21704         }
21705         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21706             return false;
21707         }
21708         
21709         return nodes[0];
21710     },
21711     createRange: function(sel)
21712     {
21713         // this has strange effects when using with 
21714         // top toolbar - not sure if it's a great idea.
21715         //this.editor.contentWindow.focus();
21716         if (typeof sel != "undefined") {
21717             try {
21718                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21719             } catch(e) {
21720                 return this.doc.createRange();
21721             }
21722         } else {
21723             return this.doc.createRange();
21724         }
21725     },
21726     getParentElement: function()
21727     {
21728         
21729         this.assignDocWin();
21730         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21731         
21732         var range = this.createRange(sel);
21733          
21734         try {
21735             var p = range.commonAncestorContainer;
21736             while (p.nodeType == 3) { // text node
21737                 p = p.parentNode;
21738             }
21739             return p;
21740         } catch (e) {
21741             return null;
21742         }
21743     
21744     },
21745     /***
21746      *
21747      * Range intersection.. the hard stuff...
21748      *  '-1' = before
21749      *  '0' = hits..
21750      *  '1' = after.
21751      *         [ -- selected range --- ]
21752      *   [fail]                        [fail]
21753      *
21754      *    basically..
21755      *      if end is before start or  hits it. fail.
21756      *      if start is after end or hits it fail.
21757      *
21758      *   if either hits (but other is outside. - then it's not 
21759      *   
21760      *    
21761      **/
21762     
21763     
21764     // @see http://www.thismuchiknow.co.uk/?p=64.
21765     rangeIntersectsNode : function(range, node)
21766     {
21767         var nodeRange = node.ownerDocument.createRange();
21768         try {
21769             nodeRange.selectNode(node);
21770         } catch (e) {
21771             nodeRange.selectNodeContents(node);
21772         }
21773     
21774         var rangeStartRange = range.cloneRange();
21775         rangeStartRange.collapse(true);
21776     
21777         var rangeEndRange = range.cloneRange();
21778         rangeEndRange.collapse(false);
21779     
21780         var nodeStartRange = nodeRange.cloneRange();
21781         nodeStartRange.collapse(true);
21782     
21783         var nodeEndRange = nodeRange.cloneRange();
21784         nodeEndRange.collapse(false);
21785     
21786         return rangeStartRange.compareBoundaryPoints(
21787                  Range.START_TO_START, nodeEndRange) == -1 &&
21788                rangeEndRange.compareBoundaryPoints(
21789                  Range.START_TO_START, nodeStartRange) == 1;
21790         
21791          
21792     },
21793     rangeCompareNode : function(range, node)
21794     {
21795         var nodeRange = node.ownerDocument.createRange();
21796         try {
21797             nodeRange.selectNode(node);
21798         } catch (e) {
21799             nodeRange.selectNodeContents(node);
21800         }
21801         
21802         
21803         range.collapse(true);
21804     
21805         nodeRange.collapse(true);
21806      
21807         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21808         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21809          
21810         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21811         
21812         var nodeIsBefore   =  ss == 1;
21813         var nodeIsAfter    = ee == -1;
21814         
21815         if (nodeIsBefore && nodeIsAfter) {
21816             return 0; // outer
21817         }
21818         if (!nodeIsBefore && nodeIsAfter) {
21819             return 1; //right trailed.
21820         }
21821         
21822         if (nodeIsBefore && !nodeIsAfter) {
21823             return 2;  // left trailed.
21824         }
21825         // fully contined.
21826         return 3;
21827     },
21828
21829     // private? - in a new class?
21830     cleanUpPaste :  function()
21831     {
21832         // cleans up the whole document..
21833         Roo.log('cleanuppaste');
21834         
21835         this.cleanUpChildren(this.doc.body);
21836         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21837         if (clean != this.doc.body.innerHTML) {
21838             this.doc.body.innerHTML = clean;
21839         }
21840         
21841     },
21842     
21843     cleanWordChars : function(input) {// change the chars to hex code
21844         var he = Roo.HtmlEditorCore;
21845         
21846         var output = input;
21847         Roo.each(he.swapCodes, function(sw) { 
21848             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21849             
21850             output = output.replace(swapper, sw[1]);
21851         });
21852         
21853         return output;
21854     },
21855     
21856     
21857     cleanUpChildren : function (n)
21858     {
21859         if (!n.childNodes.length) {
21860             return;
21861         }
21862         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21863            this.cleanUpChild(n.childNodes[i]);
21864         }
21865     },
21866     
21867     
21868         
21869     
21870     cleanUpChild : function (node)
21871     {
21872         var ed = this;
21873         //console.log(node);
21874         if (node.nodeName == "#text") {
21875             // clean up silly Windows -- stuff?
21876             return; 
21877         }
21878         if (node.nodeName == "#comment") {
21879             node.parentNode.removeChild(node);
21880             // clean up silly Windows -- stuff?
21881             return; 
21882         }
21883         var lcname = node.tagName.toLowerCase();
21884         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21885         // whitelist of tags..
21886         
21887         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21888             // remove node.
21889             node.parentNode.removeChild(node);
21890             return;
21891             
21892         }
21893         
21894         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21895         
21896         // spans with no attributes - just remove them..
21897         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21898             remove_keep_children = true;
21899         }
21900         
21901         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21902         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21903         
21904         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21905         //    remove_keep_children = true;
21906         //}
21907         
21908         if (remove_keep_children) {
21909             this.cleanUpChildren(node);
21910             // inserts everything just before this node...
21911             while (node.childNodes.length) {
21912                 var cn = node.childNodes[0];
21913                 node.removeChild(cn);
21914                 node.parentNode.insertBefore(cn, node);
21915             }
21916             node.parentNode.removeChild(node);
21917             return;
21918         }
21919         
21920         if (!node.attributes || !node.attributes.length) {
21921             
21922           
21923             
21924             
21925             this.cleanUpChildren(node);
21926             return;
21927         }
21928         
21929         function cleanAttr(n,v)
21930         {
21931             
21932             if (v.match(/^\./) || v.match(/^\//)) {
21933                 return;
21934             }
21935             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21936                 return;
21937             }
21938             if (v.match(/^#/)) {
21939                 return;
21940             }
21941 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21942             node.removeAttribute(n);
21943             
21944         }
21945         
21946         var cwhite = this.cwhite;
21947         var cblack = this.cblack;
21948             
21949         function cleanStyle(n,v)
21950         {
21951             if (v.match(/expression/)) { //XSS?? should we even bother..
21952                 node.removeAttribute(n);
21953                 return;
21954             }
21955             
21956             var parts = v.split(/;/);
21957             var clean = [];
21958             
21959             Roo.each(parts, function(p) {
21960                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21961                 if (!p.length) {
21962                     return true;
21963                 }
21964                 var l = p.split(':').shift().replace(/\s+/g,'');
21965                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21966                 
21967                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21968 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21969                     //node.removeAttribute(n);
21970                     return true;
21971                 }
21972                 //Roo.log()
21973                 // only allow 'c whitelisted system attributes'
21974                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21975 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21976                     //node.removeAttribute(n);
21977                     return true;
21978                 }
21979                 
21980                 
21981                  
21982                 
21983                 clean.push(p);
21984                 return true;
21985             });
21986             if (clean.length) { 
21987                 node.setAttribute(n, clean.join(';'));
21988             } else {
21989                 node.removeAttribute(n);
21990             }
21991             
21992         }
21993         
21994         
21995         for (var i = node.attributes.length-1; i > -1 ; i--) {
21996             var a = node.attributes[i];
21997             //console.log(a);
21998             
21999             if (a.name.toLowerCase().substr(0,2)=='on')  {
22000                 node.removeAttribute(a.name);
22001                 continue;
22002             }
22003             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22004                 node.removeAttribute(a.name);
22005                 continue;
22006             }
22007             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22008                 cleanAttr(a.name,a.value); // fixme..
22009                 continue;
22010             }
22011             if (a.name == 'style') {
22012                 cleanStyle(a.name,a.value);
22013                 continue;
22014             }
22015             /// clean up MS crap..
22016             // tecnically this should be a list of valid class'es..
22017             
22018             
22019             if (a.name == 'class') {
22020                 if (a.value.match(/^Mso/)) {
22021                     node.removeAttribute('class');
22022                 }
22023                 
22024                 if (a.value.match(/^body$/)) {
22025                     node.removeAttribute('class');
22026                 }
22027                 continue;
22028             }
22029             
22030             // style cleanup!?
22031             // class cleanup?
22032             
22033         }
22034         
22035         
22036         this.cleanUpChildren(node);
22037         
22038         
22039     },
22040     
22041     /**
22042      * Clean up MS wordisms...
22043      */
22044     cleanWord : function(node)
22045     {
22046         if (!node) {
22047             this.cleanWord(this.doc.body);
22048             return;
22049         }
22050         
22051         if(
22052                 node.nodeName == 'SPAN' &&
22053                 !node.hasAttributes() &&
22054                 node.childNodes.length == 1 &&
22055                 node.firstChild.nodeName == "#text"  
22056         ) {
22057             var textNode = node.firstChild;
22058             node.removeChild(textNode);
22059             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22060                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22061             }
22062             node.parentNode.insertBefore(textNode, node);
22063             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22064                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22065             }
22066             node.parentNode.removeChild(node);
22067         }
22068         
22069         if (node.nodeName == "#text") {
22070             // clean up silly Windows -- stuff?
22071             return; 
22072         }
22073         if (node.nodeName == "#comment") {
22074             node.parentNode.removeChild(node);
22075             // clean up silly Windows -- stuff?
22076             return; 
22077         }
22078         
22079         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22080             node.parentNode.removeChild(node);
22081             return;
22082         }
22083         //Roo.log(node.tagName);
22084         // remove - but keep children..
22085         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22086             //Roo.log('-- removed');
22087             while (node.childNodes.length) {
22088                 var cn = node.childNodes[0];
22089                 node.removeChild(cn);
22090                 node.parentNode.insertBefore(cn, node);
22091                 // move node to parent - and clean it..
22092                 this.cleanWord(cn);
22093             }
22094             node.parentNode.removeChild(node);
22095             /// no need to iterate chidlren = it's got none..
22096             //this.iterateChildren(node, this.cleanWord);
22097             return;
22098         }
22099         // clean styles
22100         if (node.className.length) {
22101             
22102             var cn = node.className.split(/\W+/);
22103             var cna = [];
22104             Roo.each(cn, function(cls) {
22105                 if (cls.match(/Mso[a-zA-Z]+/)) {
22106                     return;
22107                 }
22108                 cna.push(cls);
22109             });
22110             node.className = cna.length ? cna.join(' ') : '';
22111             if (!cna.length) {
22112                 node.removeAttribute("class");
22113             }
22114         }
22115         
22116         if (node.hasAttribute("lang")) {
22117             node.removeAttribute("lang");
22118         }
22119         
22120         if (node.hasAttribute("style")) {
22121             
22122             var styles = node.getAttribute("style").split(";");
22123             var nstyle = [];
22124             Roo.each(styles, function(s) {
22125                 if (!s.match(/:/)) {
22126                     return;
22127                 }
22128                 var kv = s.split(":");
22129                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22130                     return;
22131                 }
22132                 // what ever is left... we allow.
22133                 nstyle.push(s);
22134             });
22135             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22136             if (!nstyle.length) {
22137                 node.removeAttribute('style');
22138             }
22139         }
22140         this.iterateChildren(node, this.cleanWord);
22141         
22142         
22143         
22144     },
22145     /**
22146      * iterateChildren of a Node, calling fn each time, using this as the scole..
22147      * @param {DomNode} node node to iterate children of.
22148      * @param {Function} fn method of this class to call on each item.
22149      */
22150     iterateChildren : function(node, fn)
22151     {
22152         if (!node.childNodes.length) {
22153                 return;
22154         }
22155         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22156            fn.call(this, node.childNodes[i])
22157         }
22158     },
22159     
22160     
22161     /**
22162      * cleanTableWidths.
22163      *
22164      * Quite often pasting from word etc.. results in tables with column and widths.
22165      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22166      *
22167      */
22168     cleanTableWidths : function(node)
22169     {
22170          
22171          
22172         if (!node) {
22173             this.cleanTableWidths(this.doc.body);
22174             return;
22175         }
22176         
22177         // ignore list...
22178         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22179             return; 
22180         }
22181         Roo.log(node.tagName);
22182         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22183             this.iterateChildren(node, this.cleanTableWidths);
22184             return;
22185         }
22186         if (node.hasAttribute('width')) {
22187             node.removeAttribute('width');
22188         }
22189         
22190          
22191         if (node.hasAttribute("style")) {
22192             // pretty basic...
22193             
22194             var styles = node.getAttribute("style").split(";");
22195             var nstyle = [];
22196             Roo.each(styles, function(s) {
22197                 if (!s.match(/:/)) {
22198                     return;
22199                 }
22200                 var kv = s.split(":");
22201                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22202                     return;
22203                 }
22204                 // what ever is left... we allow.
22205                 nstyle.push(s);
22206             });
22207             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22208             if (!nstyle.length) {
22209                 node.removeAttribute('style');
22210             }
22211         }
22212         
22213         this.iterateChildren(node, this.cleanTableWidths);
22214         
22215         
22216     },
22217     
22218     
22219     
22220     
22221     domToHTML : function(currentElement, depth, nopadtext) {
22222         
22223         depth = depth || 0;
22224         nopadtext = nopadtext || false;
22225     
22226         if (!currentElement) {
22227             return this.domToHTML(this.doc.body);
22228         }
22229         
22230         //Roo.log(currentElement);
22231         var j;
22232         var allText = false;
22233         var nodeName = currentElement.nodeName;
22234         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22235         
22236         if  (nodeName == '#text') {
22237             
22238             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22239         }
22240         
22241         
22242         var ret = '';
22243         if (nodeName != 'BODY') {
22244              
22245             var i = 0;
22246             // Prints the node tagName, such as <A>, <IMG>, etc
22247             if (tagName) {
22248                 var attr = [];
22249                 for(i = 0; i < currentElement.attributes.length;i++) {
22250                     // quoting?
22251                     var aname = currentElement.attributes.item(i).name;
22252                     if (!currentElement.attributes.item(i).value.length) {
22253                         continue;
22254                     }
22255                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22256                 }
22257                 
22258                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22259             } 
22260             else {
22261                 
22262                 // eack
22263             }
22264         } else {
22265             tagName = false;
22266         }
22267         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22268             return ret;
22269         }
22270         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22271             nopadtext = true;
22272         }
22273         
22274         
22275         // Traverse the tree
22276         i = 0;
22277         var currentElementChild = currentElement.childNodes.item(i);
22278         var allText = true;
22279         var innerHTML  = '';
22280         lastnode = '';
22281         while (currentElementChild) {
22282             // Formatting code (indent the tree so it looks nice on the screen)
22283             var nopad = nopadtext;
22284             if (lastnode == 'SPAN') {
22285                 nopad  = true;
22286             }
22287             // text
22288             if  (currentElementChild.nodeName == '#text') {
22289                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22290                 toadd = nopadtext ? toadd : toadd.trim();
22291                 if (!nopad && toadd.length > 80) {
22292                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22293                 }
22294                 innerHTML  += toadd;
22295                 
22296                 i++;
22297                 currentElementChild = currentElement.childNodes.item(i);
22298                 lastNode = '';
22299                 continue;
22300             }
22301             allText = false;
22302             
22303             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22304                 
22305             // Recursively traverse the tree structure of the child node
22306             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22307             lastnode = currentElementChild.nodeName;
22308             i++;
22309             currentElementChild=currentElement.childNodes.item(i);
22310         }
22311         
22312         ret += innerHTML;
22313         
22314         if (!allText) {
22315                 // The remaining code is mostly for formatting the tree
22316             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22317         }
22318         
22319         
22320         if (tagName) {
22321             ret+= "</"+tagName+">";
22322         }
22323         return ret;
22324         
22325     },
22326         
22327     applyBlacklists : function()
22328     {
22329         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22330         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22331         
22332         this.white = [];
22333         this.black = [];
22334         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22335             if (b.indexOf(tag) > -1) {
22336                 return;
22337             }
22338             this.white.push(tag);
22339             
22340         }, this);
22341         
22342         Roo.each(w, function(tag) {
22343             if (b.indexOf(tag) > -1) {
22344                 return;
22345             }
22346             if (this.white.indexOf(tag) > -1) {
22347                 return;
22348             }
22349             this.white.push(tag);
22350             
22351         }, this);
22352         
22353         
22354         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22355             if (w.indexOf(tag) > -1) {
22356                 return;
22357             }
22358             this.black.push(tag);
22359             
22360         }, this);
22361         
22362         Roo.each(b, function(tag) {
22363             if (w.indexOf(tag) > -1) {
22364                 return;
22365             }
22366             if (this.black.indexOf(tag) > -1) {
22367                 return;
22368             }
22369             this.black.push(tag);
22370             
22371         }, this);
22372         
22373         
22374         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22375         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22376         
22377         this.cwhite = [];
22378         this.cblack = [];
22379         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22380             if (b.indexOf(tag) > -1) {
22381                 return;
22382             }
22383             this.cwhite.push(tag);
22384             
22385         }, this);
22386         
22387         Roo.each(w, function(tag) {
22388             if (b.indexOf(tag) > -1) {
22389                 return;
22390             }
22391             if (this.cwhite.indexOf(tag) > -1) {
22392                 return;
22393             }
22394             this.cwhite.push(tag);
22395             
22396         }, this);
22397         
22398         
22399         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22400             if (w.indexOf(tag) > -1) {
22401                 return;
22402             }
22403             this.cblack.push(tag);
22404             
22405         }, this);
22406         
22407         Roo.each(b, function(tag) {
22408             if (w.indexOf(tag) > -1) {
22409                 return;
22410             }
22411             if (this.cblack.indexOf(tag) > -1) {
22412                 return;
22413             }
22414             this.cblack.push(tag);
22415             
22416         }, this);
22417     },
22418     
22419     setStylesheets : function(stylesheets)
22420     {
22421         if(typeof(stylesheets) == 'string'){
22422             Roo.get(this.iframe.contentDocument.head).createChild({
22423                 tag : 'link',
22424                 rel : 'stylesheet',
22425                 type : 'text/css',
22426                 href : stylesheets
22427             });
22428             
22429             return;
22430         }
22431         var _this = this;
22432      
22433         Roo.each(stylesheets, function(s) {
22434             if(!s.length){
22435                 return;
22436             }
22437             
22438             Roo.get(_this.iframe.contentDocument.head).createChild({
22439                 tag : 'link',
22440                 rel : 'stylesheet',
22441                 type : 'text/css',
22442                 href : s
22443             });
22444         });
22445
22446         
22447     },
22448     
22449     removeStylesheets : function()
22450     {
22451         var _this = this;
22452         
22453         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22454             s.remove();
22455         });
22456     },
22457     
22458     setStyle : function(style)
22459     {
22460         Roo.get(this.iframe.contentDocument.head).createChild({
22461             tag : 'style',
22462             type : 'text/css',
22463             html : style
22464         });
22465
22466         return;
22467     }
22468     
22469     // hide stuff that is not compatible
22470     /**
22471      * @event blur
22472      * @hide
22473      */
22474     /**
22475      * @event change
22476      * @hide
22477      */
22478     /**
22479      * @event focus
22480      * @hide
22481      */
22482     /**
22483      * @event specialkey
22484      * @hide
22485      */
22486     /**
22487      * @cfg {String} fieldClass @hide
22488      */
22489     /**
22490      * @cfg {String} focusClass @hide
22491      */
22492     /**
22493      * @cfg {String} autoCreate @hide
22494      */
22495     /**
22496      * @cfg {String} inputType @hide
22497      */
22498     /**
22499      * @cfg {String} invalidClass @hide
22500      */
22501     /**
22502      * @cfg {String} invalidText @hide
22503      */
22504     /**
22505      * @cfg {String} msgFx @hide
22506      */
22507     /**
22508      * @cfg {String} validateOnBlur @hide
22509      */
22510 });
22511
22512 Roo.HtmlEditorCore.white = [
22513         'area', 'br', 'img', 'input', 'hr', 'wbr',
22514         
22515        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22516        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22517        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22518        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22519        'table',   'ul',         'xmp', 
22520        
22521        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22522       'thead',   'tr', 
22523      
22524       'dir', 'menu', 'ol', 'ul', 'dl',
22525        
22526       'embed',  'object'
22527 ];
22528
22529
22530 Roo.HtmlEditorCore.black = [
22531     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22532         'applet', // 
22533         'base',   'basefont', 'bgsound', 'blink',  'body', 
22534         'frame',  'frameset', 'head',    'html',   'ilayer', 
22535         'iframe', 'layer',  'link',     'meta',    'object',   
22536         'script', 'style' ,'title',  'xml' // clean later..
22537 ];
22538 Roo.HtmlEditorCore.clean = [
22539     'script', 'style', 'title', 'xml'
22540 ];
22541 Roo.HtmlEditorCore.remove = [
22542     'font'
22543 ];
22544 // attributes..
22545
22546 Roo.HtmlEditorCore.ablack = [
22547     'on'
22548 ];
22549     
22550 Roo.HtmlEditorCore.aclean = [ 
22551     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22552 ];
22553
22554 // protocols..
22555 Roo.HtmlEditorCore.pwhite= [
22556         'http',  'https',  'mailto'
22557 ];
22558
22559 // white listed style attributes.
22560 Roo.HtmlEditorCore.cwhite= [
22561       //  'text-align', /// default is to allow most things..
22562       
22563          
22564 //        'font-size'//??
22565 ];
22566
22567 // black listed style attributes.
22568 Roo.HtmlEditorCore.cblack= [
22569       //  'font-size' -- this can be set by the project 
22570 ];
22571
22572
22573 Roo.HtmlEditorCore.swapCodes   =[ 
22574     [    8211, "--" ], 
22575     [    8212, "--" ], 
22576     [    8216,  "'" ],  
22577     [    8217, "'" ],  
22578     [    8220, '"' ],  
22579     [    8221, '"' ],  
22580     [    8226, "*" ],  
22581     [    8230, "..." ]
22582 ]; 
22583
22584     //<script type="text/javascript">
22585
22586 /*
22587  * Ext JS Library 1.1.1
22588  * Copyright(c) 2006-2007, Ext JS, LLC.
22589  * Licence LGPL
22590  * 
22591  */
22592  
22593  
22594 Roo.form.HtmlEditor = function(config){
22595     
22596     
22597     
22598     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22599     
22600     if (!this.toolbars) {
22601         this.toolbars = [];
22602     }
22603     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22604     
22605     
22606 };
22607
22608 /**
22609  * @class Roo.form.HtmlEditor
22610  * @extends Roo.form.Field
22611  * Provides a lightweight HTML Editor component.
22612  *
22613  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22614  * 
22615  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22616  * supported by this editor.</b><br/><br/>
22617  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22618  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22619  */
22620 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22621     /**
22622      * @cfg {Boolean} clearUp
22623      */
22624     clearUp : true,
22625       /**
22626      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22627      */
22628     toolbars : false,
22629    
22630      /**
22631      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22632      *                        Roo.resizable.
22633      */
22634     resizable : false,
22635      /**
22636      * @cfg {Number} height (in pixels)
22637      */   
22638     height: 300,
22639    /**
22640      * @cfg {Number} width (in pixels)
22641      */   
22642     width: 500,
22643     
22644     /**
22645      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22646      * 
22647      */
22648     stylesheets: false,
22649     
22650     
22651      /**
22652      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22653      * 
22654      */
22655     cblack: false,
22656     /**
22657      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22658      * 
22659      */
22660     cwhite: false,
22661     
22662      /**
22663      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22664      * 
22665      */
22666     black: false,
22667     /**
22668      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22669      * 
22670      */
22671     white: false,
22672     
22673     // id of frame..
22674     frameId: false,
22675     
22676     // private properties
22677     validationEvent : false,
22678     deferHeight: true,
22679     initialized : false,
22680     activated : false,
22681     
22682     onFocus : Roo.emptyFn,
22683     iframePad:3,
22684     hideMode:'offsets',
22685     
22686     actionMode : 'container', // defaults to hiding it...
22687     
22688     defaultAutoCreate : { // modified by initCompnoent..
22689         tag: "textarea",
22690         style:"width:500px;height:300px;",
22691         autocomplete: "new-password"
22692     },
22693
22694     // private
22695     initComponent : function(){
22696         this.addEvents({
22697             /**
22698              * @event initialize
22699              * Fires when the editor is fully initialized (including the iframe)
22700              * @param {HtmlEditor} this
22701              */
22702             initialize: true,
22703             /**
22704              * @event activate
22705              * Fires when the editor is first receives the focus. Any insertion must wait
22706              * until after this event.
22707              * @param {HtmlEditor} this
22708              */
22709             activate: true,
22710              /**
22711              * @event beforesync
22712              * Fires before the textarea is updated with content from the editor iframe. Return false
22713              * to cancel the sync.
22714              * @param {HtmlEditor} this
22715              * @param {String} html
22716              */
22717             beforesync: true,
22718              /**
22719              * @event beforepush
22720              * Fires before the iframe editor is updated with content from the textarea. Return false
22721              * to cancel the push.
22722              * @param {HtmlEditor} this
22723              * @param {String} html
22724              */
22725             beforepush: true,
22726              /**
22727              * @event sync
22728              * Fires when the textarea is updated with content from the editor iframe.
22729              * @param {HtmlEditor} this
22730              * @param {String} html
22731              */
22732             sync: true,
22733              /**
22734              * @event push
22735              * Fires when the iframe editor is updated with content from the textarea.
22736              * @param {HtmlEditor} this
22737              * @param {String} html
22738              */
22739             push: true,
22740              /**
22741              * @event editmodechange
22742              * Fires when the editor switches edit modes
22743              * @param {HtmlEditor} this
22744              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22745              */
22746             editmodechange: true,
22747             /**
22748              * @event editorevent
22749              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22750              * @param {HtmlEditor} this
22751              */
22752             editorevent: true,
22753             /**
22754              * @event firstfocus
22755              * Fires when on first focus - needed by toolbars..
22756              * @param {HtmlEditor} this
22757              */
22758             firstfocus: true,
22759             /**
22760              * @event autosave
22761              * Auto save the htmlEditor value as a file into Events
22762              * @param {HtmlEditor} this
22763              */
22764             autosave: true,
22765             /**
22766              * @event savedpreview
22767              * preview the saved version of htmlEditor
22768              * @param {HtmlEditor} this
22769              */
22770             savedpreview: true,
22771             
22772             /**
22773             * @event stylesheetsclick
22774             * Fires when press the Sytlesheets button
22775             * @param {Roo.HtmlEditorCore} this
22776             */
22777             stylesheetsclick: true
22778         });
22779         this.defaultAutoCreate =  {
22780             tag: "textarea",
22781             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22782             autocomplete: "new-password"
22783         };
22784     },
22785
22786     /**
22787      * Protected method that will not generally be called directly. It
22788      * is called when the editor creates its toolbar. Override this method if you need to
22789      * add custom toolbar buttons.
22790      * @param {HtmlEditor} editor
22791      */
22792     createToolbar : function(editor){
22793         Roo.log("create toolbars");
22794         if (!editor.toolbars || !editor.toolbars.length) {
22795             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22796         }
22797         
22798         for (var i =0 ; i < editor.toolbars.length;i++) {
22799             editor.toolbars[i] = Roo.factory(
22800                     typeof(editor.toolbars[i]) == 'string' ?
22801                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22802                 Roo.form.HtmlEditor);
22803             editor.toolbars[i].init(editor);
22804         }
22805          
22806         
22807     },
22808
22809      
22810     // private
22811     onRender : function(ct, position)
22812     {
22813         var _t = this;
22814         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22815         
22816         this.wrap = this.el.wrap({
22817             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22818         });
22819         
22820         this.editorcore.onRender(ct, position);
22821          
22822         if (this.resizable) {
22823             this.resizeEl = new Roo.Resizable(this.wrap, {
22824                 pinned : true,
22825                 wrap: true,
22826                 dynamic : true,
22827                 minHeight : this.height,
22828                 height: this.height,
22829                 handles : this.resizable,
22830                 width: this.width,
22831                 listeners : {
22832                     resize : function(r, w, h) {
22833                         _t.onResize(w,h); // -something
22834                     }
22835                 }
22836             });
22837             
22838         }
22839         this.createToolbar(this);
22840        
22841         
22842         if(!this.width){
22843             this.setSize(this.wrap.getSize());
22844         }
22845         if (this.resizeEl) {
22846             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22847             // should trigger onReize..
22848         }
22849         
22850         this.keyNav = new Roo.KeyNav(this.el, {
22851             
22852             "tab" : function(e){
22853                 e.preventDefault();
22854                 
22855                 var value = this.getValue();
22856                 
22857                 var start = this.el.dom.selectionStart;
22858                 var end = this.el.dom.selectionEnd;
22859                 
22860                 if(!e.shiftKey){
22861                     
22862                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22863                     this.el.dom.setSelectionRange(end + 1, end + 1);
22864                     return;
22865                 }
22866                 
22867                 var f = value.substring(0, start).split("\t");
22868                 
22869                 if(f.pop().length != 0){
22870                     return;
22871                 }
22872                 
22873                 this.setValue(f.join("\t") + value.substring(end));
22874                 this.el.dom.setSelectionRange(start - 1, start - 1);
22875                 
22876             },
22877             
22878             "home" : function(e){
22879                 e.preventDefault();
22880                 
22881                 var curr = this.el.dom.selectionStart;
22882                 var lines = this.getValue().split("\n");
22883                 
22884                 if(!lines.length){
22885                     return;
22886                 }
22887                 
22888                 if(e.ctrlKey){
22889                     this.el.dom.setSelectionRange(0, 0);
22890                     return;
22891                 }
22892                 
22893                 var pos = 0;
22894                 
22895                 for (var i = 0; i < lines.length;i++) {
22896                     pos += lines[i].length;
22897                     
22898                     if(i != 0){
22899                         pos += 1;
22900                     }
22901                     
22902                     if(pos < curr){
22903                         continue;
22904                     }
22905                     
22906                     pos -= lines[i].length;
22907                     
22908                     break;
22909                 }
22910                 
22911                 if(!e.shiftKey){
22912                     this.el.dom.setSelectionRange(pos, pos);
22913                     return;
22914                 }
22915                 
22916                 this.el.dom.selectionStart = pos;
22917                 this.el.dom.selectionEnd = curr;
22918             },
22919             
22920             "end" : function(e){
22921                 e.preventDefault();
22922                 
22923                 var curr = this.el.dom.selectionStart;
22924                 var lines = this.getValue().split("\n");
22925                 
22926                 if(!lines.length){
22927                     return;
22928                 }
22929                 
22930                 if(e.ctrlKey){
22931                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22932                     return;
22933                 }
22934                 
22935                 var pos = 0;
22936                 
22937                 for (var i = 0; i < lines.length;i++) {
22938                     
22939                     pos += lines[i].length;
22940                     
22941                     if(i != 0){
22942                         pos += 1;
22943                     }
22944                     
22945                     if(pos < curr){
22946                         continue;
22947                     }
22948                     
22949                     break;
22950                 }
22951                 
22952                 if(!e.shiftKey){
22953                     this.el.dom.setSelectionRange(pos, pos);
22954                     return;
22955                 }
22956                 
22957                 this.el.dom.selectionStart = curr;
22958                 this.el.dom.selectionEnd = pos;
22959             },
22960
22961             scope : this,
22962
22963             doRelay : function(foo, bar, hname){
22964                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22965             },
22966
22967             forceKeyDown: true
22968         });
22969         
22970 //        if(this.autosave && this.w){
22971 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22972 //        }
22973     },
22974
22975     // private
22976     onResize : function(w, h)
22977     {
22978         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22979         var ew = false;
22980         var eh = false;
22981         
22982         if(this.el ){
22983             if(typeof w == 'number'){
22984                 var aw = w - this.wrap.getFrameWidth('lr');
22985                 this.el.setWidth(this.adjustWidth('textarea', aw));
22986                 ew = aw;
22987             }
22988             if(typeof h == 'number'){
22989                 var tbh = 0;
22990                 for (var i =0; i < this.toolbars.length;i++) {
22991                     // fixme - ask toolbars for heights?
22992                     tbh += this.toolbars[i].tb.el.getHeight();
22993                     if (this.toolbars[i].footer) {
22994                         tbh += this.toolbars[i].footer.el.getHeight();
22995                     }
22996                 }
22997                 
22998                 
22999                 
23000                 
23001                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23002                 ah -= 5; // knock a few pixes off for look..
23003 //                Roo.log(ah);
23004                 this.el.setHeight(this.adjustWidth('textarea', ah));
23005                 var eh = ah;
23006             }
23007         }
23008         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23009         this.editorcore.onResize(ew,eh);
23010         
23011     },
23012
23013     /**
23014      * Toggles the editor between standard and source edit mode.
23015      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23016      */
23017     toggleSourceEdit : function(sourceEditMode)
23018     {
23019         this.editorcore.toggleSourceEdit(sourceEditMode);
23020         
23021         if(this.editorcore.sourceEditMode){
23022             Roo.log('editor - showing textarea');
23023             
23024 //            Roo.log('in');
23025 //            Roo.log(this.syncValue());
23026             this.editorcore.syncValue();
23027             this.el.removeClass('x-hidden');
23028             this.el.dom.removeAttribute('tabIndex');
23029             this.el.focus();
23030             
23031             for (var i = 0; i < this.toolbars.length; i++) {
23032                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23033                     this.toolbars[i].tb.hide();
23034                     this.toolbars[i].footer.hide();
23035                 }
23036             }
23037             
23038         }else{
23039             Roo.log('editor - hiding textarea');
23040 //            Roo.log('out')
23041 //            Roo.log(this.pushValue()); 
23042             this.editorcore.pushValue();
23043             
23044             this.el.addClass('x-hidden');
23045             this.el.dom.setAttribute('tabIndex', -1);
23046             
23047             for (var i = 0; i < this.toolbars.length; i++) {
23048                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23049                     this.toolbars[i].tb.show();
23050                     this.toolbars[i].footer.show();
23051                 }
23052             }
23053             
23054             //this.deferFocus();
23055         }
23056         
23057         this.setSize(this.wrap.getSize());
23058         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23059         
23060         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23061     },
23062  
23063     // private (for BoxComponent)
23064     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23065
23066     // private (for BoxComponent)
23067     getResizeEl : function(){
23068         return this.wrap;
23069     },
23070
23071     // private (for BoxComponent)
23072     getPositionEl : function(){
23073         return this.wrap;
23074     },
23075
23076     // private
23077     initEvents : function(){
23078         this.originalValue = this.getValue();
23079     },
23080
23081     /**
23082      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23083      * @method
23084      */
23085     markInvalid : Roo.emptyFn,
23086     /**
23087      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23088      * @method
23089      */
23090     clearInvalid : Roo.emptyFn,
23091
23092     setValue : function(v){
23093         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23094         this.editorcore.pushValue();
23095     },
23096
23097      
23098     // private
23099     deferFocus : function(){
23100         this.focus.defer(10, this);
23101     },
23102
23103     // doc'ed in Field
23104     focus : function(){
23105         this.editorcore.focus();
23106         
23107     },
23108       
23109
23110     // private
23111     onDestroy : function(){
23112         
23113         
23114         
23115         if(this.rendered){
23116             
23117             for (var i =0; i < this.toolbars.length;i++) {
23118                 // fixme - ask toolbars for heights?
23119                 this.toolbars[i].onDestroy();
23120             }
23121             
23122             this.wrap.dom.innerHTML = '';
23123             this.wrap.remove();
23124         }
23125     },
23126
23127     // private
23128     onFirstFocus : function(){
23129         //Roo.log("onFirstFocus");
23130         this.editorcore.onFirstFocus();
23131          for (var i =0; i < this.toolbars.length;i++) {
23132             this.toolbars[i].onFirstFocus();
23133         }
23134         
23135     },
23136     
23137     // private
23138     syncValue : function()
23139     {
23140         this.editorcore.syncValue();
23141     },
23142     
23143     pushValue : function()
23144     {
23145         this.editorcore.pushValue();
23146     },
23147     
23148     setStylesheets : function(stylesheets)
23149     {
23150         this.editorcore.setStylesheets(stylesheets);
23151     },
23152     
23153     removeStylesheets : function()
23154     {
23155         this.editorcore.removeStylesheets();
23156     }
23157      
23158     
23159     // hide stuff that is not compatible
23160     /**
23161      * @event blur
23162      * @hide
23163      */
23164     /**
23165      * @event change
23166      * @hide
23167      */
23168     /**
23169      * @event focus
23170      * @hide
23171      */
23172     /**
23173      * @event specialkey
23174      * @hide
23175      */
23176     /**
23177      * @cfg {String} fieldClass @hide
23178      */
23179     /**
23180      * @cfg {String} focusClass @hide
23181      */
23182     /**
23183      * @cfg {String} autoCreate @hide
23184      */
23185     /**
23186      * @cfg {String} inputType @hide
23187      */
23188     /**
23189      * @cfg {String} invalidClass @hide
23190      */
23191     /**
23192      * @cfg {String} invalidText @hide
23193      */
23194     /**
23195      * @cfg {String} msgFx @hide
23196      */
23197     /**
23198      * @cfg {String} validateOnBlur @hide
23199      */
23200 });
23201  
23202     // <script type="text/javascript">
23203 /*
23204  * Based on
23205  * Ext JS Library 1.1.1
23206  * Copyright(c) 2006-2007, Ext JS, LLC.
23207  *  
23208  
23209  */
23210
23211 /**
23212  * @class Roo.form.HtmlEditorToolbar1
23213  * Basic Toolbar
23214  * 
23215  * Usage:
23216  *
23217  new Roo.form.HtmlEditor({
23218     ....
23219     toolbars : [
23220         new Roo.form.HtmlEditorToolbar1({
23221             disable : { fonts: 1 , format: 1, ..., ... , ...],
23222             btns : [ .... ]
23223         })
23224     }
23225      
23226  * 
23227  * @cfg {Object} disable List of elements to disable..
23228  * @cfg {Array} btns List of additional buttons.
23229  * 
23230  * 
23231  * NEEDS Extra CSS? 
23232  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23233  */
23234  
23235 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23236 {
23237     
23238     Roo.apply(this, config);
23239     
23240     // default disabled, based on 'good practice'..
23241     this.disable = this.disable || {};
23242     Roo.applyIf(this.disable, {
23243         fontSize : true,
23244         colors : true,
23245         specialElements : true
23246     });
23247     
23248     
23249     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23250     // dont call parent... till later.
23251 }
23252
23253 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23254     
23255     tb: false,
23256     
23257     rendered: false,
23258     
23259     editor : false,
23260     editorcore : false,
23261     /**
23262      * @cfg {Object} disable  List of toolbar elements to disable
23263          
23264      */
23265     disable : false,
23266     
23267     
23268      /**
23269      * @cfg {String} createLinkText The default text for the create link prompt
23270      */
23271     createLinkText : 'Please enter the URL for the link:',
23272     /**
23273      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23274      */
23275     defaultLinkValue : 'http:/'+'/',
23276    
23277     
23278       /**
23279      * @cfg {Array} fontFamilies An array of available font families
23280      */
23281     fontFamilies : [
23282         'Arial',
23283         'Courier New',
23284         'Tahoma',
23285         'Times New Roman',
23286         'Verdana'
23287     ],
23288     
23289     specialChars : [
23290            "&#169;",
23291           "&#174;",     
23292           "&#8482;",    
23293           "&#163;" ,    
23294          // "&#8212;",    
23295           "&#8230;",    
23296           "&#247;" ,    
23297         //  "&#225;" ,     ?? a acute?
23298            "&#8364;"    , //Euro
23299        //   "&#8220;"    ,
23300         //  "&#8221;"    ,
23301         //  "&#8226;"    ,
23302           "&#176;"  //   , // degrees
23303
23304          // "&#233;"     , // e ecute
23305          // "&#250;"     , // u ecute?
23306     ],
23307     
23308     specialElements : [
23309         {
23310             text: "Insert Table",
23311             xtype: 'MenuItem',
23312             xns : Roo.Menu,
23313             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23314                 
23315         },
23316         {    
23317             text: "Insert Image",
23318             xtype: 'MenuItem',
23319             xns : Roo.Menu,
23320             ihtml : '<img src="about:blank"/>'
23321             
23322         }
23323         
23324          
23325     ],
23326     
23327     
23328     inputElements : [ 
23329             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23330             "input:submit", "input:button", "select", "textarea", "label" ],
23331     formats : [
23332         ["p"] ,  
23333         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23334         ["pre"],[ "code"], 
23335         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23336         ['div'],['span'],
23337         ['sup'],['sub']
23338     ],
23339     
23340     cleanStyles : [
23341         "font-size"
23342     ],
23343      /**
23344      * @cfg {String} defaultFont default font to use.
23345      */
23346     defaultFont: 'tahoma',
23347    
23348     fontSelect : false,
23349     
23350     
23351     formatCombo : false,
23352     
23353     init : function(editor)
23354     {
23355         this.editor = editor;
23356         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23357         var editorcore = this.editorcore;
23358         
23359         var _t = this;
23360         
23361         var fid = editorcore.frameId;
23362         var etb = this;
23363         function btn(id, toggle, handler){
23364             var xid = fid + '-'+ id ;
23365             return {
23366                 id : xid,
23367                 cmd : id,
23368                 cls : 'x-btn-icon x-edit-'+id,
23369                 enableToggle:toggle !== false,
23370                 scope: _t, // was editor...
23371                 handler:handler||_t.relayBtnCmd,
23372                 clickEvent:'mousedown',
23373                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23374                 tabIndex:-1
23375             };
23376         }
23377         
23378         
23379         
23380         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23381         this.tb = tb;
23382          // stop form submits
23383         tb.el.on('click', function(e){
23384             e.preventDefault(); // what does this do?
23385         });
23386
23387         if(!this.disable.font) { // && !Roo.isSafari){
23388             /* why no safari for fonts 
23389             editor.fontSelect = tb.el.createChild({
23390                 tag:'select',
23391                 tabIndex: -1,
23392                 cls:'x-font-select',
23393                 html: this.createFontOptions()
23394             });
23395             
23396             editor.fontSelect.on('change', function(){
23397                 var font = editor.fontSelect.dom.value;
23398                 editor.relayCmd('fontname', font);
23399                 editor.deferFocus();
23400             }, editor);
23401             
23402             tb.add(
23403                 editor.fontSelect.dom,
23404                 '-'
23405             );
23406             */
23407             
23408         };
23409         if(!this.disable.formats){
23410             this.formatCombo = new Roo.form.ComboBox({
23411                 store: new Roo.data.SimpleStore({
23412                     id : 'tag',
23413                     fields: ['tag'],
23414                     data : this.formats // from states.js
23415                 }),
23416                 blockFocus : true,
23417                 name : '',
23418                 //autoCreate : {tag: "div",  size: "20"},
23419                 displayField:'tag',
23420                 typeAhead: false,
23421                 mode: 'local',
23422                 editable : false,
23423                 triggerAction: 'all',
23424                 emptyText:'Add tag',
23425                 selectOnFocus:true,
23426                 width:135,
23427                 listeners : {
23428                     'select': function(c, r, i) {
23429                         editorcore.insertTag(r.get('tag'));
23430                         editor.focus();
23431                     }
23432                 }
23433
23434             });
23435             tb.addField(this.formatCombo);
23436             
23437         }
23438         
23439         if(!this.disable.format){
23440             tb.add(
23441                 btn('bold'),
23442                 btn('italic'),
23443                 btn('underline'),
23444                 btn('strikethrough')
23445             );
23446         };
23447         if(!this.disable.fontSize){
23448             tb.add(
23449                 '-',
23450                 
23451                 
23452                 btn('increasefontsize', false, editorcore.adjustFont),
23453                 btn('decreasefontsize', false, editorcore.adjustFont)
23454             );
23455         };
23456         
23457         
23458         if(!this.disable.colors){
23459             tb.add(
23460                 '-', {
23461                     id:editorcore.frameId +'-forecolor',
23462                     cls:'x-btn-icon x-edit-forecolor',
23463                     clickEvent:'mousedown',
23464                     tooltip: this.buttonTips['forecolor'] || undefined,
23465                     tabIndex:-1,
23466                     menu : new Roo.menu.ColorMenu({
23467                         allowReselect: true,
23468                         focus: Roo.emptyFn,
23469                         value:'000000',
23470                         plain:true,
23471                         selectHandler: function(cp, color){
23472                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23473                             editor.deferFocus();
23474                         },
23475                         scope: editorcore,
23476                         clickEvent:'mousedown'
23477                     })
23478                 }, {
23479                     id:editorcore.frameId +'backcolor',
23480                     cls:'x-btn-icon x-edit-backcolor',
23481                     clickEvent:'mousedown',
23482                     tooltip: this.buttonTips['backcolor'] || undefined,
23483                     tabIndex:-1,
23484                     menu : new Roo.menu.ColorMenu({
23485                         focus: Roo.emptyFn,
23486                         value:'FFFFFF',
23487                         plain:true,
23488                         allowReselect: true,
23489                         selectHandler: function(cp, color){
23490                             if(Roo.isGecko){
23491                                 editorcore.execCmd('useCSS', false);
23492                                 editorcore.execCmd('hilitecolor', color);
23493                                 editorcore.execCmd('useCSS', true);
23494                                 editor.deferFocus();
23495                             }else{
23496                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23497                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23498                                 editor.deferFocus();
23499                             }
23500                         },
23501                         scope:editorcore,
23502                         clickEvent:'mousedown'
23503                     })
23504                 }
23505             );
23506         };
23507         // now add all the items...
23508         
23509
23510         if(!this.disable.alignments){
23511             tb.add(
23512                 '-',
23513                 btn('justifyleft'),
23514                 btn('justifycenter'),
23515                 btn('justifyright')
23516             );
23517         };
23518
23519         //if(!Roo.isSafari){
23520             if(!this.disable.links){
23521                 tb.add(
23522                     '-',
23523                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23524                 );
23525             };
23526
23527             if(!this.disable.lists){
23528                 tb.add(
23529                     '-',
23530                     btn('insertorderedlist'),
23531                     btn('insertunorderedlist')
23532                 );
23533             }
23534             if(!this.disable.sourceEdit){
23535                 tb.add(
23536                     '-',
23537                     btn('sourceedit', true, function(btn){
23538                         this.toggleSourceEdit(btn.pressed);
23539                     })
23540                 );
23541             }
23542         //}
23543         
23544         var smenu = { };
23545         // special menu.. - needs to be tidied up..
23546         if (!this.disable.special) {
23547             smenu = {
23548                 text: "&#169;",
23549                 cls: 'x-edit-none',
23550                 
23551                 menu : {
23552                     items : []
23553                 }
23554             };
23555             for (var i =0; i < this.specialChars.length; i++) {
23556                 smenu.menu.items.push({
23557                     
23558                     html: this.specialChars[i],
23559                     handler: function(a,b) {
23560                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23561                         //editor.insertAtCursor(a.html);
23562                         
23563                     },
23564                     tabIndex:-1
23565                 });
23566             }
23567             
23568             
23569             tb.add(smenu);
23570             
23571             
23572         }
23573         
23574         var cmenu = { };
23575         if (!this.disable.cleanStyles) {
23576             cmenu = {
23577                 cls: 'x-btn-icon x-btn-clear',
23578                 
23579                 menu : {
23580                     items : []
23581                 }
23582             };
23583             for (var i =0; i < this.cleanStyles.length; i++) {
23584                 cmenu.menu.items.push({
23585                     actiontype : this.cleanStyles[i],
23586                     html: 'Remove ' + this.cleanStyles[i],
23587                     handler: function(a,b) {
23588 //                        Roo.log(a);
23589 //                        Roo.log(b);
23590                         var c = Roo.get(editorcore.doc.body);
23591                         c.select('[style]').each(function(s) {
23592                             s.dom.style.removeProperty(a.actiontype);
23593                         });
23594                         editorcore.syncValue();
23595                     },
23596                     tabIndex:-1
23597                 });
23598             }
23599              cmenu.menu.items.push({
23600                 actiontype : 'tablewidths',
23601                 html: 'Remove Table Widths',
23602                 handler: function(a,b) {
23603                     editorcore.cleanTableWidths();
23604                     editorcore.syncValue();
23605                 },
23606                 tabIndex:-1
23607             });
23608             cmenu.menu.items.push({
23609                 actiontype : 'word',
23610                 html: 'Remove MS Word Formating',
23611                 handler: function(a,b) {
23612                     editorcore.cleanWord();
23613                     editorcore.syncValue();
23614                 },
23615                 tabIndex:-1
23616             });
23617             
23618             cmenu.menu.items.push({
23619                 actiontype : 'all',
23620                 html: 'Remove All Styles',
23621                 handler: function(a,b) {
23622                     
23623                     var c = Roo.get(editorcore.doc.body);
23624                     c.select('[style]').each(function(s) {
23625                         s.dom.removeAttribute('style');
23626                     });
23627                     editorcore.syncValue();
23628                 },
23629                 tabIndex:-1
23630             });
23631             
23632             cmenu.menu.items.push({
23633                 actiontype : 'all',
23634                 html: 'Remove All CSS Classes',
23635                 handler: function(a,b) {
23636                     
23637                     var c = Roo.get(editorcore.doc.body);
23638                     c.select('[class]').each(function(s) {
23639                         s.dom.removeAttribute('class');
23640                     });
23641                     editorcore.cleanWord();
23642                     editorcore.syncValue();
23643                 },
23644                 tabIndex:-1
23645             });
23646             
23647              cmenu.menu.items.push({
23648                 actiontype : 'tidy',
23649                 html: 'Tidy HTML Source',
23650                 handler: function(a,b) {
23651                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23652                     editorcore.syncValue();
23653                 },
23654                 tabIndex:-1
23655             });
23656             
23657             
23658             tb.add(cmenu);
23659         }
23660          
23661         if (!this.disable.specialElements) {
23662             var semenu = {
23663                 text: "Other;",
23664                 cls: 'x-edit-none',
23665                 menu : {
23666                     items : []
23667                 }
23668             };
23669             for (var i =0; i < this.specialElements.length; i++) {
23670                 semenu.menu.items.push(
23671                     Roo.apply({ 
23672                         handler: function(a,b) {
23673                             editor.insertAtCursor(this.ihtml);
23674                         }
23675                     }, this.specialElements[i])
23676                 );
23677                     
23678             }
23679             
23680             tb.add(semenu);
23681             
23682             
23683         }
23684          
23685         
23686         if (this.btns) {
23687             for(var i =0; i< this.btns.length;i++) {
23688                 var b = Roo.factory(this.btns[i],Roo.form);
23689                 b.cls =  'x-edit-none';
23690                 
23691                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23692                     b.cls += ' x-init-enable';
23693                 }
23694                 
23695                 b.scope = editorcore;
23696                 tb.add(b);
23697             }
23698         
23699         }
23700         
23701         
23702         
23703         // disable everything...
23704         
23705         this.tb.items.each(function(item){
23706             
23707            if(
23708                 item.id != editorcore.frameId+ '-sourceedit' && 
23709                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23710             ){
23711                 
23712                 item.disable();
23713             }
23714         });
23715         this.rendered = true;
23716         
23717         // the all the btns;
23718         editor.on('editorevent', this.updateToolbar, this);
23719         // other toolbars need to implement this..
23720         //editor.on('editmodechange', this.updateToolbar, this);
23721     },
23722     
23723     
23724     relayBtnCmd : function(btn) {
23725         this.editorcore.relayCmd(btn.cmd);
23726     },
23727     // private used internally
23728     createLink : function(){
23729         Roo.log("create link?");
23730         var url = prompt(this.createLinkText, this.defaultLinkValue);
23731         if(url && url != 'http:/'+'/'){
23732             this.editorcore.relayCmd('createlink', url);
23733         }
23734     },
23735
23736     
23737     /**
23738      * Protected method that will not generally be called directly. It triggers
23739      * a toolbar update by reading the markup state of the current selection in the editor.
23740      */
23741     updateToolbar: function(){
23742
23743         if(!this.editorcore.activated){
23744             this.editor.onFirstFocus();
23745             return;
23746         }
23747
23748         var btns = this.tb.items.map, 
23749             doc = this.editorcore.doc,
23750             frameId = this.editorcore.frameId;
23751
23752         if(!this.disable.font && !Roo.isSafari){
23753             /*
23754             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23755             if(name != this.fontSelect.dom.value){
23756                 this.fontSelect.dom.value = name;
23757             }
23758             */
23759         }
23760         if(!this.disable.format){
23761             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23762             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23763             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23764             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23765         }
23766         if(!this.disable.alignments){
23767             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23768             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23769             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23770         }
23771         if(!Roo.isSafari && !this.disable.lists){
23772             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23773             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23774         }
23775         
23776         var ans = this.editorcore.getAllAncestors();
23777         if (this.formatCombo) {
23778             
23779             
23780             var store = this.formatCombo.store;
23781             this.formatCombo.setValue("");
23782             for (var i =0; i < ans.length;i++) {
23783                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23784                     // select it..
23785                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23786                     break;
23787                 }
23788             }
23789         }
23790         
23791         
23792         
23793         // hides menus... - so this cant be on a menu...
23794         Roo.menu.MenuMgr.hideAll();
23795
23796         //this.editorsyncValue();
23797     },
23798    
23799     
23800     createFontOptions : function(){
23801         var buf = [], fs = this.fontFamilies, ff, lc;
23802         
23803         
23804         
23805         for(var i = 0, len = fs.length; i< len; i++){
23806             ff = fs[i];
23807             lc = ff.toLowerCase();
23808             buf.push(
23809                 '<option value="',lc,'" style="font-family:',ff,';"',
23810                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23811                     ff,
23812                 '</option>'
23813             );
23814         }
23815         return buf.join('');
23816     },
23817     
23818     toggleSourceEdit : function(sourceEditMode){
23819         
23820         Roo.log("toolbar toogle");
23821         if(sourceEditMode === undefined){
23822             sourceEditMode = !this.sourceEditMode;
23823         }
23824         this.sourceEditMode = sourceEditMode === true;
23825         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23826         // just toggle the button?
23827         if(btn.pressed !== this.sourceEditMode){
23828             btn.toggle(this.sourceEditMode);
23829             return;
23830         }
23831         
23832         if(sourceEditMode){
23833             Roo.log("disabling buttons");
23834             this.tb.items.each(function(item){
23835                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23836                     item.disable();
23837                 }
23838             });
23839           
23840         }else{
23841             Roo.log("enabling buttons");
23842             if(this.editorcore.initialized){
23843                 this.tb.items.each(function(item){
23844                     item.enable();
23845                 });
23846             }
23847             
23848         }
23849         Roo.log("calling toggole on editor");
23850         // tell the editor that it's been pressed..
23851         this.editor.toggleSourceEdit(sourceEditMode);
23852        
23853     },
23854      /**
23855      * Object collection of toolbar tooltips for the buttons in the editor. The key
23856      * is the command id associated with that button and the value is a valid QuickTips object.
23857      * For example:
23858 <pre><code>
23859 {
23860     bold : {
23861         title: 'Bold (Ctrl+B)',
23862         text: 'Make the selected text bold.',
23863         cls: 'x-html-editor-tip'
23864     },
23865     italic : {
23866         title: 'Italic (Ctrl+I)',
23867         text: 'Make the selected text italic.',
23868         cls: 'x-html-editor-tip'
23869     },
23870     ...
23871 </code></pre>
23872     * @type Object
23873      */
23874     buttonTips : {
23875         bold : {
23876             title: 'Bold (Ctrl+B)',
23877             text: 'Make the selected text bold.',
23878             cls: 'x-html-editor-tip'
23879         },
23880         italic : {
23881             title: 'Italic (Ctrl+I)',
23882             text: 'Make the selected text italic.',
23883             cls: 'x-html-editor-tip'
23884         },
23885         underline : {
23886             title: 'Underline (Ctrl+U)',
23887             text: 'Underline the selected text.',
23888             cls: 'x-html-editor-tip'
23889         },
23890         strikethrough : {
23891             title: 'Strikethrough',
23892             text: 'Strikethrough the selected text.',
23893             cls: 'x-html-editor-tip'
23894         },
23895         increasefontsize : {
23896             title: 'Grow Text',
23897             text: 'Increase the font size.',
23898             cls: 'x-html-editor-tip'
23899         },
23900         decreasefontsize : {
23901             title: 'Shrink Text',
23902             text: 'Decrease the font size.',
23903             cls: 'x-html-editor-tip'
23904         },
23905         backcolor : {
23906             title: 'Text Highlight Color',
23907             text: 'Change the background color of the selected text.',
23908             cls: 'x-html-editor-tip'
23909         },
23910         forecolor : {
23911             title: 'Font Color',
23912             text: 'Change the color of the selected text.',
23913             cls: 'x-html-editor-tip'
23914         },
23915         justifyleft : {
23916             title: 'Align Text Left',
23917             text: 'Align text to the left.',
23918             cls: 'x-html-editor-tip'
23919         },
23920         justifycenter : {
23921             title: 'Center Text',
23922             text: 'Center text in the editor.',
23923             cls: 'x-html-editor-tip'
23924         },
23925         justifyright : {
23926             title: 'Align Text Right',
23927             text: 'Align text to the right.',
23928             cls: 'x-html-editor-tip'
23929         },
23930         insertunorderedlist : {
23931             title: 'Bullet List',
23932             text: 'Start a bulleted list.',
23933             cls: 'x-html-editor-tip'
23934         },
23935         insertorderedlist : {
23936             title: 'Numbered List',
23937             text: 'Start a numbered list.',
23938             cls: 'x-html-editor-tip'
23939         },
23940         createlink : {
23941             title: 'Hyperlink',
23942             text: 'Make the selected text a hyperlink.',
23943             cls: 'x-html-editor-tip'
23944         },
23945         sourceedit : {
23946             title: 'Source Edit',
23947             text: 'Switch to source editing mode.',
23948             cls: 'x-html-editor-tip'
23949         }
23950     },
23951     // private
23952     onDestroy : function(){
23953         if(this.rendered){
23954             
23955             this.tb.items.each(function(item){
23956                 if(item.menu){
23957                     item.menu.removeAll();
23958                     if(item.menu.el){
23959                         item.menu.el.destroy();
23960                     }
23961                 }
23962                 item.destroy();
23963             });
23964              
23965         }
23966     },
23967     onFirstFocus: function() {
23968         this.tb.items.each(function(item){
23969            item.enable();
23970         });
23971     }
23972 });
23973
23974
23975
23976
23977 // <script type="text/javascript">
23978 /*
23979  * Based on
23980  * Ext JS Library 1.1.1
23981  * Copyright(c) 2006-2007, Ext JS, LLC.
23982  *  
23983  
23984  */
23985
23986  
23987 /**
23988  * @class Roo.form.HtmlEditor.ToolbarContext
23989  * Context Toolbar
23990  * 
23991  * Usage:
23992  *
23993  new Roo.form.HtmlEditor({
23994     ....
23995     toolbars : [
23996         { xtype: 'ToolbarStandard', styles : {} }
23997         { xtype: 'ToolbarContext', disable : {} }
23998     ]
23999 })
24000
24001      
24002  * 
24003  * @config : {Object} disable List of elements to disable.. (not done yet.)
24004  * @config : {Object} styles  Map of styles available.
24005  * 
24006  */
24007
24008 Roo.form.HtmlEditor.ToolbarContext = function(config)
24009 {
24010     
24011     Roo.apply(this, config);
24012     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24013     // dont call parent... till later.
24014     this.styles = this.styles || {};
24015 }
24016
24017  
24018
24019 Roo.form.HtmlEditor.ToolbarContext.types = {
24020     'IMG' : {
24021         width : {
24022             title: "Width",
24023             width: 40
24024         },
24025         height:  {
24026             title: "Height",
24027             width: 40
24028         },
24029         align: {
24030             title: "Align",
24031             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24032             width : 80
24033             
24034         },
24035         border: {
24036             title: "Border",
24037             width: 40
24038         },
24039         alt: {
24040             title: "Alt",
24041             width: 120
24042         },
24043         src : {
24044             title: "Src",
24045             width: 220
24046         }
24047         
24048     },
24049     'A' : {
24050         name : {
24051             title: "Name",
24052             width: 50
24053         },
24054         target:  {
24055             title: "Target",
24056             width: 120
24057         },
24058         href:  {
24059             title: "Href",
24060             width: 220
24061         } // border?
24062         
24063     },
24064     'TABLE' : {
24065         rows : {
24066             title: "Rows",
24067             width: 20
24068         },
24069         cols : {
24070             title: "Cols",
24071             width: 20
24072         },
24073         width : {
24074             title: "Width",
24075             width: 40
24076         },
24077         height : {
24078             title: "Height",
24079             width: 40
24080         },
24081         border : {
24082             title: "Border",
24083             width: 20
24084         }
24085     },
24086     'TD' : {
24087         width : {
24088             title: "Width",
24089             width: 40
24090         },
24091         height : {
24092             title: "Height",
24093             width: 40
24094         },   
24095         align: {
24096             title: "Align",
24097             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24098             width: 80
24099         },
24100         valign: {
24101             title: "Valign",
24102             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24103             width: 80
24104         },
24105         colspan: {
24106             title: "Colspan",
24107             width: 20
24108             
24109         },
24110          'font-family'  : {
24111             title : "Font",
24112             style : 'fontFamily',
24113             displayField: 'display',
24114             optname : 'font-family',
24115             width: 140
24116         }
24117     },
24118     'INPUT' : {
24119         name : {
24120             title: "name",
24121             width: 120
24122         },
24123         value : {
24124             title: "Value",
24125             width: 120
24126         },
24127         width : {
24128             title: "Width",
24129             width: 40
24130         }
24131     },
24132     'LABEL' : {
24133         'for' : {
24134             title: "For",
24135             width: 120
24136         }
24137     },
24138     'TEXTAREA' : {
24139           name : {
24140             title: "name",
24141             width: 120
24142         },
24143         rows : {
24144             title: "Rows",
24145             width: 20
24146         },
24147         cols : {
24148             title: "Cols",
24149             width: 20
24150         }
24151     },
24152     'SELECT' : {
24153         name : {
24154             title: "name",
24155             width: 120
24156         },
24157         selectoptions : {
24158             title: "Options",
24159             width: 200
24160         }
24161     },
24162     
24163     // should we really allow this??
24164     // should this just be 
24165     'BODY' : {
24166         title : {
24167             title: "Title",
24168             width: 200,
24169             disabled : true
24170         }
24171     },
24172     'SPAN' : {
24173         'font-family'  : {
24174             title : "Font",
24175             style : 'fontFamily',
24176             displayField: 'display',
24177             optname : 'font-family',
24178             width: 140
24179         }
24180     },
24181     'DIV' : {
24182         'font-family'  : {
24183             title : "Font",
24184             style : 'fontFamily',
24185             displayField: 'display',
24186             optname : 'font-family',
24187             width: 140
24188         }
24189     },
24190      'P' : {
24191         'font-family'  : {
24192             title : "Font",
24193             style : 'fontFamily',
24194             displayField: 'display',
24195             optname : 'font-family',
24196             width: 140
24197         }
24198     },
24199     
24200     '*' : {
24201         // empty..
24202     }
24203
24204 };
24205
24206 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24207 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24208
24209 Roo.form.HtmlEditor.ToolbarContext.options = {
24210         'font-family'  : [ 
24211                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24212                 [ 'Courier New', 'Courier New'],
24213                 [ 'Tahoma', 'Tahoma'],
24214                 [ 'Times New Roman,serif', 'Times'],
24215                 [ 'Verdana','Verdana' ]
24216         ]
24217 };
24218
24219 // fixme - these need to be configurable..
24220  
24221
24222 //Roo.form.HtmlEditor.ToolbarContext.types
24223
24224
24225 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24226     
24227     tb: false,
24228     
24229     rendered: false,
24230     
24231     editor : false,
24232     editorcore : false,
24233     /**
24234      * @cfg {Object} disable  List of toolbar elements to disable
24235          
24236      */
24237     disable : false,
24238     /**
24239      * @cfg {Object} styles List of styles 
24240      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24241      *
24242      * These must be defined in the page, so they get rendered correctly..
24243      * .headline { }
24244      * TD.underline { }
24245      * 
24246      */
24247     styles : false,
24248     
24249     options: false,
24250     
24251     toolbars : false,
24252     
24253     init : function(editor)
24254     {
24255         this.editor = editor;
24256         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24257         var editorcore = this.editorcore;
24258         
24259         var fid = editorcore.frameId;
24260         var etb = this;
24261         function btn(id, toggle, handler){
24262             var xid = fid + '-'+ id ;
24263             return {
24264                 id : xid,
24265                 cmd : id,
24266                 cls : 'x-btn-icon x-edit-'+id,
24267                 enableToggle:toggle !== false,
24268                 scope: editorcore, // was editor...
24269                 handler:handler||editorcore.relayBtnCmd,
24270                 clickEvent:'mousedown',
24271                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24272                 tabIndex:-1
24273             };
24274         }
24275         // create a new element.
24276         var wdiv = editor.wrap.createChild({
24277                 tag: 'div'
24278             }, editor.wrap.dom.firstChild.nextSibling, true);
24279         
24280         // can we do this more than once??
24281         
24282          // stop form submits
24283       
24284  
24285         // disable everything...
24286         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24287         this.toolbars = {};
24288            
24289         for (var i in  ty) {
24290           
24291             this.toolbars[i] = this.buildToolbar(ty[i],i);
24292         }
24293         this.tb = this.toolbars.BODY;
24294         this.tb.el.show();
24295         this.buildFooter();
24296         this.footer.show();
24297         editor.on('hide', function( ) { this.footer.hide() }, this);
24298         editor.on('show', function( ) { this.footer.show() }, this);
24299         
24300          
24301         this.rendered = true;
24302         
24303         // the all the btns;
24304         editor.on('editorevent', this.updateToolbar, this);
24305         // other toolbars need to implement this..
24306         //editor.on('editmodechange', this.updateToolbar, this);
24307     },
24308     
24309     
24310     
24311     /**
24312      * Protected method that will not generally be called directly. It triggers
24313      * a toolbar update by reading the markup state of the current selection in the editor.
24314      *
24315      * Note you can force an update by calling on('editorevent', scope, false)
24316      */
24317     updateToolbar: function(editor,ev,sel){
24318
24319         //Roo.log(ev);
24320         // capture mouse up - this is handy for selecting images..
24321         // perhaps should go somewhere else...
24322         if(!this.editorcore.activated){
24323              this.editor.onFirstFocus();
24324             return;
24325         }
24326         
24327         
24328         
24329         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24330         // selectNode - might want to handle IE?
24331         if (ev &&
24332             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24333             ev.target && ev.target.tagName == 'IMG') {
24334             // they have click on an image...
24335             // let's see if we can change the selection...
24336             sel = ev.target;
24337          
24338               var nodeRange = sel.ownerDocument.createRange();
24339             try {
24340                 nodeRange.selectNode(sel);
24341             } catch (e) {
24342                 nodeRange.selectNodeContents(sel);
24343             }
24344             //nodeRange.collapse(true);
24345             var s = this.editorcore.win.getSelection();
24346             s.removeAllRanges();
24347             s.addRange(nodeRange);
24348         }  
24349         
24350       
24351         var updateFooter = sel ? false : true;
24352         
24353         
24354         var ans = this.editorcore.getAllAncestors();
24355         
24356         // pick
24357         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24358         
24359         if (!sel) { 
24360             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24361             sel = sel ? sel : this.editorcore.doc.body;
24362             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24363             
24364         }
24365         // pick a menu that exists..
24366         var tn = sel.tagName.toUpperCase();
24367         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24368         
24369         tn = sel.tagName.toUpperCase();
24370         
24371         var lastSel = this.tb.selectedNode;
24372         
24373         this.tb.selectedNode = sel;
24374         
24375         // if current menu does not match..
24376         
24377         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24378                 
24379             this.tb.el.hide();
24380             ///console.log("show: " + tn);
24381             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24382             this.tb.el.show();
24383             // update name
24384             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24385             
24386             
24387             // update attributes
24388             if (this.tb.fields) {
24389                 this.tb.fields.each(function(e) {
24390                     if (e.stylename) {
24391                         e.setValue(sel.style[e.stylename]);
24392                         return;
24393                     } 
24394                    e.setValue(sel.getAttribute(e.attrname));
24395                 });
24396             }
24397             
24398             var hasStyles = false;
24399             for(var i in this.styles) {
24400                 hasStyles = true;
24401                 break;
24402             }
24403             
24404             // update styles
24405             if (hasStyles) { 
24406                 var st = this.tb.fields.item(0);
24407                 
24408                 st.store.removeAll();
24409                
24410                 
24411                 var cn = sel.className.split(/\s+/);
24412                 
24413                 var avs = [];
24414                 if (this.styles['*']) {
24415                     
24416                     Roo.each(this.styles['*'], function(v) {
24417                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24418                     });
24419                 }
24420                 if (this.styles[tn]) { 
24421                     Roo.each(this.styles[tn], function(v) {
24422                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24423                     });
24424                 }
24425                 
24426                 st.store.loadData(avs);
24427                 st.collapse();
24428                 st.setValue(cn);
24429             }
24430             // flag our selected Node.
24431             this.tb.selectedNode = sel;
24432            
24433            
24434             Roo.menu.MenuMgr.hideAll();
24435
24436         }
24437         
24438         if (!updateFooter) {
24439             //this.footDisp.dom.innerHTML = ''; 
24440             return;
24441         }
24442         // update the footer
24443         //
24444         var html = '';
24445         
24446         this.footerEls = ans.reverse();
24447         Roo.each(this.footerEls, function(a,i) {
24448             if (!a) { return; }
24449             html += html.length ? ' &gt; '  :  '';
24450             
24451             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24452             
24453         });
24454        
24455         // 
24456         var sz = this.footDisp.up('td').getSize();
24457         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24458         this.footDisp.dom.style.marginLeft = '5px';
24459         
24460         this.footDisp.dom.style.overflow = 'hidden';
24461         
24462         this.footDisp.dom.innerHTML = html;
24463             
24464         //this.editorsyncValue();
24465     },
24466      
24467     
24468    
24469        
24470     // private
24471     onDestroy : function(){
24472         if(this.rendered){
24473             
24474             this.tb.items.each(function(item){
24475                 if(item.menu){
24476                     item.menu.removeAll();
24477                     if(item.menu.el){
24478                         item.menu.el.destroy();
24479                     }
24480                 }
24481                 item.destroy();
24482             });
24483              
24484         }
24485     },
24486     onFirstFocus: function() {
24487         // need to do this for all the toolbars..
24488         this.tb.items.each(function(item){
24489            item.enable();
24490         });
24491     },
24492     buildToolbar: function(tlist, nm)
24493     {
24494         var editor = this.editor;
24495         var editorcore = this.editorcore;
24496          // create a new element.
24497         var wdiv = editor.wrap.createChild({
24498                 tag: 'div'
24499             }, editor.wrap.dom.firstChild.nextSibling, true);
24500         
24501        
24502         var tb = new Roo.Toolbar(wdiv);
24503         // add the name..
24504         
24505         tb.add(nm+ ":&nbsp;");
24506         
24507         var styles = [];
24508         for(var i in this.styles) {
24509             styles.push(i);
24510         }
24511         
24512         // styles...
24513         if (styles && styles.length) {
24514             
24515             // this needs a multi-select checkbox...
24516             tb.addField( new Roo.form.ComboBox({
24517                 store: new Roo.data.SimpleStore({
24518                     id : 'val',
24519                     fields: ['val', 'selected'],
24520                     data : [] 
24521                 }),
24522                 name : '-roo-edit-className',
24523                 attrname : 'className',
24524                 displayField: 'val',
24525                 typeAhead: false,
24526                 mode: 'local',
24527                 editable : false,
24528                 triggerAction: 'all',
24529                 emptyText:'Select Style',
24530                 selectOnFocus:true,
24531                 width: 130,
24532                 listeners : {
24533                     'select': function(c, r, i) {
24534                         // initial support only for on class per el..
24535                         tb.selectedNode.className =  r ? r.get('val') : '';
24536                         editorcore.syncValue();
24537                     }
24538                 }
24539     
24540             }));
24541         }
24542         
24543         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24544         var tbops = tbc.options;
24545         
24546         for (var i in tlist) {
24547             
24548             var item = tlist[i];
24549             tb.add(item.title + ":&nbsp;");
24550             
24551             
24552             //optname == used so you can configure the options available..
24553             var opts = item.opts ? item.opts : false;
24554             if (item.optname) {
24555                 opts = tbops[item.optname];
24556            
24557             }
24558             
24559             if (opts) {
24560                 // opts == pulldown..
24561                 tb.addField( new Roo.form.ComboBox({
24562                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24563                         id : 'val',
24564                         fields: ['val', 'display'],
24565                         data : opts  
24566                     }),
24567                     name : '-roo-edit-' + i,
24568                     attrname : i,
24569                     stylename : item.style ? item.style : false,
24570                     displayField: item.displayField ? item.displayField : 'val',
24571                     valueField :  'val',
24572                     typeAhead: false,
24573                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24574                     editable : false,
24575                     triggerAction: 'all',
24576                     emptyText:'Select',
24577                     selectOnFocus:true,
24578                     width: item.width ? item.width  : 130,
24579                     listeners : {
24580                         'select': function(c, r, i) {
24581                             if (c.stylename) {
24582                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24583                                 return;
24584                             }
24585                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24586                         }
24587                     }
24588
24589                 }));
24590                 continue;
24591                     
24592                  
24593                 
24594                 tb.addField( new Roo.form.TextField({
24595                     name: i,
24596                     width: 100,
24597                     //allowBlank:false,
24598                     value: ''
24599                 }));
24600                 continue;
24601             }
24602             tb.addField( new Roo.form.TextField({
24603                 name: '-roo-edit-' + i,
24604                 attrname : i,
24605                 
24606                 width: item.width,
24607                 //allowBlank:true,
24608                 value: '',
24609                 listeners: {
24610                     'change' : function(f, nv, ov) {
24611                         tb.selectedNode.setAttribute(f.attrname, nv);
24612                         editorcore.syncValue();
24613                     }
24614                 }
24615             }));
24616              
24617         }
24618         
24619         var _this = this;
24620         
24621         if(nm == 'BODY'){
24622             tb.addSeparator();
24623         
24624             tb.addButton( {
24625                 text: 'Stylesheets',
24626
24627                 listeners : {
24628                     click : function ()
24629                     {
24630                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24631                     }
24632                 }
24633             });
24634         }
24635         
24636         tb.addFill();
24637         tb.addButton( {
24638             text: 'Remove Tag',
24639     
24640             listeners : {
24641                 click : function ()
24642                 {
24643                     // remove
24644                     // undo does not work.
24645                      
24646                     var sn = tb.selectedNode;
24647                     
24648                     var pn = sn.parentNode;
24649                     
24650                     var stn =  sn.childNodes[0];
24651                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24652                     while (sn.childNodes.length) {
24653                         var node = sn.childNodes[0];
24654                         sn.removeChild(node);
24655                         //Roo.log(node);
24656                         pn.insertBefore(node, sn);
24657                         
24658                     }
24659                     pn.removeChild(sn);
24660                     var range = editorcore.createRange();
24661         
24662                     range.setStart(stn,0);
24663                     range.setEnd(en,0); //????
24664                     //range.selectNode(sel);
24665                     
24666                     
24667                     var selection = editorcore.getSelection();
24668                     selection.removeAllRanges();
24669                     selection.addRange(range);
24670                     
24671                     
24672                     
24673                     //_this.updateToolbar(null, null, pn);
24674                     _this.updateToolbar(null, null, null);
24675                     _this.footDisp.dom.innerHTML = ''; 
24676                 }
24677             }
24678             
24679                     
24680                 
24681             
24682         });
24683         
24684         
24685         tb.el.on('click', function(e){
24686             e.preventDefault(); // what does this do?
24687         });
24688         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24689         tb.el.hide();
24690         tb.name = nm;
24691         // dont need to disable them... as they will get hidden
24692         return tb;
24693          
24694         
24695     },
24696     buildFooter : function()
24697     {
24698         
24699         var fel = this.editor.wrap.createChild();
24700         this.footer = new Roo.Toolbar(fel);
24701         // toolbar has scrolly on left / right?
24702         var footDisp= new Roo.Toolbar.Fill();
24703         var _t = this;
24704         this.footer.add(
24705             {
24706                 text : '&lt;',
24707                 xtype: 'Button',
24708                 handler : function() {
24709                     _t.footDisp.scrollTo('left',0,true)
24710                 }
24711             }
24712         );
24713         this.footer.add( footDisp );
24714         this.footer.add( 
24715             {
24716                 text : '&gt;',
24717                 xtype: 'Button',
24718                 handler : function() {
24719                     // no animation..
24720                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24721                 }
24722             }
24723         );
24724         var fel = Roo.get(footDisp.el);
24725         fel.addClass('x-editor-context');
24726         this.footDispWrap = fel; 
24727         this.footDispWrap.overflow  = 'hidden';
24728         
24729         this.footDisp = fel.createChild();
24730         this.footDispWrap.on('click', this.onContextClick, this)
24731         
24732         
24733     },
24734     onContextClick : function (ev,dom)
24735     {
24736         ev.preventDefault();
24737         var  cn = dom.className;
24738         //Roo.log(cn);
24739         if (!cn.match(/x-ed-loc-/)) {
24740             return;
24741         }
24742         var n = cn.split('-').pop();
24743         var ans = this.footerEls;
24744         var sel = ans[n];
24745         
24746          // pick
24747         var range = this.editorcore.createRange();
24748         
24749         range.selectNodeContents(sel);
24750         //range.selectNode(sel);
24751         
24752         
24753         var selection = this.editorcore.getSelection();
24754         selection.removeAllRanges();
24755         selection.addRange(range);
24756         
24757         
24758         
24759         this.updateToolbar(null, null, sel);
24760         
24761         
24762     }
24763     
24764     
24765     
24766     
24767     
24768 });
24769
24770
24771
24772
24773
24774 /*
24775  * Based on:
24776  * Ext JS Library 1.1.1
24777  * Copyright(c) 2006-2007, Ext JS, LLC.
24778  *
24779  * Originally Released Under LGPL - original licence link has changed is not relivant.
24780  *
24781  * Fork - LGPL
24782  * <script type="text/javascript">
24783  */
24784  
24785 /**
24786  * @class Roo.form.BasicForm
24787  * @extends Roo.util.Observable
24788  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24789  * @constructor
24790  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24791  * @param {Object} config Configuration options
24792  */
24793 Roo.form.BasicForm = function(el, config){
24794     this.allItems = [];
24795     this.childForms = [];
24796     Roo.apply(this, config);
24797     /*
24798      * The Roo.form.Field items in this form.
24799      * @type MixedCollection
24800      */
24801      
24802      
24803     this.items = new Roo.util.MixedCollection(false, function(o){
24804         return o.id || (o.id = Roo.id());
24805     });
24806     this.addEvents({
24807         /**
24808          * @event beforeaction
24809          * Fires before any action is performed. Return false to cancel the action.
24810          * @param {Form} this
24811          * @param {Action} action The action to be performed
24812          */
24813         beforeaction: true,
24814         /**
24815          * @event actionfailed
24816          * Fires when an action fails.
24817          * @param {Form} this
24818          * @param {Action} action The action that failed
24819          */
24820         actionfailed : true,
24821         /**
24822          * @event actioncomplete
24823          * Fires when an action is completed.
24824          * @param {Form} this
24825          * @param {Action} action The action that completed
24826          */
24827         actioncomplete : true
24828     });
24829     if(el){
24830         this.initEl(el);
24831     }
24832     Roo.form.BasicForm.superclass.constructor.call(this);
24833     
24834     Roo.form.BasicForm.popover.apply();
24835 };
24836
24837 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24838     /**
24839      * @cfg {String} method
24840      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24841      */
24842     /**
24843      * @cfg {DataReader} reader
24844      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24845      * This is optional as there is built-in support for processing JSON.
24846      */
24847     /**
24848      * @cfg {DataReader} errorReader
24849      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24850      * This is completely optional as there is built-in support for processing JSON.
24851      */
24852     /**
24853      * @cfg {String} url
24854      * The URL to use for form actions if one isn't supplied in the action options.
24855      */
24856     /**
24857      * @cfg {Boolean} fileUpload
24858      * Set to true if this form is a file upload.
24859      */
24860      
24861     /**
24862      * @cfg {Object} baseParams
24863      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24864      */
24865      /**
24866      
24867     /**
24868      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24869      */
24870     timeout: 30,
24871
24872     // private
24873     activeAction : null,
24874
24875     /**
24876      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24877      * or setValues() data instead of when the form was first created.
24878      */
24879     trackResetOnLoad : false,
24880     
24881     
24882     /**
24883      * childForms - used for multi-tab forms
24884      * @type {Array}
24885      */
24886     childForms : false,
24887     
24888     /**
24889      * allItems - full list of fields.
24890      * @type {Array}
24891      */
24892     allItems : false,
24893     
24894     /**
24895      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24896      * element by passing it or its id or mask the form itself by passing in true.
24897      * @type Mixed
24898      */
24899     waitMsgTarget : false,
24900     
24901     /**
24902      * @type Boolean
24903      */
24904     disableMask : false,
24905     
24906     /**
24907      * @cfg {Boolean} errorMask (true|false) default false
24908      */
24909     errorMask : false,
24910     
24911     /**
24912      * @cfg {Number} maskOffset Default 100
24913      */
24914     maskOffset : 100,
24915
24916     // private
24917     initEl : function(el){
24918         this.el = Roo.get(el);
24919         this.id = this.el.id || Roo.id();
24920         this.el.on('submit', this.onSubmit, this);
24921         this.el.addClass('x-form');
24922     },
24923
24924     // private
24925     onSubmit : function(e){
24926         e.stopEvent();
24927     },
24928
24929     /**
24930      * Returns true if client-side validation on the form is successful.
24931      * @return Boolean
24932      */
24933     isValid : function(){
24934         var valid = true;
24935         var target = false;
24936         this.items.each(function(f){
24937             if(f.validate()){
24938                 return;
24939             }
24940             
24941             valid = false;
24942                 
24943             if(!target && f.el.isVisible(true)){
24944                 target = f;
24945             }
24946         });
24947         
24948         if(this.errorMask && !valid){
24949             Roo.form.BasicForm.popover.mask(this, target);
24950         }
24951         
24952         return valid;
24953     },
24954
24955     /**
24956      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24957      * @return Boolean
24958      */
24959     isDirty : function(){
24960         var dirty = false;
24961         this.items.each(function(f){
24962            if(f.isDirty()){
24963                dirty = true;
24964                return false;
24965            }
24966         });
24967         return dirty;
24968     },
24969     
24970     /**
24971      * Returns true if any fields in this form have changed since their original load. (New version)
24972      * @return Boolean
24973      */
24974     
24975     hasChanged : function()
24976     {
24977         var dirty = false;
24978         this.items.each(function(f){
24979            if(f.hasChanged()){
24980                dirty = true;
24981                return false;
24982            }
24983         });
24984         return dirty;
24985         
24986     },
24987     /**
24988      * Resets all hasChanged to 'false' -
24989      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24990      * So hasChanged storage is only to be used for this purpose
24991      * @return Boolean
24992      */
24993     resetHasChanged : function()
24994     {
24995         this.items.each(function(f){
24996            f.resetHasChanged();
24997         });
24998         
24999     },
25000     
25001     
25002     /**
25003      * Performs a predefined action (submit or load) or custom actions you define on this form.
25004      * @param {String} actionName The name of the action type
25005      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25006      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25007      * accept other config options):
25008      * <pre>
25009 Property          Type             Description
25010 ----------------  ---------------  ----------------------------------------------------------------------------------
25011 url               String           The url for the action (defaults to the form's url)
25012 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25013 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25014 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25015                                    validate the form on the client (defaults to false)
25016      * </pre>
25017      * @return {BasicForm} this
25018      */
25019     doAction : function(action, options){
25020         if(typeof action == 'string'){
25021             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25022         }
25023         if(this.fireEvent('beforeaction', this, action) !== false){
25024             this.beforeAction(action);
25025             action.run.defer(100, action);
25026         }
25027         return this;
25028     },
25029
25030     /**
25031      * Shortcut to do a submit action.
25032      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25033      * @return {BasicForm} this
25034      */
25035     submit : function(options){
25036         this.doAction('submit', options);
25037         return this;
25038     },
25039
25040     /**
25041      * Shortcut to do a load action.
25042      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25043      * @return {BasicForm} this
25044      */
25045     load : function(options){
25046         this.doAction('load', options);
25047         return this;
25048     },
25049
25050     /**
25051      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25052      * @param {Record} record The record to edit
25053      * @return {BasicForm} this
25054      */
25055     updateRecord : function(record){
25056         record.beginEdit();
25057         var fs = record.fields;
25058         fs.each(function(f){
25059             var field = this.findField(f.name);
25060             if(field){
25061                 record.set(f.name, field.getValue());
25062             }
25063         }, this);
25064         record.endEdit();
25065         return this;
25066     },
25067
25068     /**
25069      * Loads an Roo.data.Record into this form.
25070      * @param {Record} record The record to load
25071      * @return {BasicForm} this
25072      */
25073     loadRecord : function(record){
25074         this.setValues(record.data);
25075         return this;
25076     },
25077
25078     // private
25079     beforeAction : function(action){
25080         var o = action.options;
25081         
25082         if(!this.disableMask) {
25083             if(this.waitMsgTarget === true){
25084                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25085             }else if(this.waitMsgTarget){
25086                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25087                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25088             }else {
25089                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25090             }
25091         }
25092         
25093          
25094     },
25095
25096     // private
25097     afterAction : function(action, success){
25098         this.activeAction = null;
25099         var o = action.options;
25100         
25101         if(!this.disableMask) {
25102             if(this.waitMsgTarget === true){
25103                 this.el.unmask();
25104             }else if(this.waitMsgTarget){
25105                 this.waitMsgTarget.unmask();
25106             }else{
25107                 Roo.MessageBox.updateProgress(1);
25108                 Roo.MessageBox.hide();
25109             }
25110         }
25111         
25112         if(success){
25113             if(o.reset){
25114                 this.reset();
25115             }
25116             Roo.callback(o.success, o.scope, [this, action]);
25117             this.fireEvent('actioncomplete', this, action);
25118             
25119         }else{
25120             
25121             // failure condition..
25122             // we have a scenario where updates need confirming.
25123             // eg. if a locking scenario exists..
25124             // we look for { errors : { needs_confirm : true }} in the response.
25125             if (
25126                 (typeof(action.result) != 'undefined')  &&
25127                 (typeof(action.result.errors) != 'undefined')  &&
25128                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25129            ){
25130                 var _t = this;
25131                 Roo.MessageBox.confirm(
25132                     "Change requires confirmation",
25133                     action.result.errorMsg,
25134                     function(r) {
25135                         if (r != 'yes') {
25136                             return;
25137                         }
25138                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25139                     }
25140                     
25141                 );
25142                 
25143                 
25144                 
25145                 return;
25146             }
25147             
25148             Roo.callback(o.failure, o.scope, [this, action]);
25149             // show an error message if no failed handler is set..
25150             if (!this.hasListener('actionfailed')) {
25151                 Roo.MessageBox.alert("Error",
25152                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25153                         action.result.errorMsg :
25154                         "Saving Failed, please check your entries or try again"
25155                 );
25156             }
25157             
25158             this.fireEvent('actionfailed', this, action);
25159         }
25160         
25161     },
25162
25163     /**
25164      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25165      * @param {String} id The value to search for
25166      * @return Field
25167      */
25168     findField : function(id){
25169         var field = this.items.get(id);
25170         if(!field){
25171             this.items.each(function(f){
25172                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25173                     field = f;
25174                     return false;
25175                 }
25176             });
25177         }
25178         return field || null;
25179     },
25180
25181     /**
25182      * Add a secondary form to this one, 
25183      * Used to provide tabbed forms. One form is primary, with hidden values 
25184      * which mirror the elements from the other forms.
25185      * 
25186      * @param {Roo.form.Form} form to add.
25187      * 
25188      */
25189     addForm : function(form)
25190     {
25191        
25192         if (this.childForms.indexOf(form) > -1) {
25193             // already added..
25194             return;
25195         }
25196         this.childForms.push(form);
25197         var n = '';
25198         Roo.each(form.allItems, function (fe) {
25199             
25200             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25201             if (this.findField(n)) { // already added..
25202                 return;
25203             }
25204             var add = new Roo.form.Hidden({
25205                 name : n
25206             });
25207             add.render(this.el);
25208             
25209             this.add( add );
25210         }, this);
25211         
25212     },
25213     /**
25214      * Mark fields in this form invalid in bulk.
25215      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25216      * @return {BasicForm} this
25217      */
25218     markInvalid : function(errors){
25219         if(errors instanceof Array){
25220             for(var i = 0, len = errors.length; i < len; i++){
25221                 var fieldError = errors[i];
25222                 var f = this.findField(fieldError.id);
25223                 if(f){
25224                     f.markInvalid(fieldError.msg);
25225                 }
25226             }
25227         }else{
25228             var field, id;
25229             for(id in errors){
25230                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25231                     field.markInvalid(errors[id]);
25232                 }
25233             }
25234         }
25235         Roo.each(this.childForms || [], function (f) {
25236             f.markInvalid(errors);
25237         });
25238         
25239         return this;
25240     },
25241
25242     /**
25243      * Set values for fields in this form in bulk.
25244      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25245      * @return {BasicForm} this
25246      */
25247     setValues : function(values){
25248         if(values instanceof Array){ // array of objects
25249             for(var i = 0, len = values.length; i < len; i++){
25250                 var v = values[i];
25251                 var f = this.findField(v.id);
25252                 if(f){
25253                     f.setValue(v.value);
25254                     if(this.trackResetOnLoad){
25255                         f.originalValue = f.getValue();
25256                     }
25257                 }
25258             }
25259         }else{ // object hash
25260             var field, id;
25261             for(id in values){
25262                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25263                     
25264                     if (field.setFromData && 
25265                         field.valueField && 
25266                         field.displayField &&
25267                         // combos' with local stores can 
25268                         // be queried via setValue()
25269                         // to set their value..
25270                         (field.store && !field.store.isLocal)
25271                         ) {
25272                         // it's a combo
25273                         var sd = { };
25274                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25275                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25276                         field.setFromData(sd);
25277                         
25278                     } else {
25279                         field.setValue(values[id]);
25280                     }
25281                     
25282                     
25283                     if(this.trackResetOnLoad){
25284                         field.originalValue = field.getValue();
25285                     }
25286                 }
25287             }
25288         }
25289         this.resetHasChanged();
25290         
25291         
25292         Roo.each(this.childForms || [], function (f) {
25293             f.setValues(values);
25294             f.resetHasChanged();
25295         });
25296                 
25297         return this;
25298     },
25299  
25300     /**
25301      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25302      * they are returned as an array.
25303      * @param {Boolean} asString
25304      * @return {Object}
25305      */
25306     getValues : function(asString){
25307         if (this.childForms) {
25308             // copy values from the child forms
25309             Roo.each(this.childForms, function (f) {
25310                 this.setValues(f.getValues());
25311             }, this);
25312         }
25313         
25314         // use formdata
25315         if (typeof(FormData) != 'undefined' && asString !== true) {
25316             var fd = (new FormData(this.el.dom)).entries();
25317             var ret = {};
25318             var ent = fd.next();
25319             while (!ent.done) {
25320                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25321                 ent = fd.next();
25322             };
25323             return ret;
25324         }
25325         
25326         
25327         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25328         if(asString === true){
25329             return fs;
25330         }
25331         return Roo.urlDecode(fs);
25332     },
25333     
25334     /**
25335      * Returns the fields in this form as an object with key/value pairs. 
25336      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25337      * @return {Object}
25338      */
25339     getFieldValues : function(with_hidden)
25340     {
25341         if (this.childForms) {
25342             // copy values from the child forms
25343             // should this call getFieldValues - probably not as we do not currently copy
25344             // hidden fields when we generate..
25345             Roo.each(this.childForms, function (f) {
25346                 this.setValues(f.getValues());
25347             }, this);
25348         }
25349         
25350         var ret = {};
25351         this.items.each(function(f){
25352             if (!f.getName()) {
25353                 return;
25354             }
25355             var v = f.getValue();
25356             if (f.inputType =='radio') {
25357                 if (typeof(ret[f.getName()]) == 'undefined') {
25358                     ret[f.getName()] = ''; // empty..
25359                 }
25360                 
25361                 if (!f.el.dom.checked) {
25362                     return;
25363                     
25364                 }
25365                 v = f.el.dom.value;
25366                 
25367             }
25368             
25369             // not sure if this supported any more..
25370             if ((typeof(v) == 'object') && f.getRawValue) {
25371                 v = f.getRawValue() ; // dates..
25372             }
25373             // combo boxes where name != hiddenName...
25374             if (f.name != f.getName()) {
25375                 ret[f.name] = f.getRawValue();
25376             }
25377             ret[f.getName()] = v;
25378         });
25379         
25380         return ret;
25381     },
25382
25383     /**
25384      * Clears all invalid messages in this form.
25385      * @return {BasicForm} this
25386      */
25387     clearInvalid : function(){
25388         this.items.each(function(f){
25389            f.clearInvalid();
25390         });
25391         
25392         Roo.each(this.childForms || [], function (f) {
25393             f.clearInvalid();
25394         });
25395         
25396         
25397         return this;
25398     },
25399
25400     /**
25401      * Resets this form.
25402      * @return {BasicForm} this
25403      */
25404     reset : function(){
25405         this.items.each(function(f){
25406             f.reset();
25407         });
25408         
25409         Roo.each(this.childForms || [], function (f) {
25410             f.reset();
25411         });
25412         this.resetHasChanged();
25413         
25414         return this;
25415     },
25416
25417     /**
25418      * Add Roo.form components to this form.
25419      * @param {Field} field1
25420      * @param {Field} field2 (optional)
25421      * @param {Field} etc (optional)
25422      * @return {BasicForm} this
25423      */
25424     add : function(){
25425         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25426         return this;
25427     },
25428
25429
25430     /**
25431      * Removes a field from the items collection (does NOT remove its markup).
25432      * @param {Field} field
25433      * @return {BasicForm} this
25434      */
25435     remove : function(field){
25436         this.items.remove(field);
25437         return this;
25438     },
25439
25440     /**
25441      * Looks at the fields in this form, checks them for an id attribute,
25442      * and calls applyTo on the existing dom element with that id.
25443      * @return {BasicForm} this
25444      */
25445     render : function(){
25446         this.items.each(function(f){
25447             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25448                 f.applyTo(f.id);
25449             }
25450         });
25451         return this;
25452     },
25453
25454     /**
25455      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25456      * @param {Object} values
25457      * @return {BasicForm} this
25458      */
25459     applyToFields : function(o){
25460         this.items.each(function(f){
25461            Roo.apply(f, o);
25462         });
25463         return this;
25464     },
25465
25466     /**
25467      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25468      * @param {Object} values
25469      * @return {BasicForm} this
25470      */
25471     applyIfToFields : function(o){
25472         this.items.each(function(f){
25473            Roo.applyIf(f, o);
25474         });
25475         return this;
25476     }
25477 });
25478
25479 // back compat
25480 Roo.BasicForm = Roo.form.BasicForm;
25481
25482 Roo.apply(Roo.form.BasicForm, {
25483     
25484     popover : {
25485         
25486         padding : 5,
25487         
25488         isApplied : false,
25489         
25490         isMasked : false,
25491         
25492         form : false,
25493         
25494         target : false,
25495         
25496         intervalID : false,
25497         
25498         maskEl : false,
25499         
25500         apply : function()
25501         {
25502             if(this.isApplied){
25503                 return;
25504             }
25505             
25506             this.maskEl = {
25507                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25508                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25509                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25510                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25511             };
25512             
25513             this.maskEl.top.enableDisplayMode("block");
25514             this.maskEl.left.enableDisplayMode("block");
25515             this.maskEl.bottom.enableDisplayMode("block");
25516             this.maskEl.right.enableDisplayMode("block");
25517             
25518             Roo.get(document.body).on('click', function(){
25519                 this.unmask();
25520             }, this);
25521             
25522             Roo.get(document.body).on('touchstart', function(){
25523                 this.unmask();
25524             }, this);
25525             
25526             this.isApplied = true
25527         },
25528         
25529         mask : function(form, target)
25530         {
25531             this.form = form;
25532             
25533             this.target = target;
25534             
25535             if(!this.form.errorMask || !target.el){
25536                 return;
25537             }
25538             
25539             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25540             
25541             var ot = this.target.el.calcOffsetsTo(scrollable);
25542             
25543             var scrollTo = ot[1] - this.form.maskOffset;
25544             
25545             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25546             
25547             scrollable.scrollTo('top', scrollTo);
25548             
25549             var el = this.target.wrap || this.target.el;
25550             
25551             var box = el.getBox();
25552             
25553             this.maskEl.top.setStyle('position', 'absolute');
25554             this.maskEl.top.setStyle('z-index', 10000);
25555             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25556             this.maskEl.top.setLeft(0);
25557             this.maskEl.top.setTop(0);
25558             this.maskEl.top.show();
25559             
25560             this.maskEl.left.setStyle('position', 'absolute');
25561             this.maskEl.left.setStyle('z-index', 10000);
25562             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25563             this.maskEl.left.setLeft(0);
25564             this.maskEl.left.setTop(box.y - this.padding);
25565             this.maskEl.left.show();
25566
25567             this.maskEl.bottom.setStyle('position', 'absolute');
25568             this.maskEl.bottom.setStyle('z-index', 10000);
25569             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25570             this.maskEl.bottom.setLeft(0);
25571             this.maskEl.bottom.setTop(box.bottom + this.padding);
25572             this.maskEl.bottom.show();
25573
25574             this.maskEl.right.setStyle('position', 'absolute');
25575             this.maskEl.right.setStyle('z-index', 10000);
25576             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25577             this.maskEl.right.setLeft(box.right + this.padding);
25578             this.maskEl.right.setTop(box.y - this.padding);
25579             this.maskEl.right.show();
25580
25581             this.intervalID = window.setInterval(function() {
25582                 Roo.form.BasicForm.popover.unmask();
25583             }, 10000);
25584
25585             window.onwheel = function(){ return false;};
25586             
25587             (function(){ this.isMasked = true; }).defer(500, this);
25588             
25589         },
25590         
25591         unmask : function()
25592         {
25593             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25594                 return;
25595             }
25596             
25597             this.maskEl.top.setStyle('position', 'absolute');
25598             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25599             this.maskEl.top.hide();
25600
25601             this.maskEl.left.setStyle('position', 'absolute');
25602             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25603             this.maskEl.left.hide();
25604
25605             this.maskEl.bottom.setStyle('position', 'absolute');
25606             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25607             this.maskEl.bottom.hide();
25608
25609             this.maskEl.right.setStyle('position', 'absolute');
25610             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25611             this.maskEl.right.hide();
25612             
25613             window.onwheel = function(){ return true;};
25614             
25615             if(this.intervalID){
25616                 window.clearInterval(this.intervalID);
25617                 this.intervalID = false;
25618             }
25619             
25620             this.isMasked = false;
25621             
25622         }
25623         
25624     }
25625     
25626 });/*
25627  * Based on:
25628  * Ext JS Library 1.1.1
25629  * Copyright(c) 2006-2007, Ext JS, LLC.
25630  *
25631  * Originally Released Under LGPL - original licence link has changed is not relivant.
25632  *
25633  * Fork - LGPL
25634  * <script type="text/javascript">
25635  */
25636
25637 /**
25638  * @class Roo.form.Form
25639  * @extends Roo.form.BasicForm
25640  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25641  * @constructor
25642  * @param {Object} config Configuration options
25643  */
25644 Roo.form.Form = function(config){
25645     var xitems =  [];
25646     if (config.items) {
25647         xitems = config.items;
25648         delete config.items;
25649     }
25650    
25651     
25652     Roo.form.Form.superclass.constructor.call(this, null, config);
25653     this.url = this.url || this.action;
25654     if(!this.root){
25655         this.root = new Roo.form.Layout(Roo.applyIf({
25656             id: Roo.id()
25657         }, config));
25658     }
25659     this.active = this.root;
25660     /**
25661      * Array of all the buttons that have been added to this form via {@link addButton}
25662      * @type Array
25663      */
25664     this.buttons = [];
25665     this.allItems = [];
25666     this.addEvents({
25667         /**
25668          * @event clientvalidation
25669          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25670          * @param {Form} this
25671          * @param {Boolean} valid true if the form has passed client-side validation
25672          */
25673         clientvalidation: true,
25674         /**
25675          * @event rendered
25676          * Fires when the form is rendered
25677          * @param {Roo.form.Form} form
25678          */
25679         rendered : true
25680     });
25681     
25682     if (this.progressUrl) {
25683             // push a hidden field onto the list of fields..
25684             this.addxtype( {
25685                     xns: Roo.form, 
25686                     xtype : 'Hidden', 
25687                     name : 'UPLOAD_IDENTIFIER' 
25688             });
25689         }
25690         
25691     
25692     Roo.each(xitems, this.addxtype, this);
25693     
25694 };
25695
25696 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25697     /**
25698      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25699      */
25700     /**
25701      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25702      */
25703     /**
25704      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25705      */
25706     buttonAlign:'center',
25707
25708     /**
25709      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25710      */
25711     minButtonWidth:75,
25712
25713     /**
25714      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25715      * This property cascades to child containers if not set.
25716      */
25717     labelAlign:'left',
25718
25719     /**
25720      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25721      * fires a looping event with that state. This is required to bind buttons to the valid
25722      * state using the config value formBind:true on the button.
25723      */
25724     monitorValid : false,
25725
25726     /**
25727      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25728      */
25729     monitorPoll : 200,
25730     
25731     /**
25732      * @cfg {String} progressUrl - Url to return progress data 
25733      */
25734     
25735     progressUrl : false,
25736     /**
25737      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25738      * sending a formdata with extra parameters - eg uploaded elements.
25739      */
25740     
25741     formData : false,
25742     
25743     /**
25744      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25745      * fields are added and the column is closed. If no fields are passed the column remains open
25746      * until end() is called.
25747      * @param {Object} config The config to pass to the column
25748      * @param {Field} field1 (optional)
25749      * @param {Field} field2 (optional)
25750      * @param {Field} etc (optional)
25751      * @return Column The column container object
25752      */
25753     column : function(c){
25754         var col = new Roo.form.Column(c);
25755         this.start(col);
25756         if(arguments.length > 1){ // duplicate code required because of Opera
25757             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25758             this.end();
25759         }
25760         return col;
25761     },
25762
25763     /**
25764      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25765      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25766      * until end() is called.
25767      * @param {Object} config The config to pass to the fieldset
25768      * @param {Field} field1 (optional)
25769      * @param {Field} field2 (optional)
25770      * @param {Field} etc (optional)
25771      * @return FieldSet The fieldset container object
25772      */
25773     fieldset : function(c){
25774         var fs = new Roo.form.FieldSet(c);
25775         this.start(fs);
25776         if(arguments.length > 1){ // duplicate code required because of Opera
25777             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25778             this.end();
25779         }
25780         return fs;
25781     },
25782
25783     /**
25784      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25785      * fields are added and the container is closed. If no fields are passed the container remains open
25786      * until end() is called.
25787      * @param {Object} config The config to pass to the Layout
25788      * @param {Field} field1 (optional)
25789      * @param {Field} field2 (optional)
25790      * @param {Field} etc (optional)
25791      * @return Layout The container object
25792      */
25793     container : function(c){
25794         var l = new Roo.form.Layout(c);
25795         this.start(l);
25796         if(arguments.length > 1){ // duplicate code required because of Opera
25797             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25798             this.end();
25799         }
25800         return l;
25801     },
25802
25803     /**
25804      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25805      * @param {Object} container A Roo.form.Layout or subclass of Layout
25806      * @return {Form} this
25807      */
25808     start : function(c){
25809         // cascade label info
25810         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25811         this.active.stack.push(c);
25812         c.ownerCt = this.active;
25813         this.active = c;
25814         return this;
25815     },
25816
25817     /**
25818      * Closes the current open container
25819      * @return {Form} this
25820      */
25821     end : function(){
25822         if(this.active == this.root){
25823             return this;
25824         }
25825         this.active = this.active.ownerCt;
25826         return this;
25827     },
25828
25829     /**
25830      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25831      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25832      * as the label of the field.
25833      * @param {Field} field1
25834      * @param {Field} field2 (optional)
25835      * @param {Field} etc. (optional)
25836      * @return {Form} this
25837      */
25838     add : function(){
25839         this.active.stack.push.apply(this.active.stack, arguments);
25840         this.allItems.push.apply(this.allItems,arguments);
25841         var r = [];
25842         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25843             if(a[i].isFormField){
25844                 r.push(a[i]);
25845             }
25846         }
25847         if(r.length > 0){
25848             Roo.form.Form.superclass.add.apply(this, r);
25849         }
25850         return this;
25851     },
25852     
25853
25854     
25855     
25856     
25857      /**
25858      * Find any element that has been added to a form, using it's ID or name
25859      * This can include framesets, columns etc. along with regular fields..
25860      * @param {String} id - id or name to find.
25861      
25862      * @return {Element} e - or false if nothing found.
25863      */
25864     findbyId : function(id)
25865     {
25866         var ret = false;
25867         if (!id) {
25868             return ret;
25869         }
25870         Roo.each(this.allItems, function(f){
25871             if (f.id == id || f.name == id ){
25872                 ret = f;
25873                 return false;
25874             }
25875         });
25876         return ret;
25877     },
25878
25879     
25880     
25881     /**
25882      * Render this form into the passed container. This should only be called once!
25883      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25884      * @return {Form} this
25885      */
25886     render : function(ct)
25887     {
25888         
25889         
25890         
25891         ct = Roo.get(ct);
25892         var o = this.autoCreate || {
25893             tag: 'form',
25894             method : this.method || 'POST',
25895             id : this.id || Roo.id()
25896         };
25897         this.initEl(ct.createChild(o));
25898
25899         this.root.render(this.el);
25900         
25901        
25902              
25903         this.items.each(function(f){
25904             f.render('x-form-el-'+f.id);
25905         });
25906
25907         if(this.buttons.length > 0){
25908             // tables are required to maintain order and for correct IE layout
25909             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25910                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25911                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25912             }}, null, true);
25913             var tr = tb.getElementsByTagName('tr')[0];
25914             for(var i = 0, len = this.buttons.length; i < len; i++) {
25915                 var b = this.buttons[i];
25916                 var td = document.createElement('td');
25917                 td.className = 'x-form-btn-td';
25918                 b.render(tr.appendChild(td));
25919             }
25920         }
25921         if(this.monitorValid){ // initialize after render
25922             this.startMonitoring();
25923         }
25924         this.fireEvent('rendered', this);
25925         return this;
25926     },
25927
25928     /**
25929      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25930      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25931      * object or a valid Roo.DomHelper element config
25932      * @param {Function} handler The function called when the button is clicked
25933      * @param {Object} scope (optional) The scope of the handler function
25934      * @return {Roo.Button}
25935      */
25936     addButton : function(config, handler, scope){
25937         var bc = {
25938             handler: handler,
25939             scope: scope,
25940             minWidth: this.minButtonWidth,
25941             hideParent:true
25942         };
25943         if(typeof config == "string"){
25944             bc.text = config;
25945         }else{
25946             Roo.apply(bc, config);
25947         }
25948         var btn = new Roo.Button(null, bc);
25949         this.buttons.push(btn);
25950         return btn;
25951     },
25952
25953      /**
25954      * Adds a series of form elements (using the xtype property as the factory method.
25955      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25956      * @param {Object} config 
25957      */
25958     
25959     addxtype : function()
25960     {
25961         var ar = Array.prototype.slice.call(arguments, 0);
25962         var ret = false;
25963         for(var i = 0; i < ar.length; i++) {
25964             if (!ar[i]) {
25965                 continue; // skip -- if this happends something invalid got sent, we 
25966                 // should ignore it, as basically that interface element will not show up
25967                 // and that should be pretty obvious!!
25968             }
25969             
25970             if (Roo.form[ar[i].xtype]) {
25971                 ar[i].form = this;
25972                 var fe = Roo.factory(ar[i], Roo.form);
25973                 if (!ret) {
25974                     ret = fe;
25975                 }
25976                 fe.form = this;
25977                 if (fe.store) {
25978                     fe.store.form = this;
25979                 }
25980                 if (fe.isLayout) {  
25981                          
25982                     this.start(fe);
25983                     this.allItems.push(fe);
25984                     if (fe.items && fe.addxtype) {
25985                         fe.addxtype.apply(fe, fe.items);
25986                         delete fe.items;
25987                     }
25988                      this.end();
25989                     continue;
25990                 }
25991                 
25992                 
25993                  
25994                 this.add(fe);
25995               //  console.log('adding ' + ar[i].xtype);
25996             }
25997             if (ar[i].xtype == 'Button') {  
25998                 //console.log('adding button');
25999                 //console.log(ar[i]);
26000                 this.addButton(ar[i]);
26001                 this.allItems.push(fe);
26002                 continue;
26003             }
26004             
26005             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26006                 alert('end is not supported on xtype any more, use items');
26007             //    this.end();
26008             //    //console.log('adding end');
26009             }
26010             
26011         }
26012         return ret;
26013     },
26014     
26015     /**
26016      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26017      * option "monitorValid"
26018      */
26019     startMonitoring : function(){
26020         if(!this.bound){
26021             this.bound = true;
26022             Roo.TaskMgr.start({
26023                 run : this.bindHandler,
26024                 interval : this.monitorPoll || 200,
26025                 scope: this
26026             });
26027         }
26028     },
26029
26030     /**
26031      * Stops monitoring of the valid state of this form
26032      */
26033     stopMonitoring : function(){
26034         this.bound = false;
26035     },
26036
26037     // private
26038     bindHandler : function(){
26039         if(!this.bound){
26040             return false; // stops binding
26041         }
26042         var valid = true;
26043         this.items.each(function(f){
26044             if(!f.isValid(true)){
26045                 valid = false;
26046                 return false;
26047             }
26048         });
26049         for(var i = 0, len = this.buttons.length; i < len; i++){
26050             var btn = this.buttons[i];
26051             if(btn.formBind === true && btn.disabled === valid){
26052                 btn.setDisabled(!valid);
26053             }
26054         }
26055         this.fireEvent('clientvalidation', this, valid);
26056     }
26057     
26058     
26059     
26060     
26061     
26062     
26063     
26064     
26065 });
26066
26067
26068 // back compat
26069 Roo.Form = Roo.form.Form;
26070 /*
26071  * Based on:
26072  * Ext JS Library 1.1.1
26073  * Copyright(c) 2006-2007, Ext JS, LLC.
26074  *
26075  * Originally Released Under LGPL - original licence link has changed is not relivant.
26076  *
26077  * Fork - LGPL
26078  * <script type="text/javascript">
26079  */
26080
26081 // as we use this in bootstrap.
26082 Roo.namespace('Roo.form');
26083  /**
26084  * @class Roo.form.Action
26085  * Internal Class used to handle form actions
26086  * @constructor
26087  * @param {Roo.form.BasicForm} el The form element or its id
26088  * @param {Object} config Configuration options
26089  */
26090
26091  
26092  
26093 // define the action interface
26094 Roo.form.Action = function(form, options){
26095     this.form = form;
26096     this.options = options || {};
26097 };
26098 /**
26099  * Client Validation Failed
26100  * @const 
26101  */
26102 Roo.form.Action.CLIENT_INVALID = 'client';
26103 /**
26104  * Server Validation Failed
26105  * @const 
26106  */
26107 Roo.form.Action.SERVER_INVALID = 'server';
26108  /**
26109  * Connect to Server Failed
26110  * @const 
26111  */
26112 Roo.form.Action.CONNECT_FAILURE = 'connect';
26113 /**
26114  * Reading Data from Server Failed
26115  * @const 
26116  */
26117 Roo.form.Action.LOAD_FAILURE = 'load';
26118
26119 Roo.form.Action.prototype = {
26120     type : 'default',
26121     failureType : undefined,
26122     response : undefined,
26123     result : undefined,
26124
26125     // interface method
26126     run : function(options){
26127
26128     },
26129
26130     // interface method
26131     success : function(response){
26132
26133     },
26134
26135     // interface method
26136     handleResponse : function(response){
26137
26138     },
26139
26140     // default connection failure
26141     failure : function(response){
26142         
26143         this.response = response;
26144         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26145         this.form.afterAction(this, false);
26146     },
26147
26148     processResponse : function(response){
26149         this.response = response;
26150         if(!response.responseText){
26151             return true;
26152         }
26153         this.result = this.handleResponse(response);
26154         return this.result;
26155     },
26156
26157     // utility functions used internally
26158     getUrl : function(appendParams){
26159         var url = this.options.url || this.form.url || this.form.el.dom.action;
26160         if(appendParams){
26161             var p = this.getParams();
26162             if(p){
26163                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26164             }
26165         }
26166         return url;
26167     },
26168
26169     getMethod : function(){
26170         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26171     },
26172
26173     getParams : function(){
26174         var bp = this.form.baseParams;
26175         var p = this.options.params;
26176         if(p){
26177             if(typeof p == "object"){
26178                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26179             }else if(typeof p == 'string' && bp){
26180                 p += '&' + Roo.urlEncode(bp);
26181             }
26182         }else if(bp){
26183             p = Roo.urlEncode(bp);
26184         }
26185         return p;
26186     },
26187
26188     createCallback : function(){
26189         return {
26190             success: this.success,
26191             failure: this.failure,
26192             scope: this,
26193             timeout: (this.form.timeout*1000),
26194             upload: this.form.fileUpload ? this.success : undefined
26195         };
26196     }
26197 };
26198
26199 Roo.form.Action.Submit = function(form, options){
26200     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26201 };
26202
26203 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26204     type : 'submit',
26205
26206     haveProgress : false,
26207     uploadComplete : false,
26208     
26209     // uploadProgress indicator.
26210     uploadProgress : function()
26211     {
26212         if (!this.form.progressUrl) {
26213             return;
26214         }
26215         
26216         if (!this.haveProgress) {
26217             Roo.MessageBox.progress("Uploading", "Uploading");
26218         }
26219         if (this.uploadComplete) {
26220            Roo.MessageBox.hide();
26221            return;
26222         }
26223         
26224         this.haveProgress = true;
26225    
26226         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26227         
26228         var c = new Roo.data.Connection();
26229         c.request({
26230             url : this.form.progressUrl,
26231             params: {
26232                 id : uid
26233             },
26234             method: 'GET',
26235             success : function(req){
26236                //console.log(data);
26237                 var rdata = false;
26238                 var edata;
26239                 try  {
26240                    rdata = Roo.decode(req.responseText)
26241                 } catch (e) {
26242                     Roo.log("Invalid data from server..");
26243                     Roo.log(edata);
26244                     return;
26245                 }
26246                 if (!rdata || !rdata.success) {
26247                     Roo.log(rdata);
26248                     Roo.MessageBox.alert(Roo.encode(rdata));
26249                     return;
26250                 }
26251                 var data = rdata.data;
26252                 
26253                 if (this.uploadComplete) {
26254                    Roo.MessageBox.hide();
26255                    return;
26256                 }
26257                    
26258                 if (data){
26259                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26260                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26261                     );
26262                 }
26263                 this.uploadProgress.defer(2000,this);
26264             },
26265        
26266             failure: function(data) {
26267                 Roo.log('progress url failed ');
26268                 Roo.log(data);
26269             },
26270             scope : this
26271         });
26272            
26273     },
26274     
26275     
26276     run : function()
26277     {
26278         // run get Values on the form, so it syncs any secondary forms.
26279         this.form.getValues();
26280         
26281         var o = this.options;
26282         var method = this.getMethod();
26283         var isPost = method == 'POST';
26284         if(o.clientValidation === false || this.form.isValid()){
26285             
26286             if (this.form.progressUrl) {
26287                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26288                     (new Date() * 1) + '' + Math.random());
26289                     
26290             } 
26291             
26292             
26293             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26294                 form:this.form.el.dom,
26295                 url:this.getUrl(!isPost),
26296                 method: method,
26297                 params:isPost ? this.getParams() : null,
26298                 isUpload: this.form.fileUpload,
26299                 formData : this.form.formData
26300             }));
26301             
26302             this.uploadProgress();
26303
26304         }else if (o.clientValidation !== false){ // client validation failed
26305             this.failureType = Roo.form.Action.CLIENT_INVALID;
26306             this.form.afterAction(this, false);
26307         }
26308     },
26309
26310     success : function(response)
26311     {
26312         this.uploadComplete= true;
26313         if (this.haveProgress) {
26314             Roo.MessageBox.hide();
26315         }
26316         
26317         
26318         var result = this.processResponse(response);
26319         if(result === true || result.success){
26320             this.form.afterAction(this, true);
26321             return;
26322         }
26323         if(result.errors){
26324             this.form.markInvalid(result.errors);
26325             this.failureType = Roo.form.Action.SERVER_INVALID;
26326         }
26327         this.form.afterAction(this, false);
26328     },
26329     failure : function(response)
26330     {
26331         this.uploadComplete= true;
26332         if (this.haveProgress) {
26333             Roo.MessageBox.hide();
26334         }
26335         
26336         this.response = response;
26337         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26338         this.form.afterAction(this, false);
26339     },
26340     
26341     handleResponse : function(response){
26342         if(this.form.errorReader){
26343             var rs = this.form.errorReader.read(response);
26344             var errors = [];
26345             if(rs.records){
26346                 for(var i = 0, len = rs.records.length; i < len; i++) {
26347                     var r = rs.records[i];
26348                     errors[i] = r.data;
26349                 }
26350             }
26351             if(errors.length < 1){
26352                 errors = null;
26353             }
26354             return {
26355                 success : rs.success,
26356                 errors : errors
26357             };
26358         }
26359         var ret = false;
26360         try {
26361             ret = Roo.decode(response.responseText);
26362         } catch (e) {
26363             ret = {
26364                 success: false,
26365                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26366                 errors : []
26367             };
26368         }
26369         return ret;
26370         
26371     }
26372 });
26373
26374
26375 Roo.form.Action.Load = function(form, options){
26376     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26377     this.reader = this.form.reader;
26378 };
26379
26380 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26381     type : 'load',
26382
26383     run : function(){
26384         
26385         Roo.Ajax.request(Roo.apply(
26386                 this.createCallback(), {
26387                     method:this.getMethod(),
26388                     url:this.getUrl(false),
26389                     params:this.getParams()
26390         }));
26391     },
26392
26393     success : function(response){
26394         
26395         var result = this.processResponse(response);
26396         if(result === true || !result.success || !result.data){
26397             this.failureType = Roo.form.Action.LOAD_FAILURE;
26398             this.form.afterAction(this, false);
26399             return;
26400         }
26401         this.form.clearInvalid();
26402         this.form.setValues(result.data);
26403         this.form.afterAction(this, true);
26404     },
26405
26406     handleResponse : function(response){
26407         if(this.form.reader){
26408             var rs = this.form.reader.read(response);
26409             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26410             return {
26411                 success : rs.success,
26412                 data : data
26413             };
26414         }
26415         return Roo.decode(response.responseText);
26416     }
26417 });
26418
26419 Roo.form.Action.ACTION_TYPES = {
26420     'load' : Roo.form.Action.Load,
26421     'submit' : Roo.form.Action.Submit
26422 };/*
26423  * Based on:
26424  * Ext JS Library 1.1.1
26425  * Copyright(c) 2006-2007, Ext JS, LLC.
26426  *
26427  * Originally Released Under LGPL - original licence link has changed is not relivant.
26428  *
26429  * Fork - LGPL
26430  * <script type="text/javascript">
26431  */
26432  
26433 /**
26434  * @class Roo.form.Layout
26435  * @extends Roo.Component
26436  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26437  * @constructor
26438  * @param {Object} config Configuration options
26439  */
26440 Roo.form.Layout = function(config){
26441     var xitems = [];
26442     if (config.items) {
26443         xitems = config.items;
26444         delete config.items;
26445     }
26446     Roo.form.Layout.superclass.constructor.call(this, config);
26447     this.stack = [];
26448     Roo.each(xitems, this.addxtype, this);
26449      
26450 };
26451
26452 Roo.extend(Roo.form.Layout, Roo.Component, {
26453     /**
26454      * @cfg {String/Object} autoCreate
26455      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26456      */
26457     /**
26458      * @cfg {String/Object/Function} style
26459      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26460      * a function which returns such a specification.
26461      */
26462     /**
26463      * @cfg {String} labelAlign
26464      * Valid values are "left," "top" and "right" (defaults to "left")
26465      */
26466     /**
26467      * @cfg {Number} labelWidth
26468      * Fixed width in pixels of all field labels (defaults to undefined)
26469      */
26470     /**
26471      * @cfg {Boolean} clear
26472      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26473      */
26474     clear : true,
26475     /**
26476      * @cfg {String} labelSeparator
26477      * The separator to use after field labels (defaults to ':')
26478      */
26479     labelSeparator : ':',
26480     /**
26481      * @cfg {Boolean} hideLabels
26482      * True to suppress the display of field labels in this layout (defaults to false)
26483      */
26484     hideLabels : false,
26485
26486     // private
26487     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26488     
26489     isLayout : true,
26490     
26491     // private
26492     onRender : function(ct, position){
26493         if(this.el){ // from markup
26494             this.el = Roo.get(this.el);
26495         }else {  // generate
26496             var cfg = this.getAutoCreate();
26497             this.el = ct.createChild(cfg, position);
26498         }
26499         if(this.style){
26500             this.el.applyStyles(this.style);
26501         }
26502         if(this.labelAlign){
26503             this.el.addClass('x-form-label-'+this.labelAlign);
26504         }
26505         if(this.hideLabels){
26506             this.labelStyle = "display:none";
26507             this.elementStyle = "padding-left:0;";
26508         }else{
26509             if(typeof this.labelWidth == 'number'){
26510                 this.labelStyle = "width:"+this.labelWidth+"px;";
26511                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26512             }
26513             if(this.labelAlign == 'top'){
26514                 this.labelStyle = "width:auto;";
26515                 this.elementStyle = "padding-left:0;";
26516             }
26517         }
26518         var stack = this.stack;
26519         var slen = stack.length;
26520         if(slen > 0){
26521             if(!this.fieldTpl){
26522                 var t = new Roo.Template(
26523                     '<div class="x-form-item {5}">',
26524                         '<label for="{0}" style="{2}">{1}{4}</label>',
26525                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26526                         '</div>',
26527                     '</div><div class="x-form-clear-left"></div>'
26528                 );
26529                 t.disableFormats = true;
26530                 t.compile();
26531                 Roo.form.Layout.prototype.fieldTpl = t;
26532             }
26533             for(var i = 0; i < slen; i++) {
26534                 if(stack[i].isFormField){
26535                     this.renderField(stack[i]);
26536                 }else{
26537                     this.renderComponent(stack[i]);
26538                 }
26539             }
26540         }
26541         if(this.clear){
26542             this.el.createChild({cls:'x-form-clear'});
26543         }
26544     },
26545
26546     // private
26547     renderField : function(f){
26548         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26549                f.id, //0
26550                f.fieldLabel, //1
26551                f.labelStyle||this.labelStyle||'', //2
26552                this.elementStyle||'', //3
26553                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26554                f.itemCls||this.itemCls||''  //5
26555        ], true).getPrevSibling());
26556     },
26557
26558     // private
26559     renderComponent : function(c){
26560         c.render(c.isLayout ? this.el : this.el.createChild());    
26561     },
26562     /**
26563      * Adds a object form elements (using the xtype property as the factory method.)
26564      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26565      * @param {Object} config 
26566      */
26567     addxtype : function(o)
26568     {
26569         // create the lement.
26570         o.form = this.form;
26571         var fe = Roo.factory(o, Roo.form);
26572         this.form.allItems.push(fe);
26573         this.stack.push(fe);
26574         
26575         if (fe.isFormField) {
26576             this.form.items.add(fe);
26577         }
26578          
26579         return fe;
26580     }
26581 });
26582
26583 /**
26584  * @class Roo.form.Column
26585  * @extends Roo.form.Layout
26586  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26587  * @constructor
26588  * @param {Object} config Configuration options
26589  */
26590 Roo.form.Column = function(config){
26591     Roo.form.Column.superclass.constructor.call(this, config);
26592 };
26593
26594 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26595     /**
26596      * @cfg {Number/String} width
26597      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26598      */
26599     /**
26600      * @cfg {String/Object} autoCreate
26601      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26602      */
26603
26604     // private
26605     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26606
26607     // private
26608     onRender : function(ct, position){
26609         Roo.form.Column.superclass.onRender.call(this, ct, position);
26610         if(this.width){
26611             this.el.setWidth(this.width);
26612         }
26613     }
26614 });
26615
26616
26617 /**
26618  * @class Roo.form.Row
26619  * @extends Roo.form.Layout
26620  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26621  * @constructor
26622  * @param {Object} config Configuration options
26623  */
26624
26625  
26626 Roo.form.Row = function(config){
26627     Roo.form.Row.superclass.constructor.call(this, config);
26628 };
26629  
26630 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26631       /**
26632      * @cfg {Number/String} width
26633      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26634      */
26635     /**
26636      * @cfg {Number/String} height
26637      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26638      */
26639     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26640     
26641     padWidth : 20,
26642     // private
26643     onRender : function(ct, position){
26644         //console.log('row render');
26645         if(!this.rowTpl){
26646             var t = new Roo.Template(
26647                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26648                     '<label for="{0}" style="{2}">{1}{4}</label>',
26649                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26650                     '</div>',
26651                 '</div>'
26652             );
26653             t.disableFormats = true;
26654             t.compile();
26655             Roo.form.Layout.prototype.rowTpl = t;
26656         }
26657         this.fieldTpl = this.rowTpl;
26658         
26659         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26660         var labelWidth = 100;
26661         
26662         if ((this.labelAlign != 'top')) {
26663             if (typeof this.labelWidth == 'number') {
26664                 labelWidth = this.labelWidth
26665             }
26666             this.padWidth =  20 + labelWidth;
26667             
26668         }
26669         
26670         Roo.form.Column.superclass.onRender.call(this, ct, position);
26671         if(this.width){
26672             this.el.setWidth(this.width);
26673         }
26674         if(this.height){
26675             this.el.setHeight(this.height);
26676         }
26677     },
26678     
26679     // private
26680     renderField : function(f){
26681         f.fieldEl = this.fieldTpl.append(this.el, [
26682                f.id, f.fieldLabel,
26683                f.labelStyle||this.labelStyle||'',
26684                this.elementStyle||'',
26685                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26686                f.itemCls||this.itemCls||'',
26687                f.width ? f.width + this.padWidth : 160 + this.padWidth
26688        ],true);
26689     }
26690 });
26691  
26692
26693 /**
26694  * @class Roo.form.FieldSet
26695  * @extends Roo.form.Layout
26696  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26697  * @constructor
26698  * @param {Object} config Configuration options
26699  */
26700 Roo.form.FieldSet = function(config){
26701     Roo.form.FieldSet.superclass.constructor.call(this, config);
26702 };
26703
26704 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26705     /**
26706      * @cfg {String} legend
26707      * The text to display as the legend for the FieldSet (defaults to '')
26708      */
26709     /**
26710      * @cfg {String/Object} autoCreate
26711      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26712      */
26713
26714     // private
26715     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26716
26717     // private
26718     onRender : function(ct, position){
26719         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26720         if(this.legend){
26721             this.setLegend(this.legend);
26722         }
26723     },
26724
26725     // private
26726     setLegend : function(text){
26727         if(this.rendered){
26728             this.el.child('legend').update(text);
26729         }
26730     }
26731 });/*
26732  * Based on:
26733  * Ext JS Library 1.1.1
26734  * Copyright(c) 2006-2007, Ext JS, LLC.
26735  *
26736  * Originally Released Under LGPL - original licence link has changed is not relivant.
26737  *
26738  * Fork - LGPL
26739  * <script type="text/javascript">
26740  */
26741 /**
26742  * @class Roo.form.VTypes
26743  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26744  * @singleton
26745  */
26746 Roo.form.VTypes = function(){
26747     // closure these in so they are only created once.
26748     var alpha = /^[a-zA-Z_]+$/;
26749     var alphanum = /^[a-zA-Z0-9_]+$/;
26750     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26751     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26752
26753     // All these messages and functions are configurable
26754     return {
26755         /**
26756          * The function used to validate email addresses
26757          * @param {String} value The email address
26758          */
26759         'email' : function(v){
26760             return email.test(v);
26761         },
26762         /**
26763          * The error text to display when the email validation function returns false
26764          * @type String
26765          */
26766         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26767         /**
26768          * The keystroke filter mask to be applied on email input
26769          * @type RegExp
26770          */
26771         'emailMask' : /[a-z0-9_\.\-@]/i,
26772
26773         /**
26774          * The function used to validate URLs
26775          * @param {String} value The URL
26776          */
26777         'url' : function(v){
26778             return url.test(v);
26779         },
26780         /**
26781          * The error text to display when the url validation function returns false
26782          * @type String
26783          */
26784         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26785         
26786         /**
26787          * The function used to validate alpha values
26788          * @param {String} value The value
26789          */
26790         'alpha' : function(v){
26791             return alpha.test(v);
26792         },
26793         /**
26794          * The error text to display when the alpha validation function returns false
26795          * @type String
26796          */
26797         'alphaText' : 'This field should only contain letters and _',
26798         /**
26799          * The keystroke filter mask to be applied on alpha input
26800          * @type RegExp
26801          */
26802         'alphaMask' : /[a-z_]/i,
26803
26804         /**
26805          * The function used to validate alphanumeric values
26806          * @param {String} value The value
26807          */
26808         'alphanum' : function(v){
26809             return alphanum.test(v);
26810         },
26811         /**
26812          * The error text to display when the alphanumeric validation function returns false
26813          * @type String
26814          */
26815         'alphanumText' : 'This field should only contain letters, numbers and _',
26816         /**
26817          * The keystroke filter mask to be applied on alphanumeric input
26818          * @type RegExp
26819          */
26820         'alphanumMask' : /[a-z0-9_]/i
26821     };
26822 }();//<script type="text/javascript">
26823
26824 /**
26825  * @class Roo.form.FCKeditor
26826  * @extends Roo.form.TextArea
26827  * Wrapper around the FCKEditor http://www.fckeditor.net
26828  * @constructor
26829  * Creates a new FCKeditor
26830  * @param {Object} config Configuration options
26831  */
26832 Roo.form.FCKeditor = function(config){
26833     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26834     this.addEvents({
26835          /**
26836          * @event editorinit
26837          * Fired when the editor is initialized - you can add extra handlers here..
26838          * @param {FCKeditor} this
26839          * @param {Object} the FCK object.
26840          */
26841         editorinit : true
26842     });
26843     
26844     
26845 };
26846 Roo.form.FCKeditor.editors = { };
26847 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26848 {
26849     //defaultAutoCreate : {
26850     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26851     //},
26852     // private
26853     /**
26854      * @cfg {Object} fck options - see fck manual for details.
26855      */
26856     fckconfig : false,
26857     
26858     /**
26859      * @cfg {Object} fck toolbar set (Basic or Default)
26860      */
26861     toolbarSet : 'Basic',
26862     /**
26863      * @cfg {Object} fck BasePath
26864      */ 
26865     basePath : '/fckeditor/',
26866     
26867     
26868     frame : false,
26869     
26870     value : '',
26871     
26872    
26873     onRender : function(ct, position)
26874     {
26875         if(!this.el){
26876             this.defaultAutoCreate = {
26877                 tag: "textarea",
26878                 style:"width:300px;height:60px;",
26879                 autocomplete: "new-password"
26880             };
26881         }
26882         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26883         /*
26884         if(this.grow){
26885             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26886             if(this.preventScrollbars){
26887                 this.el.setStyle("overflow", "hidden");
26888             }
26889             this.el.setHeight(this.growMin);
26890         }
26891         */
26892         //console.log('onrender' + this.getId() );
26893         Roo.form.FCKeditor.editors[this.getId()] = this;
26894          
26895
26896         this.replaceTextarea() ;
26897         
26898     },
26899     
26900     getEditor : function() {
26901         return this.fckEditor;
26902     },
26903     /**
26904      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26905      * @param {Mixed} value The value to set
26906      */
26907     
26908     
26909     setValue : function(value)
26910     {
26911         //console.log('setValue: ' + value);
26912         
26913         if(typeof(value) == 'undefined') { // not sure why this is happending...
26914             return;
26915         }
26916         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26917         
26918         //if(!this.el || !this.getEditor()) {
26919         //    this.value = value;
26920             //this.setValue.defer(100,this,[value]);    
26921         //    return;
26922         //} 
26923         
26924         if(!this.getEditor()) {
26925             return;
26926         }
26927         
26928         this.getEditor().SetData(value);
26929         
26930         //
26931
26932     },
26933
26934     /**
26935      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26936      * @return {Mixed} value The field value
26937      */
26938     getValue : function()
26939     {
26940         
26941         if (this.frame && this.frame.dom.style.display == 'none') {
26942             return Roo.form.FCKeditor.superclass.getValue.call(this);
26943         }
26944         
26945         if(!this.el || !this.getEditor()) {
26946            
26947            // this.getValue.defer(100,this); 
26948             return this.value;
26949         }
26950        
26951         
26952         var value=this.getEditor().GetData();
26953         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26954         return Roo.form.FCKeditor.superclass.getValue.call(this);
26955         
26956
26957     },
26958
26959     /**
26960      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26961      * @return {Mixed} value The field value
26962      */
26963     getRawValue : function()
26964     {
26965         if (this.frame && this.frame.dom.style.display == 'none') {
26966             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26967         }
26968         
26969         if(!this.el || !this.getEditor()) {
26970             //this.getRawValue.defer(100,this); 
26971             return this.value;
26972             return;
26973         }
26974         
26975         
26976         
26977         var value=this.getEditor().GetData();
26978         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26979         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26980          
26981     },
26982     
26983     setSize : function(w,h) {
26984         
26985         
26986         
26987         //if (this.frame && this.frame.dom.style.display == 'none') {
26988         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26989         //    return;
26990         //}
26991         //if(!this.el || !this.getEditor()) {
26992         //    this.setSize.defer(100,this, [w,h]); 
26993         //    return;
26994         //}
26995         
26996         
26997         
26998         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26999         
27000         this.frame.dom.setAttribute('width', w);
27001         this.frame.dom.setAttribute('height', h);
27002         this.frame.setSize(w,h);
27003         
27004     },
27005     
27006     toggleSourceEdit : function(value) {
27007         
27008       
27009          
27010         this.el.dom.style.display = value ? '' : 'none';
27011         this.frame.dom.style.display = value ?  'none' : '';
27012         
27013     },
27014     
27015     
27016     focus: function(tag)
27017     {
27018         if (this.frame.dom.style.display == 'none') {
27019             return Roo.form.FCKeditor.superclass.focus.call(this);
27020         }
27021         if(!this.el || !this.getEditor()) {
27022             this.focus.defer(100,this, [tag]); 
27023             return;
27024         }
27025         
27026         
27027         
27028         
27029         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27030         this.getEditor().Focus();
27031         if (tgs.length) {
27032             if (!this.getEditor().Selection.GetSelection()) {
27033                 this.focus.defer(100,this, [tag]); 
27034                 return;
27035             }
27036             
27037             
27038             var r = this.getEditor().EditorDocument.createRange();
27039             r.setStart(tgs[0],0);
27040             r.setEnd(tgs[0],0);
27041             this.getEditor().Selection.GetSelection().removeAllRanges();
27042             this.getEditor().Selection.GetSelection().addRange(r);
27043             this.getEditor().Focus();
27044         }
27045         
27046     },
27047     
27048     
27049     
27050     replaceTextarea : function()
27051     {
27052         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27053             return ;
27054         }
27055         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27056         //{
27057             // We must check the elements firstly using the Id and then the name.
27058         var oTextarea = document.getElementById( this.getId() );
27059         
27060         var colElementsByName = document.getElementsByName( this.getId() ) ;
27061          
27062         oTextarea.style.display = 'none' ;
27063
27064         if ( oTextarea.tabIndex ) {            
27065             this.TabIndex = oTextarea.tabIndex ;
27066         }
27067         
27068         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27069         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27070         this.frame = Roo.get(this.getId() + '___Frame')
27071     },
27072     
27073     _getConfigHtml : function()
27074     {
27075         var sConfig = '' ;
27076
27077         for ( var o in this.fckconfig ) {
27078             sConfig += sConfig.length > 0  ? '&amp;' : '';
27079             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27080         }
27081
27082         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27083     },
27084     
27085     
27086     _getIFrameHtml : function()
27087     {
27088         var sFile = 'fckeditor.html' ;
27089         /* no idea what this is about..
27090         try
27091         {
27092             if ( (/fcksource=true/i).test( window.top.location.search ) )
27093                 sFile = 'fckeditor.original.html' ;
27094         }
27095         catch (e) { 
27096         */
27097
27098         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27099         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27100         
27101         
27102         var html = '<iframe id="' + this.getId() +
27103             '___Frame" src="' + sLink +
27104             '" width="' + this.width +
27105             '" height="' + this.height + '"' +
27106             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27107             ' frameborder="0" scrolling="no"></iframe>' ;
27108
27109         return html ;
27110     },
27111     
27112     _insertHtmlBefore : function( html, element )
27113     {
27114         if ( element.insertAdjacentHTML )       {
27115             // IE
27116             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27117         } else { // Gecko
27118             var oRange = document.createRange() ;
27119             oRange.setStartBefore( element ) ;
27120             var oFragment = oRange.createContextualFragment( html );
27121             element.parentNode.insertBefore( oFragment, element ) ;
27122         }
27123     }
27124     
27125     
27126   
27127     
27128     
27129     
27130     
27131
27132 });
27133
27134 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27135
27136 function FCKeditor_OnComplete(editorInstance){
27137     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27138     f.fckEditor = editorInstance;
27139     //console.log("loaded");
27140     f.fireEvent('editorinit', f, editorInstance);
27141
27142   
27143
27144  
27145
27146
27147
27148
27149
27150
27151
27152
27153
27154
27155
27156
27157
27158
27159
27160 //<script type="text/javascript">
27161 /**
27162  * @class Roo.form.GridField
27163  * @extends Roo.form.Field
27164  * Embed a grid (or editable grid into a form)
27165  * STATUS ALPHA
27166  * 
27167  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27168  * it needs 
27169  * xgrid.store = Roo.data.Store
27170  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27171  * xgrid.store.reader = Roo.data.JsonReader 
27172  * 
27173  * 
27174  * @constructor
27175  * Creates a new GridField
27176  * @param {Object} config Configuration options
27177  */
27178 Roo.form.GridField = function(config){
27179     Roo.form.GridField.superclass.constructor.call(this, config);
27180      
27181 };
27182
27183 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27184     /**
27185      * @cfg {Number} width  - used to restrict width of grid..
27186      */
27187     width : 100,
27188     /**
27189      * @cfg {Number} height - used to restrict height of grid..
27190      */
27191     height : 50,
27192      /**
27193      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27194          * 
27195          *}
27196      */
27197     xgrid : false, 
27198     /**
27199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27200      * {tag: "input", type: "checkbox", autocomplete: "off"})
27201      */
27202    // defaultAutoCreate : { tag: 'div' },
27203     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27204     /**
27205      * @cfg {String} addTitle Text to include for adding a title.
27206      */
27207     addTitle : false,
27208     //
27209     onResize : function(){
27210         Roo.form.Field.superclass.onResize.apply(this, arguments);
27211     },
27212
27213     initEvents : function(){
27214         // Roo.form.Checkbox.superclass.initEvents.call(this);
27215         // has no events...
27216        
27217     },
27218
27219
27220     getResizeEl : function(){
27221         return this.wrap;
27222     },
27223
27224     getPositionEl : function(){
27225         return this.wrap;
27226     },
27227
27228     // private
27229     onRender : function(ct, position){
27230         
27231         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27232         var style = this.style;
27233         delete this.style;
27234         
27235         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27236         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27237         this.viewEl = this.wrap.createChild({ tag: 'div' });
27238         if (style) {
27239             this.viewEl.applyStyles(style);
27240         }
27241         if (this.width) {
27242             this.viewEl.setWidth(this.width);
27243         }
27244         if (this.height) {
27245             this.viewEl.setHeight(this.height);
27246         }
27247         //if(this.inputValue !== undefined){
27248         //this.setValue(this.value);
27249         
27250         
27251         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27252         
27253         
27254         this.grid.render();
27255         this.grid.getDataSource().on('remove', this.refreshValue, this);
27256         this.grid.getDataSource().on('update', this.refreshValue, this);
27257         this.grid.on('afteredit', this.refreshValue, this);
27258  
27259     },
27260      
27261     
27262     /**
27263      * Sets the value of the item. 
27264      * @param {String} either an object  or a string..
27265      */
27266     setValue : function(v){
27267         //this.value = v;
27268         v = v || []; // empty set..
27269         // this does not seem smart - it really only affects memoryproxy grids..
27270         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27271             var ds = this.grid.getDataSource();
27272             // assumes a json reader..
27273             var data = {}
27274             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27275             ds.loadData( data);
27276         }
27277         // clear selection so it does not get stale.
27278         if (this.grid.sm) { 
27279             this.grid.sm.clearSelections();
27280         }
27281         
27282         Roo.form.GridField.superclass.setValue.call(this, v);
27283         this.refreshValue();
27284         // should load data in the grid really....
27285     },
27286     
27287     // private
27288     refreshValue: function() {
27289          var val = [];
27290         this.grid.getDataSource().each(function(r) {
27291             val.push(r.data);
27292         });
27293         this.el.dom.value = Roo.encode(val);
27294     }
27295     
27296      
27297     
27298     
27299 });/*
27300  * Based on:
27301  * Ext JS Library 1.1.1
27302  * Copyright(c) 2006-2007, Ext JS, LLC.
27303  *
27304  * Originally Released Under LGPL - original licence link has changed is not relivant.
27305  *
27306  * Fork - LGPL
27307  * <script type="text/javascript">
27308  */
27309 /**
27310  * @class Roo.form.DisplayField
27311  * @extends Roo.form.Field
27312  * A generic Field to display non-editable data.
27313  * @cfg {Boolean} closable (true|false) default false
27314  * @constructor
27315  * Creates a new Display Field item.
27316  * @param {Object} config Configuration options
27317  */
27318 Roo.form.DisplayField = function(config){
27319     Roo.form.DisplayField.superclass.constructor.call(this, config);
27320     
27321     this.addEvents({
27322         /**
27323          * @event close
27324          * Fires after the click the close btn
27325              * @param {Roo.form.DisplayField} this
27326              */
27327         close : true
27328     });
27329 };
27330
27331 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27332     inputType:      'hidden',
27333     allowBlank:     true,
27334     readOnly:         true,
27335     
27336  
27337     /**
27338      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27339      */
27340     focusClass : undefined,
27341     /**
27342      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27343      */
27344     fieldClass: 'x-form-field',
27345     
27346      /**
27347      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27348      */
27349     valueRenderer: undefined,
27350     
27351     width: 100,
27352     /**
27353      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27354      * {tag: "input", type: "checkbox", autocomplete: "off"})
27355      */
27356      
27357  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27358  
27359     closable : false,
27360     
27361     onResize : function(){
27362         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27363         
27364     },
27365
27366     initEvents : function(){
27367         // Roo.form.Checkbox.superclass.initEvents.call(this);
27368         // has no events...
27369         
27370         if(this.closable){
27371             this.closeEl.on('click', this.onClose, this);
27372         }
27373        
27374     },
27375
27376
27377     getResizeEl : function(){
27378         return this.wrap;
27379     },
27380
27381     getPositionEl : function(){
27382         return this.wrap;
27383     },
27384
27385     // private
27386     onRender : function(ct, position){
27387         
27388         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27389         //if(this.inputValue !== undefined){
27390         this.wrap = this.el.wrap();
27391         
27392         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27393         
27394         if(this.closable){
27395             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27396         }
27397         
27398         if (this.bodyStyle) {
27399             this.viewEl.applyStyles(this.bodyStyle);
27400         }
27401         //this.viewEl.setStyle('padding', '2px');
27402         
27403         this.setValue(this.value);
27404         
27405     },
27406 /*
27407     // private
27408     initValue : Roo.emptyFn,
27409
27410   */
27411
27412         // private
27413     onClick : function(){
27414         
27415     },
27416
27417     /**
27418      * Sets the checked state of the checkbox.
27419      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27420      */
27421     setValue : function(v){
27422         this.value = v;
27423         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27424         // this might be called before we have a dom element..
27425         if (!this.viewEl) {
27426             return;
27427         }
27428         this.viewEl.dom.innerHTML = html;
27429         Roo.form.DisplayField.superclass.setValue.call(this, v);
27430
27431     },
27432     
27433     onClose : function(e)
27434     {
27435         e.preventDefault();
27436         
27437         this.fireEvent('close', this);
27438     }
27439 });/*
27440  * 
27441  * Licence- LGPL
27442  * 
27443  */
27444
27445 /**
27446  * @class Roo.form.DayPicker
27447  * @extends Roo.form.Field
27448  * A Day picker show [M] [T] [W] ....
27449  * @constructor
27450  * Creates a new Day Picker
27451  * @param {Object} config Configuration options
27452  */
27453 Roo.form.DayPicker= function(config){
27454     Roo.form.DayPicker.superclass.constructor.call(this, config);
27455      
27456 };
27457
27458 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27459     /**
27460      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27461      */
27462     focusClass : undefined,
27463     /**
27464      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27465      */
27466     fieldClass: "x-form-field",
27467    
27468     /**
27469      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27470      * {tag: "input", type: "checkbox", autocomplete: "off"})
27471      */
27472     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27473     
27474    
27475     actionMode : 'viewEl', 
27476     //
27477     // private
27478  
27479     inputType : 'hidden',
27480     
27481      
27482     inputElement: false, // real input element?
27483     basedOn: false, // ????
27484     
27485     isFormField: true, // not sure where this is needed!!!!
27486
27487     onResize : function(){
27488         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27489         if(!this.boxLabel){
27490             this.el.alignTo(this.wrap, 'c-c');
27491         }
27492     },
27493
27494     initEvents : function(){
27495         Roo.form.Checkbox.superclass.initEvents.call(this);
27496         this.el.on("click", this.onClick,  this);
27497         this.el.on("change", this.onClick,  this);
27498     },
27499
27500
27501     getResizeEl : function(){
27502         return this.wrap;
27503     },
27504
27505     getPositionEl : function(){
27506         return this.wrap;
27507     },
27508
27509     
27510     // private
27511     onRender : function(ct, position){
27512         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27513        
27514         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27515         
27516         var r1 = '<table><tr>';
27517         var r2 = '<tr class="x-form-daypick-icons">';
27518         for (var i=0; i < 7; i++) {
27519             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27520             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27521         }
27522         
27523         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27524         viewEl.select('img').on('click', this.onClick, this);
27525         this.viewEl = viewEl;   
27526         
27527         
27528         // this will not work on Chrome!!!
27529         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27530         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27531         
27532         
27533           
27534
27535     },
27536
27537     // private
27538     initValue : Roo.emptyFn,
27539
27540     /**
27541      * Returns the checked state of the checkbox.
27542      * @return {Boolean} True if checked, else false
27543      */
27544     getValue : function(){
27545         return this.el.dom.value;
27546         
27547     },
27548
27549         // private
27550     onClick : function(e){ 
27551         //this.setChecked(!this.checked);
27552         Roo.get(e.target).toggleClass('x-menu-item-checked');
27553         this.refreshValue();
27554         //if(this.el.dom.checked != this.checked){
27555         //    this.setValue(this.el.dom.checked);
27556        // }
27557     },
27558     
27559     // private
27560     refreshValue : function()
27561     {
27562         var val = '';
27563         this.viewEl.select('img',true).each(function(e,i,n)  {
27564             val += e.is(".x-menu-item-checked") ? String(n) : '';
27565         });
27566         this.setValue(val, true);
27567     },
27568
27569     /**
27570      * Sets the checked state of the checkbox.
27571      * On is always based on a string comparison between inputValue and the param.
27572      * @param {Boolean/String} value - the value to set 
27573      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27574      */
27575     setValue : function(v,suppressEvent){
27576         if (!this.el.dom) {
27577             return;
27578         }
27579         var old = this.el.dom.value ;
27580         this.el.dom.value = v;
27581         if (suppressEvent) {
27582             return ;
27583         }
27584          
27585         // update display..
27586         this.viewEl.select('img',true).each(function(e,i,n)  {
27587             
27588             var on = e.is(".x-menu-item-checked");
27589             var newv = v.indexOf(String(n)) > -1;
27590             if (on != newv) {
27591                 e.toggleClass('x-menu-item-checked');
27592             }
27593             
27594         });
27595         
27596         
27597         this.fireEvent('change', this, v, old);
27598         
27599         
27600     },
27601    
27602     // handle setting of hidden value by some other method!!?!?
27603     setFromHidden: function()
27604     {
27605         if(!this.el){
27606             return;
27607         }
27608         //console.log("SET FROM HIDDEN");
27609         //alert('setFrom hidden');
27610         this.setValue(this.el.dom.value);
27611     },
27612     
27613     onDestroy : function()
27614     {
27615         if(this.viewEl){
27616             Roo.get(this.viewEl).remove();
27617         }
27618          
27619         Roo.form.DayPicker.superclass.onDestroy.call(this);
27620     }
27621
27622 });/*
27623  * RooJS Library 1.1.1
27624  * Copyright(c) 2008-2011  Alan Knowles
27625  *
27626  * License - LGPL
27627  */
27628  
27629
27630 /**
27631  * @class Roo.form.ComboCheck
27632  * @extends Roo.form.ComboBox
27633  * A combobox for multiple select items.
27634  *
27635  * FIXME - could do with a reset button..
27636  * 
27637  * @constructor
27638  * Create a new ComboCheck
27639  * @param {Object} config Configuration options
27640  */
27641 Roo.form.ComboCheck = function(config){
27642     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27643     // should verify some data...
27644     // like
27645     // hiddenName = required..
27646     // displayField = required
27647     // valudField == required
27648     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27649     var _t = this;
27650     Roo.each(req, function(e) {
27651         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27652             throw "Roo.form.ComboCheck : missing value for: " + e;
27653         }
27654     });
27655     
27656     
27657 };
27658
27659 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27660      
27661      
27662     editable : false,
27663      
27664     selectedClass: 'x-menu-item-checked', 
27665     
27666     // private
27667     onRender : function(ct, position){
27668         var _t = this;
27669         
27670         
27671         
27672         if(!this.tpl){
27673             var cls = 'x-combo-list';
27674
27675             
27676             this.tpl =  new Roo.Template({
27677                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27678                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27679                    '<span>{' + this.displayField + '}</span>' +
27680                     '</div>' 
27681                 
27682             });
27683         }
27684  
27685         
27686         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27687         this.view.singleSelect = false;
27688         this.view.multiSelect = true;
27689         this.view.toggleSelect = true;
27690         this.pageTb.add(new Roo.Toolbar.Fill(), {
27691             
27692             text: 'Done',
27693             handler: function()
27694             {
27695                 _t.collapse();
27696             }
27697         });
27698     },
27699     
27700     onViewOver : function(e, t){
27701         // do nothing...
27702         return;
27703         
27704     },
27705     
27706     onViewClick : function(doFocus,index){
27707         return;
27708         
27709     },
27710     select: function () {
27711         //Roo.log("SELECT CALLED");
27712     },
27713      
27714     selectByValue : function(xv, scrollIntoView){
27715         var ar = this.getValueArray();
27716         var sels = [];
27717         
27718         Roo.each(ar, function(v) {
27719             if(v === undefined || v === null){
27720                 return;
27721             }
27722             var r = this.findRecord(this.valueField, v);
27723             if(r){
27724                 sels.push(this.store.indexOf(r))
27725                 
27726             }
27727         },this);
27728         this.view.select(sels);
27729         return false;
27730     },
27731     
27732     
27733     
27734     onSelect : function(record, index){
27735        // Roo.log("onselect Called");
27736        // this is only called by the clear button now..
27737         this.view.clearSelections();
27738         this.setValue('[]');
27739         if (this.value != this.valueBefore) {
27740             this.fireEvent('change', this, this.value, this.valueBefore);
27741             this.valueBefore = this.value;
27742         }
27743     },
27744     getValueArray : function()
27745     {
27746         var ar = [] ;
27747         
27748         try {
27749             //Roo.log(this.value);
27750             if (typeof(this.value) == 'undefined') {
27751                 return [];
27752             }
27753             var ar = Roo.decode(this.value);
27754             return  ar instanceof Array ? ar : []; //?? valid?
27755             
27756         } catch(e) {
27757             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27758             return [];
27759         }
27760          
27761     },
27762     expand : function ()
27763     {
27764         
27765         Roo.form.ComboCheck.superclass.expand.call(this);
27766         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27767         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27768         
27769
27770     },
27771     
27772     collapse : function(){
27773         Roo.form.ComboCheck.superclass.collapse.call(this);
27774         var sl = this.view.getSelectedIndexes();
27775         var st = this.store;
27776         var nv = [];
27777         var tv = [];
27778         var r;
27779         Roo.each(sl, function(i) {
27780             r = st.getAt(i);
27781             nv.push(r.get(this.valueField));
27782         },this);
27783         this.setValue(Roo.encode(nv));
27784         if (this.value != this.valueBefore) {
27785
27786             this.fireEvent('change', this, this.value, this.valueBefore);
27787             this.valueBefore = this.value;
27788         }
27789         
27790     },
27791     
27792     setValue : function(v){
27793         // Roo.log(v);
27794         this.value = v;
27795         
27796         var vals = this.getValueArray();
27797         var tv = [];
27798         Roo.each(vals, function(k) {
27799             var r = this.findRecord(this.valueField, k);
27800             if(r){
27801                 tv.push(r.data[this.displayField]);
27802             }else if(this.valueNotFoundText !== undefined){
27803                 tv.push( this.valueNotFoundText );
27804             }
27805         },this);
27806        // Roo.log(tv);
27807         
27808         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27809         this.hiddenField.value = v;
27810         this.value = v;
27811     }
27812     
27813 });/*
27814  * Based on:
27815  * Ext JS Library 1.1.1
27816  * Copyright(c) 2006-2007, Ext JS, LLC.
27817  *
27818  * Originally Released Under LGPL - original licence link has changed is not relivant.
27819  *
27820  * Fork - LGPL
27821  * <script type="text/javascript">
27822  */
27823  
27824 /**
27825  * @class Roo.form.Signature
27826  * @extends Roo.form.Field
27827  * Signature field.  
27828  * @constructor
27829  * 
27830  * @param {Object} config Configuration options
27831  */
27832
27833 Roo.form.Signature = function(config){
27834     Roo.form.Signature.superclass.constructor.call(this, config);
27835     
27836     this.addEvents({// not in used??
27837          /**
27838          * @event confirm
27839          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27840              * @param {Roo.form.Signature} combo This combo box
27841              */
27842         'confirm' : true,
27843         /**
27844          * @event reset
27845          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27846              * @param {Roo.form.ComboBox} combo This combo box
27847              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27848              */
27849         'reset' : true
27850     });
27851 };
27852
27853 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27854     /**
27855      * @cfg {Object} labels Label to use when rendering a form.
27856      * defaults to 
27857      * labels : { 
27858      *      clear : "Clear",
27859      *      confirm : "Confirm"
27860      *  }
27861      */
27862     labels : { 
27863         clear : "Clear",
27864         confirm : "Confirm"
27865     },
27866     /**
27867      * @cfg {Number} width The signature panel width (defaults to 300)
27868      */
27869     width: 300,
27870     /**
27871      * @cfg {Number} height The signature panel height (defaults to 100)
27872      */
27873     height : 100,
27874     /**
27875      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27876      */
27877     allowBlank : false,
27878     
27879     //private
27880     // {Object} signPanel The signature SVG panel element (defaults to {})
27881     signPanel : {},
27882     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27883     isMouseDown : false,
27884     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27885     isConfirmed : false,
27886     // {String} signatureTmp SVG mapping string (defaults to empty string)
27887     signatureTmp : '',
27888     
27889     
27890     defaultAutoCreate : { // modified by initCompnoent..
27891         tag: "input",
27892         type:"hidden"
27893     },
27894
27895     // private
27896     onRender : function(ct, position){
27897         
27898         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27899         
27900         this.wrap = this.el.wrap({
27901             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27902         });
27903         
27904         this.createToolbar(this);
27905         this.signPanel = this.wrap.createChild({
27906                 tag: 'div',
27907                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27908             }, this.el
27909         );
27910             
27911         this.svgID = Roo.id();
27912         this.svgEl = this.signPanel.createChild({
27913               xmlns : 'http://www.w3.org/2000/svg',
27914               tag : 'svg',
27915               id : this.svgID + "-svg",
27916               width: this.width,
27917               height: this.height,
27918               viewBox: '0 0 '+this.width+' '+this.height,
27919               cn : [
27920                 {
27921                     tag: "rect",
27922                     id: this.svgID + "-svg-r",
27923                     width: this.width,
27924                     height: this.height,
27925                     fill: "#ffa"
27926                 },
27927                 {
27928                     tag: "line",
27929                     id: this.svgID + "-svg-l",
27930                     x1: "0", // start
27931                     y1: (this.height*0.8), // start set the line in 80% of height
27932                     x2: this.width, // end
27933                     y2: (this.height*0.8), // end set the line in 80% of height
27934                     'stroke': "#666",
27935                     'stroke-width': "1",
27936                     'stroke-dasharray': "3",
27937                     'shape-rendering': "crispEdges",
27938                     'pointer-events': "none"
27939                 },
27940                 {
27941                     tag: "path",
27942                     id: this.svgID + "-svg-p",
27943                     'stroke': "navy",
27944                     'stroke-width': "3",
27945                     'fill': "none",
27946                     'pointer-events': 'none'
27947                 }
27948               ]
27949         });
27950         this.createSVG();
27951         this.svgBox = this.svgEl.dom.getScreenCTM();
27952     },
27953     createSVG : function(){ 
27954         var svg = this.signPanel;
27955         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27956         var t = this;
27957
27958         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27959         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27960         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27961         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27962         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27963         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27964         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27965         
27966     },
27967     isTouchEvent : function(e){
27968         return e.type.match(/^touch/);
27969     },
27970     getCoords : function (e) {
27971         var pt    = this.svgEl.dom.createSVGPoint();
27972         pt.x = e.clientX; 
27973         pt.y = e.clientY;
27974         if (this.isTouchEvent(e)) {
27975             pt.x =  e.targetTouches[0].clientX;
27976             pt.y = e.targetTouches[0].clientY;
27977         }
27978         var a = this.svgEl.dom.getScreenCTM();
27979         var b = a.inverse();
27980         var mx = pt.matrixTransform(b);
27981         return mx.x + ',' + mx.y;
27982     },
27983     //mouse event headler 
27984     down : function (e) {
27985         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27986         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27987         
27988         this.isMouseDown = true;
27989         
27990         e.preventDefault();
27991     },
27992     move : function (e) {
27993         if (this.isMouseDown) {
27994             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27995             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27996         }
27997         
27998         e.preventDefault();
27999     },
28000     up : function (e) {
28001         this.isMouseDown = false;
28002         var sp = this.signatureTmp.split(' ');
28003         
28004         if(sp.length > 1){
28005             if(!sp[sp.length-2].match(/^L/)){
28006                 sp.pop();
28007                 sp.pop();
28008                 sp.push("");
28009                 this.signatureTmp = sp.join(" ");
28010             }
28011         }
28012         if(this.getValue() != this.signatureTmp){
28013             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28014             this.isConfirmed = false;
28015         }
28016         e.preventDefault();
28017     },
28018     
28019     /**
28020      * Protected method that will not generally be called directly. It
28021      * is called when the editor creates its toolbar. Override this method if you need to
28022      * add custom toolbar buttons.
28023      * @param {HtmlEditor} editor
28024      */
28025     createToolbar : function(editor){
28026          function btn(id, toggle, handler){
28027             var xid = fid + '-'+ id ;
28028             return {
28029                 id : xid,
28030                 cmd : id,
28031                 cls : 'x-btn-icon x-edit-'+id,
28032                 enableToggle:toggle !== false,
28033                 scope: editor, // was editor...
28034                 handler:handler||editor.relayBtnCmd,
28035                 clickEvent:'mousedown',
28036                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28037                 tabIndex:-1
28038             };
28039         }
28040         
28041         
28042         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28043         this.tb = tb;
28044         this.tb.add(
28045            {
28046                 cls : ' x-signature-btn x-signature-'+id,
28047                 scope: editor, // was editor...
28048                 handler: this.reset,
28049                 clickEvent:'mousedown',
28050                 text: this.labels.clear
28051             },
28052             {
28053                  xtype : 'Fill',
28054                  xns: Roo.Toolbar
28055             }, 
28056             {
28057                 cls : '  x-signature-btn x-signature-'+id,
28058                 scope: editor, // was editor...
28059                 handler: this.confirmHandler,
28060                 clickEvent:'mousedown',
28061                 text: this.labels.confirm
28062             }
28063         );
28064     
28065     },
28066     //public
28067     /**
28068      * when user is clicked confirm then show this image.....
28069      * 
28070      * @return {String} Image Data URI
28071      */
28072     getImageDataURI : function(){
28073         var svg = this.svgEl.dom.parentNode.innerHTML;
28074         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28075         return src; 
28076     },
28077     /**
28078      * 
28079      * @return {Boolean} this.isConfirmed
28080      */
28081     getConfirmed : function(){
28082         return this.isConfirmed;
28083     },
28084     /**
28085      * 
28086      * @return {Number} this.width
28087      */
28088     getWidth : function(){
28089         return this.width;
28090     },
28091     /**
28092      * 
28093      * @return {Number} this.height
28094      */
28095     getHeight : function(){
28096         return this.height;
28097     },
28098     // private
28099     getSignature : function(){
28100         return this.signatureTmp;
28101     },
28102     // private
28103     reset : function(){
28104         this.signatureTmp = '';
28105         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28106         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28107         this.isConfirmed = false;
28108         Roo.form.Signature.superclass.reset.call(this);
28109     },
28110     setSignature : function(s){
28111         this.signatureTmp = s;
28112         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28113         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28114         this.setValue(s);
28115         this.isConfirmed = false;
28116         Roo.form.Signature.superclass.reset.call(this);
28117     }, 
28118     test : function(){
28119 //        Roo.log(this.signPanel.dom.contentWindow.up())
28120     },
28121     //private
28122     setConfirmed : function(){
28123         
28124         
28125         
28126 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28127     },
28128     // private
28129     confirmHandler : function(){
28130         if(!this.getSignature()){
28131             return;
28132         }
28133         
28134         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28135         this.setValue(this.getSignature());
28136         this.isConfirmed = true;
28137         
28138         this.fireEvent('confirm', this);
28139     },
28140     // private
28141     // Subclasses should provide the validation implementation by overriding this
28142     validateValue : function(value){
28143         if(this.allowBlank){
28144             return true;
28145         }
28146         
28147         if(this.isConfirmed){
28148             return true;
28149         }
28150         return false;
28151     }
28152 });/*
28153  * Based on:
28154  * Ext JS Library 1.1.1
28155  * Copyright(c) 2006-2007, Ext JS, LLC.
28156  *
28157  * Originally Released Under LGPL - original licence link has changed is not relivant.
28158  *
28159  * Fork - LGPL
28160  * <script type="text/javascript">
28161  */
28162  
28163
28164 /**
28165  * @class Roo.form.ComboBox
28166  * @extends Roo.form.TriggerField
28167  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28168  * @constructor
28169  * Create a new ComboBox.
28170  * @param {Object} config Configuration options
28171  */
28172 Roo.form.Select = function(config){
28173     Roo.form.Select.superclass.constructor.call(this, config);
28174      
28175 };
28176
28177 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28178     /**
28179      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28180      */
28181     /**
28182      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28183      * rendering into an Roo.Editor, defaults to false)
28184      */
28185     /**
28186      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28187      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28188      */
28189     /**
28190      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28191      */
28192     /**
28193      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28194      * the dropdown list (defaults to undefined, with no header element)
28195      */
28196
28197      /**
28198      * @cfg {String/Roo.Template} tpl The template to use to render the output
28199      */
28200      
28201     // private
28202     defaultAutoCreate : {tag: "select"  },
28203     /**
28204      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28205      */
28206     listWidth: undefined,
28207     /**
28208      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28209      * mode = 'remote' or 'text' if mode = 'local')
28210      */
28211     displayField: undefined,
28212     /**
28213      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28214      * mode = 'remote' or 'value' if mode = 'local'). 
28215      * Note: use of a valueField requires the user make a selection
28216      * in order for a value to be mapped.
28217      */
28218     valueField: undefined,
28219     
28220     
28221     /**
28222      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28223      * field's data value (defaults to the underlying DOM element's name)
28224      */
28225     hiddenName: undefined,
28226     /**
28227      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28228      */
28229     listClass: '',
28230     /**
28231      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28232      */
28233     selectedClass: 'x-combo-selected',
28234     /**
28235      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28236      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28237      * which displays a downward arrow icon).
28238      */
28239     triggerClass : 'x-form-arrow-trigger',
28240     /**
28241      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28242      */
28243     shadow:'sides',
28244     /**
28245      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28246      * anchor positions (defaults to 'tl-bl')
28247      */
28248     listAlign: 'tl-bl?',
28249     /**
28250      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28251      */
28252     maxHeight: 300,
28253     /**
28254      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28255      * query specified by the allQuery config option (defaults to 'query')
28256      */
28257     triggerAction: 'query',
28258     /**
28259      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28260      * (defaults to 4, does not apply if editable = false)
28261      */
28262     minChars : 4,
28263     /**
28264      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28265      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28266      */
28267     typeAhead: false,
28268     /**
28269      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28270      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28271      */
28272     queryDelay: 500,
28273     /**
28274      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28275      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28276      */
28277     pageSize: 0,
28278     /**
28279      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28280      * when editable = true (defaults to false)
28281      */
28282     selectOnFocus:false,
28283     /**
28284      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28285      */
28286     queryParam: 'query',
28287     /**
28288      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28289      * when mode = 'remote' (defaults to 'Loading...')
28290      */
28291     loadingText: 'Loading...',
28292     /**
28293      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28294      */
28295     resizable: false,
28296     /**
28297      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28298      */
28299     handleHeight : 8,
28300     /**
28301      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28302      * traditional select (defaults to true)
28303      */
28304     editable: true,
28305     /**
28306      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28307      */
28308     allQuery: '',
28309     /**
28310      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28311      */
28312     mode: 'remote',
28313     /**
28314      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28315      * listWidth has a higher value)
28316      */
28317     minListWidth : 70,
28318     /**
28319      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28320      * allow the user to set arbitrary text into the field (defaults to false)
28321      */
28322     forceSelection:false,
28323     /**
28324      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28325      * if typeAhead = true (defaults to 250)
28326      */
28327     typeAheadDelay : 250,
28328     /**
28329      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28330      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28331      */
28332     valueNotFoundText : undefined,
28333     
28334     /**
28335      * @cfg {String} defaultValue The value displayed after loading the store.
28336      */
28337     defaultValue: '',
28338     
28339     /**
28340      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28341      */
28342     blockFocus : false,
28343     
28344     /**
28345      * @cfg {Boolean} disableClear Disable showing of clear button.
28346      */
28347     disableClear : false,
28348     /**
28349      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28350      */
28351     alwaysQuery : false,
28352     
28353     //private
28354     addicon : false,
28355     editicon: false,
28356     
28357     // element that contains real text value.. (when hidden is used..)
28358      
28359     // private
28360     onRender : function(ct, position){
28361         Roo.form.Field.prototype.onRender.call(this, ct, position);
28362         
28363         if(this.store){
28364             this.store.on('beforeload', this.onBeforeLoad, this);
28365             this.store.on('load', this.onLoad, this);
28366             this.store.on('loadexception', this.onLoadException, this);
28367             this.store.load({});
28368         }
28369         
28370         
28371         
28372     },
28373
28374     // private
28375     initEvents : function(){
28376         //Roo.form.ComboBox.superclass.initEvents.call(this);
28377  
28378     },
28379
28380     onDestroy : function(){
28381        
28382         if(this.store){
28383             this.store.un('beforeload', this.onBeforeLoad, this);
28384             this.store.un('load', this.onLoad, this);
28385             this.store.un('loadexception', this.onLoadException, this);
28386         }
28387         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28388     },
28389
28390     // private
28391     fireKey : function(e){
28392         if(e.isNavKeyPress() && !this.list.isVisible()){
28393             this.fireEvent("specialkey", this, e);
28394         }
28395     },
28396
28397     // private
28398     onResize: function(w, h){
28399         
28400         return; 
28401     
28402         
28403     },
28404
28405     /**
28406      * Allow or prevent the user from directly editing the field text.  If false is passed,
28407      * the user will only be able to select from the items defined in the dropdown list.  This method
28408      * is the runtime equivalent of setting the 'editable' config option at config time.
28409      * @param {Boolean} value True to allow the user to directly edit the field text
28410      */
28411     setEditable : function(value){
28412          
28413     },
28414
28415     // private
28416     onBeforeLoad : function(){
28417         
28418         Roo.log("Select before load");
28419         return;
28420     
28421         this.innerList.update(this.loadingText ?
28422                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28423         //this.restrictHeight();
28424         this.selectedIndex = -1;
28425     },
28426
28427     // private
28428     onLoad : function(){
28429
28430     
28431         var dom = this.el.dom;
28432         dom.innerHTML = '';
28433          var od = dom.ownerDocument;
28434          
28435         if (this.emptyText) {
28436             var op = od.createElement('option');
28437             op.setAttribute('value', '');
28438             op.innerHTML = String.format('{0}', this.emptyText);
28439             dom.appendChild(op);
28440         }
28441         if(this.store.getCount() > 0){
28442            
28443             var vf = this.valueField;
28444             var df = this.displayField;
28445             this.store.data.each(function(r) {
28446                 // which colmsn to use... testing - cdoe / title..
28447                 var op = od.createElement('option');
28448                 op.setAttribute('value', r.data[vf]);
28449                 op.innerHTML = String.format('{0}', r.data[df]);
28450                 dom.appendChild(op);
28451             });
28452             if (typeof(this.defaultValue != 'undefined')) {
28453                 this.setValue(this.defaultValue);
28454             }
28455             
28456              
28457         }else{
28458             //this.onEmptyResults();
28459         }
28460         //this.el.focus();
28461     },
28462     // private
28463     onLoadException : function()
28464     {
28465         dom.innerHTML = '';
28466             
28467         Roo.log("Select on load exception");
28468         return;
28469     
28470         this.collapse();
28471         Roo.log(this.store.reader.jsonData);
28472         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28473             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28474         }
28475         
28476         
28477     },
28478     // private
28479     onTypeAhead : function(){
28480          
28481     },
28482
28483     // private
28484     onSelect : function(record, index){
28485         Roo.log('on select?');
28486         return;
28487         if(this.fireEvent('beforeselect', this, record, index) !== false){
28488             this.setFromData(index > -1 ? record.data : false);
28489             this.collapse();
28490             this.fireEvent('select', this, record, index);
28491         }
28492     },
28493
28494     /**
28495      * Returns the currently selected field value or empty string if no value is set.
28496      * @return {String} value The selected value
28497      */
28498     getValue : function(){
28499         var dom = this.el.dom;
28500         this.value = dom.options[dom.selectedIndex].value;
28501         return this.value;
28502         
28503     },
28504
28505     /**
28506      * Clears any text/value currently set in the field
28507      */
28508     clearValue : function(){
28509         this.value = '';
28510         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28511         
28512     },
28513
28514     /**
28515      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28516      * will be displayed in the field.  If the value does not match the data value of an existing item,
28517      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28518      * Otherwise the field will be blank (although the value will still be set).
28519      * @param {String} value The value to match
28520      */
28521     setValue : function(v){
28522         var d = this.el.dom;
28523         for (var i =0; i < d.options.length;i++) {
28524             if (v == d.options[i].value) {
28525                 d.selectedIndex = i;
28526                 this.value = v;
28527                 return;
28528             }
28529         }
28530         this.clearValue();
28531     },
28532     /**
28533      * @property {Object} the last set data for the element
28534      */
28535     
28536     lastData : false,
28537     /**
28538      * Sets the value of the field based on a object which is related to the record format for the store.
28539      * @param {Object} value the value to set as. or false on reset?
28540      */
28541     setFromData : function(o){
28542         Roo.log('setfrom data?');
28543          
28544         
28545         
28546     },
28547     // private
28548     reset : function(){
28549         this.clearValue();
28550     },
28551     // private
28552     findRecord : function(prop, value){
28553         
28554         return false;
28555     
28556         var record;
28557         if(this.store.getCount() > 0){
28558             this.store.each(function(r){
28559                 if(r.data[prop] == value){
28560                     record = r;
28561                     return false;
28562                 }
28563                 return true;
28564             });
28565         }
28566         return record;
28567     },
28568     
28569     getName: function()
28570     {
28571         // returns hidden if it's set..
28572         if (!this.rendered) {return ''};
28573         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28574         
28575     },
28576      
28577
28578     
28579
28580     // private
28581     onEmptyResults : function(){
28582         Roo.log('empty results');
28583         //this.collapse();
28584     },
28585
28586     /**
28587      * Returns true if the dropdown list is expanded, else false.
28588      */
28589     isExpanded : function(){
28590         return false;
28591     },
28592
28593     /**
28594      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28595      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28596      * @param {String} value The data value of the item to select
28597      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28598      * selected item if it is not currently in view (defaults to true)
28599      * @return {Boolean} True if the value matched an item in the list, else false
28600      */
28601     selectByValue : function(v, scrollIntoView){
28602         Roo.log('select By Value');
28603         return false;
28604     
28605         if(v !== undefined && v !== null){
28606             var r = this.findRecord(this.valueField || this.displayField, v);
28607             if(r){
28608                 this.select(this.store.indexOf(r), scrollIntoView);
28609                 return true;
28610             }
28611         }
28612         return false;
28613     },
28614
28615     /**
28616      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28617      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28618      * @param {Number} index The zero-based index of the list item to select
28619      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28620      * selected item if it is not currently in view (defaults to true)
28621      */
28622     select : function(index, scrollIntoView){
28623         Roo.log('select ');
28624         return  ;
28625         
28626         this.selectedIndex = index;
28627         this.view.select(index);
28628         if(scrollIntoView !== false){
28629             var el = this.view.getNode(index);
28630             if(el){
28631                 this.innerList.scrollChildIntoView(el, false);
28632             }
28633         }
28634     },
28635
28636       
28637
28638     // private
28639     validateBlur : function(){
28640         
28641         return;
28642         
28643     },
28644
28645     // private
28646     initQuery : function(){
28647         this.doQuery(this.getRawValue());
28648     },
28649
28650     // private
28651     doForce : function(){
28652         if(this.el.dom.value.length > 0){
28653             this.el.dom.value =
28654                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28655              
28656         }
28657     },
28658
28659     /**
28660      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28661      * query allowing the query action to be canceled if needed.
28662      * @param {String} query The SQL query to execute
28663      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28664      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28665      * saved in the current store (defaults to false)
28666      */
28667     doQuery : function(q, forceAll){
28668         
28669         Roo.log('doQuery?');
28670         if(q === undefined || q === null){
28671             q = '';
28672         }
28673         var qe = {
28674             query: q,
28675             forceAll: forceAll,
28676             combo: this,
28677             cancel:false
28678         };
28679         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28680             return false;
28681         }
28682         q = qe.query;
28683         forceAll = qe.forceAll;
28684         if(forceAll === true || (q.length >= this.minChars)){
28685             if(this.lastQuery != q || this.alwaysQuery){
28686                 this.lastQuery = q;
28687                 if(this.mode == 'local'){
28688                     this.selectedIndex = -1;
28689                     if(forceAll){
28690                         this.store.clearFilter();
28691                     }else{
28692                         this.store.filter(this.displayField, q);
28693                     }
28694                     this.onLoad();
28695                 }else{
28696                     this.store.baseParams[this.queryParam] = q;
28697                     this.store.load({
28698                         params: this.getParams(q)
28699                     });
28700                     this.expand();
28701                 }
28702             }else{
28703                 this.selectedIndex = -1;
28704                 this.onLoad();   
28705             }
28706         }
28707     },
28708
28709     // private
28710     getParams : function(q){
28711         var p = {};
28712         //p[this.queryParam] = q;
28713         if(this.pageSize){
28714             p.start = 0;
28715             p.limit = this.pageSize;
28716         }
28717         return p;
28718     },
28719
28720     /**
28721      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28722      */
28723     collapse : function(){
28724         
28725     },
28726
28727     // private
28728     collapseIf : function(e){
28729         
28730     },
28731
28732     /**
28733      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28734      */
28735     expand : function(){
28736         
28737     } ,
28738
28739     // private
28740      
28741
28742     /** 
28743     * @cfg {Boolean} grow 
28744     * @hide 
28745     */
28746     /** 
28747     * @cfg {Number} growMin 
28748     * @hide 
28749     */
28750     /** 
28751     * @cfg {Number} growMax 
28752     * @hide 
28753     */
28754     /**
28755      * @hide
28756      * @method autoSize
28757      */
28758     
28759     setWidth : function()
28760     {
28761         
28762     },
28763     getResizeEl : function(){
28764         return this.el;
28765     }
28766 });//<script type="text/javasscript">
28767  
28768
28769 /**
28770  * @class Roo.DDView
28771  * A DnD enabled version of Roo.View.
28772  * @param {Element/String} container The Element in which to create the View.
28773  * @param {String} tpl The template string used to create the markup for each element of the View
28774  * @param {Object} config The configuration properties. These include all the config options of
28775  * {@link Roo.View} plus some specific to this class.<br>
28776  * <p>
28777  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28778  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28779  * <p>
28780  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28781 .x-view-drag-insert-above {
28782         border-top:1px dotted #3366cc;
28783 }
28784 .x-view-drag-insert-below {
28785         border-bottom:1px dotted #3366cc;
28786 }
28787 </code></pre>
28788  * 
28789  */
28790  
28791 Roo.DDView = function(container, tpl, config) {
28792     Roo.DDView.superclass.constructor.apply(this, arguments);
28793     this.getEl().setStyle("outline", "0px none");
28794     this.getEl().unselectable();
28795     if (this.dragGroup) {
28796                 this.setDraggable(this.dragGroup.split(","));
28797     }
28798     if (this.dropGroup) {
28799                 this.setDroppable(this.dropGroup.split(","));
28800     }
28801     if (this.deletable) {
28802         this.setDeletable();
28803     }
28804     this.isDirtyFlag = false;
28805         this.addEvents({
28806                 "drop" : true
28807         });
28808 };
28809
28810 Roo.extend(Roo.DDView, Roo.View, {
28811 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28812 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28813 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28814 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28815
28816         isFormField: true,
28817
28818         reset: Roo.emptyFn,
28819         
28820         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28821
28822         validate: function() {
28823                 return true;
28824         },
28825         
28826         destroy: function() {
28827                 this.purgeListeners();
28828                 this.getEl.removeAllListeners();
28829                 this.getEl().remove();
28830                 if (this.dragZone) {
28831                         if (this.dragZone.destroy) {
28832                                 this.dragZone.destroy();
28833                         }
28834                 }
28835                 if (this.dropZone) {
28836                         if (this.dropZone.destroy) {
28837                                 this.dropZone.destroy();
28838                         }
28839                 }
28840         },
28841
28842 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28843         getName: function() {
28844                 return this.name;
28845         },
28846
28847 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28848         setValue: function(v) {
28849                 if (!this.store) {
28850                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28851                 }
28852                 var data = {};
28853                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28854                 this.store.proxy = new Roo.data.MemoryProxy(data);
28855                 this.store.load();
28856         },
28857
28858 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28859         getValue: function() {
28860                 var result = '(';
28861                 this.store.each(function(rec) {
28862                         result += rec.id + ',';
28863                 });
28864                 return result.substr(0, result.length - 1) + ')';
28865         },
28866         
28867         getIds: function() {
28868                 var i = 0, result = new Array(this.store.getCount());
28869                 this.store.each(function(rec) {
28870                         result[i++] = rec.id;
28871                 });
28872                 return result;
28873         },
28874         
28875         isDirty: function() {
28876                 return this.isDirtyFlag;
28877         },
28878
28879 /**
28880  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28881  *      whole Element becomes the target, and this causes the drop gesture to append.
28882  */
28883     getTargetFromEvent : function(e) {
28884                 var target = e.getTarget();
28885                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28886                 target = target.parentNode;
28887                 }
28888                 if (!target) {
28889                         target = this.el.dom.lastChild || this.el.dom;
28890                 }
28891                 return target;
28892     },
28893
28894 /**
28895  *      Create the drag data which consists of an object which has the property "ddel" as
28896  *      the drag proxy element. 
28897  */
28898     getDragData : function(e) {
28899         var target = this.findItemFromChild(e.getTarget());
28900                 if(target) {
28901                         this.handleSelection(e);
28902                         var selNodes = this.getSelectedNodes();
28903             var dragData = {
28904                 source: this,
28905                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28906                 nodes: selNodes,
28907                 records: []
28908                         };
28909                         var selectedIndices = this.getSelectedIndexes();
28910                         for (var i = 0; i < selectedIndices.length; i++) {
28911                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28912                         }
28913                         if (selNodes.length == 1) {
28914                                 dragData.ddel = target.cloneNode(true); // the div element
28915                         } else {
28916                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28917                                 div.className = 'multi-proxy';
28918                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28919                                         div.appendChild(selNodes[i].cloneNode(true));
28920                                 }
28921                                 dragData.ddel = div;
28922                         }
28923             //console.log(dragData)
28924             //console.log(dragData.ddel.innerHTML)
28925                         return dragData;
28926                 }
28927         //console.log('nodragData')
28928                 return false;
28929     },
28930     
28931 /**     Specify to which ddGroup items in this DDView may be dragged. */
28932     setDraggable: function(ddGroup) {
28933         if (ddGroup instanceof Array) {
28934                 Roo.each(ddGroup, this.setDraggable, this);
28935                 return;
28936         }
28937         if (this.dragZone) {
28938                 this.dragZone.addToGroup(ddGroup);
28939         } else {
28940                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28941                                 containerScroll: true,
28942                                 ddGroup: ddGroup 
28943
28944                         });
28945 //                      Draggability implies selection. DragZone's mousedown selects the element.
28946                         if (!this.multiSelect) { this.singleSelect = true; }
28947
28948 //                      Wire the DragZone's handlers up to methods in *this*
28949                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28950                 }
28951     },
28952
28953 /**     Specify from which ddGroup this DDView accepts drops. */
28954     setDroppable: function(ddGroup) {
28955         if (ddGroup instanceof Array) {
28956                 Roo.each(ddGroup, this.setDroppable, this);
28957                 return;
28958         }
28959         if (this.dropZone) {
28960                 this.dropZone.addToGroup(ddGroup);
28961         } else {
28962                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28963                                 containerScroll: true,
28964                                 ddGroup: ddGroup
28965                         });
28966
28967 //                      Wire the DropZone's handlers up to methods in *this*
28968                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28969                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28970                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28971                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28972                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28973                 }
28974     },
28975
28976 /**     Decide whether to drop above or below a View node. */
28977     getDropPoint : function(e, n, dd){
28978         if (n == this.el.dom) { return "above"; }
28979                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28980                 var c = t + (b - t) / 2;
28981                 var y = Roo.lib.Event.getPageY(e);
28982                 if(y <= c) {
28983                         return "above";
28984                 }else{
28985                         return "below";
28986                 }
28987     },
28988
28989     onNodeEnter : function(n, dd, e, data){
28990                 return false;
28991     },
28992     
28993     onNodeOver : function(n, dd, e, data){
28994                 var pt = this.getDropPoint(e, n, dd);
28995                 // set the insert point style on the target node
28996                 var dragElClass = this.dropNotAllowed;
28997                 if (pt) {
28998                         var targetElClass;
28999                         if (pt == "above"){
29000                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29001                                 targetElClass = "x-view-drag-insert-above";
29002                         } else {
29003                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29004                                 targetElClass = "x-view-drag-insert-below";
29005                         }
29006                         if (this.lastInsertClass != targetElClass){
29007                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29008                                 this.lastInsertClass = targetElClass;
29009                         }
29010                 }
29011                 return dragElClass;
29012         },
29013
29014     onNodeOut : function(n, dd, e, data){
29015                 this.removeDropIndicators(n);
29016     },
29017
29018     onNodeDrop : function(n, dd, e, data){
29019         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29020                 return false;
29021         }
29022         var pt = this.getDropPoint(e, n, dd);
29023                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29024                 if (pt == "below") { insertAt++; }
29025                 for (var i = 0; i < data.records.length; i++) {
29026                         var r = data.records[i];
29027                         var dup = this.store.getById(r.id);
29028                         if (dup && (dd != this.dragZone)) {
29029                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29030                         } else {
29031                                 if (data.copy) {
29032                                         this.store.insert(insertAt++, r.copy());
29033                                 } else {
29034                                         data.source.isDirtyFlag = true;
29035                                         r.store.remove(r);
29036                                         this.store.insert(insertAt++, r);
29037                                 }
29038                                 this.isDirtyFlag = true;
29039                         }
29040                 }
29041                 this.dragZone.cachedTarget = null;
29042                 return true;
29043     },
29044
29045     removeDropIndicators : function(n){
29046                 if(n){
29047                         Roo.fly(n).removeClass([
29048                                 "x-view-drag-insert-above",
29049                                 "x-view-drag-insert-below"]);
29050                         this.lastInsertClass = "_noclass";
29051                 }
29052     },
29053
29054 /**
29055  *      Utility method. Add a delete option to the DDView's context menu.
29056  *      @param {String} imageUrl The URL of the "delete" icon image.
29057  */
29058         setDeletable: function(imageUrl) {
29059                 if (!this.singleSelect && !this.multiSelect) {
29060                         this.singleSelect = true;
29061                 }
29062                 var c = this.getContextMenu();
29063                 this.contextMenu.on("itemclick", function(item) {
29064                         switch (item.id) {
29065                                 case "delete":
29066                                         this.remove(this.getSelectedIndexes());
29067                                         break;
29068                         }
29069                 }, this);
29070                 this.contextMenu.add({
29071                         icon: imageUrl,
29072                         id: "delete",
29073                         text: 'Delete'
29074                 });
29075         },
29076         
29077 /**     Return the context menu for this DDView. */
29078         getContextMenu: function() {
29079                 if (!this.contextMenu) {
29080 //                      Create the View's context menu
29081                         this.contextMenu = new Roo.menu.Menu({
29082                                 id: this.id + "-contextmenu"
29083                         });
29084                         this.el.on("contextmenu", this.showContextMenu, this);
29085                 }
29086                 return this.contextMenu;
29087         },
29088         
29089         disableContextMenu: function() {
29090                 if (this.contextMenu) {
29091                         this.el.un("contextmenu", this.showContextMenu, this);
29092                 }
29093         },
29094
29095         showContextMenu: function(e, item) {
29096         item = this.findItemFromChild(e.getTarget());
29097                 if (item) {
29098                         e.stopEvent();
29099                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29100                         this.contextMenu.showAt(e.getXY());
29101             }
29102     },
29103
29104 /**
29105  *      Remove {@link Roo.data.Record}s at the specified indices.
29106  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29107  */
29108     remove: function(selectedIndices) {
29109                 selectedIndices = [].concat(selectedIndices);
29110                 for (var i = 0; i < selectedIndices.length; i++) {
29111                         var rec = this.store.getAt(selectedIndices[i]);
29112                         this.store.remove(rec);
29113                 }
29114     },
29115
29116 /**
29117  *      Double click fires the event, but also, if this is draggable, and there is only one other
29118  *      related DropZone, it transfers the selected node.
29119  */
29120     onDblClick : function(e){
29121         var item = this.findItemFromChild(e.getTarget());
29122         if(item){
29123             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29124                 return false;
29125             }
29126             if (this.dragGroup) {
29127                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29128                     while (targets.indexOf(this.dropZone) > -1) {
29129                             targets.remove(this.dropZone);
29130                                 }
29131                     if (targets.length == 1) {
29132                                         this.dragZone.cachedTarget = null;
29133                         var el = Roo.get(targets[0].getEl());
29134                         var box = el.getBox(true);
29135                         targets[0].onNodeDrop(el.dom, {
29136                                 target: el.dom,
29137                                 xy: [box.x, box.y + box.height - 1]
29138                         }, null, this.getDragData(e));
29139                     }
29140                 }
29141         }
29142     },
29143     
29144     handleSelection: function(e) {
29145                 this.dragZone.cachedTarget = null;
29146         var item = this.findItemFromChild(e.getTarget());
29147         if (!item) {
29148                 this.clearSelections(true);
29149                 return;
29150         }
29151                 if (item && (this.multiSelect || this.singleSelect)){
29152                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29153                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29154                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29155                                 this.unselect(item);
29156                         } else {
29157                                 this.select(item, this.multiSelect && e.ctrlKey);
29158                                 this.lastSelection = item;
29159                         }
29160                 }
29161     },
29162
29163     onItemClick : function(item, index, e){
29164                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29165                         return false;
29166                 }
29167                 return true;
29168     },
29169
29170     unselect : function(nodeInfo, suppressEvent){
29171                 var node = this.getNode(nodeInfo);
29172                 if(node && this.isSelected(node)){
29173                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29174                                 Roo.fly(node).removeClass(this.selectedClass);
29175                                 this.selections.remove(node);
29176                                 if(!suppressEvent){
29177                                         this.fireEvent("selectionchange", this, this.selections);
29178                                 }
29179                         }
29180                 }
29181     }
29182 });
29183 /*
29184  * Based on:
29185  * Ext JS Library 1.1.1
29186  * Copyright(c) 2006-2007, Ext JS, LLC.
29187  *
29188  * Originally Released Under LGPL - original licence link has changed is not relivant.
29189  *
29190  * Fork - LGPL
29191  * <script type="text/javascript">
29192  */
29193  
29194 /**
29195  * @class Roo.LayoutManager
29196  * @extends Roo.util.Observable
29197  * Base class for layout managers.
29198  */
29199 Roo.LayoutManager = function(container, config){
29200     Roo.LayoutManager.superclass.constructor.call(this);
29201     this.el = Roo.get(container);
29202     // ie scrollbar fix
29203     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29204         document.body.scroll = "no";
29205     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29206         this.el.position('relative');
29207     }
29208     this.id = this.el.id;
29209     this.el.addClass("x-layout-container");
29210     /** false to disable window resize monitoring @type Boolean */
29211     this.monitorWindowResize = true;
29212     this.regions = {};
29213     this.addEvents({
29214         /**
29215          * @event layout
29216          * Fires when a layout is performed. 
29217          * @param {Roo.LayoutManager} this
29218          */
29219         "layout" : true,
29220         /**
29221          * @event regionresized
29222          * Fires when the user resizes a region. 
29223          * @param {Roo.LayoutRegion} region The resized region
29224          * @param {Number} newSize The new size (width for east/west, height for north/south)
29225          */
29226         "regionresized" : true,
29227         /**
29228          * @event regioncollapsed
29229          * Fires when a region is collapsed. 
29230          * @param {Roo.LayoutRegion} region The collapsed region
29231          */
29232         "regioncollapsed" : true,
29233         /**
29234          * @event regionexpanded
29235          * Fires when a region is expanded.  
29236          * @param {Roo.LayoutRegion} region The expanded region
29237          */
29238         "regionexpanded" : true
29239     });
29240     this.updating = false;
29241     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29242 };
29243
29244 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29245     /**
29246      * Returns true if this layout is currently being updated
29247      * @return {Boolean}
29248      */
29249     isUpdating : function(){
29250         return this.updating; 
29251     },
29252     
29253     /**
29254      * Suspend the LayoutManager from doing auto-layouts while
29255      * making multiple add or remove calls
29256      */
29257     beginUpdate : function(){
29258         this.updating = true;    
29259     },
29260     
29261     /**
29262      * Restore auto-layouts and optionally disable the manager from performing a layout
29263      * @param {Boolean} noLayout true to disable a layout update 
29264      */
29265     endUpdate : function(noLayout){
29266         this.updating = false;
29267         if(!noLayout){
29268             this.layout();
29269         }    
29270     },
29271     
29272     layout: function(){
29273         
29274     },
29275     
29276     onRegionResized : function(region, newSize){
29277         this.fireEvent("regionresized", region, newSize);
29278         this.layout();
29279     },
29280     
29281     onRegionCollapsed : function(region){
29282         this.fireEvent("regioncollapsed", region);
29283     },
29284     
29285     onRegionExpanded : function(region){
29286         this.fireEvent("regionexpanded", region);
29287     },
29288         
29289     /**
29290      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29291      * performs box-model adjustments.
29292      * @return {Object} The size as an object {width: (the width), height: (the height)}
29293      */
29294     getViewSize : function(){
29295         var size;
29296         if(this.el.dom != document.body){
29297             size = this.el.getSize();
29298         }else{
29299             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29300         }
29301         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29302         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29303         return size;
29304     },
29305     
29306     /**
29307      * Returns the Element this layout is bound to.
29308      * @return {Roo.Element}
29309      */
29310     getEl : function(){
29311         return this.el;
29312     },
29313     
29314     /**
29315      * Returns the specified region.
29316      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29317      * @return {Roo.LayoutRegion}
29318      */
29319     getRegion : function(target){
29320         return this.regions[target.toLowerCase()];
29321     },
29322     
29323     onWindowResize : function(){
29324         if(this.monitorWindowResize){
29325             this.layout();
29326         }
29327     }
29328 });/*
29329  * Based on:
29330  * Ext JS Library 1.1.1
29331  * Copyright(c) 2006-2007, Ext JS, LLC.
29332  *
29333  * Originally Released Under LGPL - original licence link has changed is not relivant.
29334  *
29335  * Fork - LGPL
29336  * <script type="text/javascript">
29337  */
29338 /**
29339  * @class Roo.BorderLayout
29340  * @extends Roo.LayoutManager
29341  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29342  * please see: <br><br>
29343  * <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>
29344  * <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>
29345  * Example:
29346  <pre><code>
29347  var layout = new Roo.BorderLayout(document.body, {
29348     north: {
29349         initialSize: 25,
29350         titlebar: false
29351     },
29352     west: {
29353         split:true,
29354         initialSize: 200,
29355         minSize: 175,
29356         maxSize: 400,
29357         titlebar: true,
29358         collapsible: true
29359     },
29360     east: {
29361         split:true,
29362         initialSize: 202,
29363         minSize: 175,
29364         maxSize: 400,
29365         titlebar: true,
29366         collapsible: true
29367     },
29368     south: {
29369         split:true,
29370         initialSize: 100,
29371         minSize: 100,
29372         maxSize: 200,
29373         titlebar: true,
29374         collapsible: true
29375     },
29376     center: {
29377         titlebar: true,
29378         autoScroll:true,
29379         resizeTabs: true,
29380         minTabWidth: 50,
29381         preferredTabWidth: 150
29382     }
29383 });
29384
29385 // shorthand
29386 var CP = Roo.ContentPanel;
29387
29388 layout.beginUpdate();
29389 layout.add("north", new CP("north", "North"));
29390 layout.add("south", new CP("south", {title: "South", closable: true}));
29391 layout.add("west", new CP("west", {title: "West"}));
29392 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29393 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29394 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29395 layout.getRegion("center").showPanel("center1");
29396 layout.endUpdate();
29397 </code></pre>
29398
29399 <b>The container the layout is rendered into can be either the body element or any other element.
29400 If it is not the body element, the container needs to either be an absolute positioned element,
29401 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29402 the container size if it is not the body element.</b>
29403
29404 * @constructor
29405 * Create a new BorderLayout
29406 * @param {String/HTMLElement/Element} container The container this layout is bound to
29407 * @param {Object} config Configuration options
29408  */
29409 Roo.BorderLayout = function(container, config){
29410     config = config || {};
29411     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29412     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29413     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29414         var target = this.factory.validRegions[i];
29415         if(config[target]){
29416             this.addRegion(target, config[target]);
29417         }
29418     }
29419 };
29420
29421 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29422     /**
29423      * Creates and adds a new region if it doesn't already exist.
29424      * @param {String} target The target region key (north, south, east, west or center).
29425      * @param {Object} config The regions config object
29426      * @return {BorderLayoutRegion} The new region
29427      */
29428     addRegion : function(target, config){
29429         if(!this.regions[target]){
29430             var r = this.factory.create(target, this, config);
29431             this.bindRegion(target, r);
29432         }
29433         return this.regions[target];
29434     },
29435
29436     // private (kinda)
29437     bindRegion : function(name, r){
29438         this.regions[name] = r;
29439         r.on("visibilitychange", this.layout, this);
29440         r.on("paneladded", this.layout, this);
29441         r.on("panelremoved", this.layout, this);
29442         r.on("invalidated", this.layout, this);
29443         r.on("resized", this.onRegionResized, this);
29444         r.on("collapsed", this.onRegionCollapsed, this);
29445         r.on("expanded", this.onRegionExpanded, this);
29446     },
29447
29448     /**
29449      * Performs a layout update.
29450      */
29451     layout : function(){
29452         if(this.updating) {
29453             return;
29454         }
29455         var size = this.getViewSize();
29456         var w = size.width;
29457         var h = size.height;
29458         var centerW = w;
29459         var centerH = h;
29460         var centerY = 0;
29461         var centerX = 0;
29462         //var x = 0, y = 0;
29463
29464         var rs = this.regions;
29465         var north = rs["north"];
29466         var south = rs["south"]; 
29467         var west = rs["west"];
29468         var east = rs["east"];
29469         var center = rs["center"];
29470         //if(this.hideOnLayout){ // not supported anymore
29471             //c.el.setStyle("display", "none");
29472         //}
29473         if(north && north.isVisible()){
29474             var b = north.getBox();
29475             var m = north.getMargins();
29476             b.width = w - (m.left+m.right);
29477             b.x = m.left;
29478             b.y = m.top;
29479             centerY = b.height + b.y + m.bottom;
29480             centerH -= centerY;
29481             north.updateBox(this.safeBox(b));
29482         }
29483         if(south && south.isVisible()){
29484             var b = south.getBox();
29485             var m = south.getMargins();
29486             b.width = w - (m.left+m.right);
29487             b.x = m.left;
29488             var totalHeight = (b.height + m.top + m.bottom);
29489             b.y = h - totalHeight + m.top;
29490             centerH -= totalHeight;
29491             south.updateBox(this.safeBox(b));
29492         }
29493         if(west && west.isVisible()){
29494             var b = west.getBox();
29495             var m = west.getMargins();
29496             b.height = centerH - (m.top+m.bottom);
29497             b.x = m.left;
29498             b.y = centerY + m.top;
29499             var totalWidth = (b.width + m.left + m.right);
29500             centerX += totalWidth;
29501             centerW -= totalWidth;
29502             west.updateBox(this.safeBox(b));
29503         }
29504         if(east && east.isVisible()){
29505             var b = east.getBox();
29506             var m = east.getMargins();
29507             b.height = centerH - (m.top+m.bottom);
29508             var totalWidth = (b.width + m.left + m.right);
29509             b.x = w - totalWidth + m.left;
29510             b.y = centerY + m.top;
29511             centerW -= totalWidth;
29512             east.updateBox(this.safeBox(b));
29513         }
29514         if(center){
29515             var m = center.getMargins();
29516             var centerBox = {
29517                 x: centerX + m.left,
29518                 y: centerY + m.top,
29519                 width: centerW - (m.left+m.right),
29520                 height: centerH - (m.top+m.bottom)
29521             };
29522             //if(this.hideOnLayout){
29523                 //center.el.setStyle("display", "block");
29524             //}
29525             center.updateBox(this.safeBox(centerBox));
29526         }
29527         this.el.repaint();
29528         this.fireEvent("layout", this);
29529     },
29530
29531     // private
29532     safeBox : function(box){
29533         box.width = Math.max(0, box.width);
29534         box.height = Math.max(0, box.height);
29535         return box;
29536     },
29537
29538     /**
29539      * Adds a ContentPanel (or subclass) to this layout.
29540      * @param {String} target The target region key (north, south, east, west or center).
29541      * @param {Roo.ContentPanel} panel The panel to add
29542      * @return {Roo.ContentPanel} The added panel
29543      */
29544     add : function(target, panel){
29545          
29546         target = target.toLowerCase();
29547         return this.regions[target].add(panel);
29548     },
29549
29550     /**
29551      * Remove a ContentPanel (or subclass) to this layout.
29552      * @param {String} target The target region key (north, south, east, west or center).
29553      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29554      * @return {Roo.ContentPanel} The removed panel
29555      */
29556     remove : function(target, panel){
29557         target = target.toLowerCase();
29558         return this.regions[target].remove(panel);
29559     },
29560
29561     /**
29562      * Searches all regions for a panel with the specified id
29563      * @param {String} panelId
29564      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29565      */
29566     findPanel : function(panelId){
29567         var rs = this.regions;
29568         for(var target in rs){
29569             if(typeof rs[target] != "function"){
29570                 var p = rs[target].getPanel(panelId);
29571                 if(p){
29572                     return p;
29573                 }
29574             }
29575         }
29576         return null;
29577     },
29578
29579     /**
29580      * Searches all regions for a panel with the specified id and activates (shows) it.
29581      * @param {String/ContentPanel} panelId The panels id or the panel itself
29582      * @return {Roo.ContentPanel} The shown panel or null
29583      */
29584     showPanel : function(panelId) {
29585       var rs = this.regions;
29586       for(var target in rs){
29587          var r = rs[target];
29588          if(typeof r != "function"){
29589             if(r.hasPanel(panelId)){
29590                return r.showPanel(panelId);
29591             }
29592          }
29593       }
29594       return null;
29595    },
29596
29597    /**
29598      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29599      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29600      */
29601     restoreState : function(provider){
29602         if(!provider){
29603             provider = Roo.state.Manager;
29604         }
29605         var sm = new Roo.LayoutStateManager();
29606         sm.init(this, provider);
29607     },
29608
29609     /**
29610      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29611      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29612      * a valid ContentPanel config object.  Example:
29613      * <pre><code>
29614 // Create the main layout
29615 var layout = new Roo.BorderLayout('main-ct', {
29616     west: {
29617         split:true,
29618         minSize: 175,
29619         titlebar: true
29620     },
29621     center: {
29622         title:'Components'
29623     }
29624 }, 'main-ct');
29625
29626 // Create and add multiple ContentPanels at once via configs
29627 layout.batchAdd({
29628    west: {
29629        id: 'source-files',
29630        autoCreate:true,
29631        title:'Ext Source Files',
29632        autoScroll:true,
29633        fitToFrame:true
29634    },
29635    center : {
29636        el: cview,
29637        autoScroll:true,
29638        fitToFrame:true,
29639        toolbar: tb,
29640        resizeEl:'cbody'
29641    }
29642 });
29643 </code></pre>
29644      * @param {Object} regions An object containing ContentPanel configs by region name
29645      */
29646     batchAdd : function(regions){
29647         this.beginUpdate();
29648         for(var rname in regions){
29649             var lr = this.regions[rname];
29650             if(lr){
29651                 this.addTypedPanels(lr, regions[rname]);
29652             }
29653         }
29654         this.endUpdate();
29655     },
29656
29657     // private
29658     addTypedPanels : function(lr, ps){
29659         if(typeof ps == 'string'){
29660             lr.add(new Roo.ContentPanel(ps));
29661         }
29662         else if(ps instanceof Array){
29663             for(var i =0, len = ps.length; i < len; i++){
29664                 this.addTypedPanels(lr, ps[i]);
29665             }
29666         }
29667         else if(!ps.events){ // raw config?
29668             var el = ps.el;
29669             delete ps.el; // prevent conflict
29670             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29671         }
29672         else {  // panel object assumed!
29673             lr.add(ps);
29674         }
29675     },
29676     /**
29677      * Adds a xtype elements to the layout.
29678      * <pre><code>
29679
29680 layout.addxtype({
29681        xtype : 'ContentPanel',
29682        region: 'west',
29683        items: [ .... ]
29684    }
29685 );
29686
29687 layout.addxtype({
29688         xtype : 'NestedLayoutPanel',
29689         region: 'west',
29690         layout: {
29691            center: { },
29692            west: { }   
29693         },
29694         items : [ ... list of content panels or nested layout panels.. ]
29695    }
29696 );
29697 </code></pre>
29698      * @param {Object} cfg Xtype definition of item to add.
29699      */
29700     addxtype : function(cfg)
29701     {
29702         // basically accepts a pannel...
29703         // can accept a layout region..!?!?
29704         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29705         
29706         if (!cfg.xtype.match(/Panel$/)) {
29707             return false;
29708         }
29709         var ret = false;
29710         
29711         if (typeof(cfg.region) == 'undefined') {
29712             Roo.log("Failed to add Panel, region was not set");
29713             Roo.log(cfg);
29714             return false;
29715         }
29716         var region = cfg.region;
29717         delete cfg.region;
29718         
29719           
29720         var xitems = [];
29721         if (cfg.items) {
29722             xitems = cfg.items;
29723             delete cfg.items;
29724         }
29725         var nb = false;
29726         
29727         switch(cfg.xtype) 
29728         {
29729             case 'ContentPanel':  // ContentPanel (el, cfg)
29730             case 'ScrollPanel':  // ContentPanel (el, cfg)
29731             case 'ViewPanel': 
29732                 if(cfg.autoCreate) {
29733                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29734                 } else {
29735                     var el = this.el.createChild();
29736                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29737                 }
29738                 
29739                 this.add(region, ret);
29740                 break;
29741             
29742             
29743             case 'TreePanel': // our new panel!
29744                 cfg.el = this.el.createChild();
29745                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29746                 this.add(region, ret);
29747                 break;
29748             
29749             case 'NestedLayoutPanel': 
29750                 // create a new Layout (which is  a Border Layout...
29751                 var el = this.el.createChild();
29752                 var clayout = cfg.layout;
29753                 delete cfg.layout;
29754                 clayout.items   = clayout.items  || [];
29755                 // replace this exitems with the clayout ones..
29756                 xitems = clayout.items;
29757                  
29758                 
29759                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29760                     cfg.background = false;
29761                 }
29762                 var layout = new Roo.BorderLayout(el, clayout);
29763                 
29764                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29765                 //console.log('adding nested layout panel '  + cfg.toSource());
29766                 this.add(region, ret);
29767                 nb = {}; /// find first...
29768                 break;
29769                 
29770             case 'GridPanel': 
29771             
29772                 // needs grid and region
29773                 
29774                 //var el = this.getRegion(region).el.createChild();
29775                 var el = this.el.createChild();
29776                 // create the grid first...
29777                 
29778                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29779                 delete cfg.grid;
29780                 if (region == 'center' && this.active ) {
29781                     cfg.background = false;
29782                 }
29783                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29784                 
29785                 this.add(region, ret);
29786                 if (cfg.background) {
29787                     ret.on('activate', function(gp) {
29788                         if (!gp.grid.rendered) {
29789                             gp.grid.render();
29790                         }
29791                     });
29792                 } else {
29793                     grid.render();
29794                 }
29795                 break;
29796            
29797            
29798            
29799                 
29800                 
29801                 
29802             default:
29803                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29804                     
29805                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29806                     this.add(region, ret);
29807                 } else {
29808                 
29809                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29810                     return null;
29811                 }
29812                 
29813              // GridPanel (grid, cfg)
29814             
29815         }
29816         this.beginUpdate();
29817         // add children..
29818         var region = '';
29819         var abn = {};
29820         Roo.each(xitems, function(i)  {
29821             region = nb && i.region ? i.region : false;
29822             
29823             var add = ret.addxtype(i);
29824            
29825             if (region) {
29826                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29827                 if (!i.background) {
29828                     abn[region] = nb[region] ;
29829                 }
29830             }
29831             
29832         });
29833         this.endUpdate();
29834
29835         // make the last non-background panel active..
29836         //if (nb) { Roo.log(abn); }
29837         if (nb) {
29838             
29839             for(var r in abn) {
29840                 region = this.getRegion(r);
29841                 if (region) {
29842                     // tried using nb[r], but it does not work..
29843                      
29844                     region.showPanel(abn[r]);
29845                    
29846                 }
29847             }
29848         }
29849         return ret;
29850         
29851     }
29852 });
29853
29854 /**
29855  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29856  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29857  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29858  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29859  * <pre><code>
29860 // shorthand
29861 var CP = Roo.ContentPanel;
29862
29863 var layout = Roo.BorderLayout.create({
29864     north: {
29865         initialSize: 25,
29866         titlebar: false,
29867         panels: [new CP("north", "North")]
29868     },
29869     west: {
29870         split:true,
29871         initialSize: 200,
29872         minSize: 175,
29873         maxSize: 400,
29874         titlebar: true,
29875         collapsible: true,
29876         panels: [new CP("west", {title: "West"})]
29877     },
29878     east: {
29879         split:true,
29880         initialSize: 202,
29881         minSize: 175,
29882         maxSize: 400,
29883         titlebar: true,
29884         collapsible: true,
29885         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29886     },
29887     south: {
29888         split:true,
29889         initialSize: 100,
29890         minSize: 100,
29891         maxSize: 200,
29892         titlebar: true,
29893         collapsible: true,
29894         panels: [new CP("south", {title: "South", closable: true})]
29895     },
29896     center: {
29897         titlebar: true,
29898         autoScroll:true,
29899         resizeTabs: true,
29900         minTabWidth: 50,
29901         preferredTabWidth: 150,
29902         panels: [
29903             new CP("center1", {title: "Close Me", closable: true}),
29904             new CP("center2", {title: "Center Panel", closable: false})
29905         ]
29906     }
29907 }, document.body);
29908
29909 layout.getRegion("center").showPanel("center1");
29910 </code></pre>
29911  * @param config
29912  * @param targetEl
29913  */
29914 Roo.BorderLayout.create = function(config, targetEl){
29915     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29916     layout.beginUpdate();
29917     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29918     for(var j = 0, jlen = regions.length; j < jlen; j++){
29919         var lr = regions[j];
29920         if(layout.regions[lr] && config[lr].panels){
29921             var r = layout.regions[lr];
29922             var ps = config[lr].panels;
29923             layout.addTypedPanels(r, ps);
29924         }
29925     }
29926     layout.endUpdate();
29927     return layout;
29928 };
29929
29930 // private
29931 Roo.BorderLayout.RegionFactory = {
29932     // private
29933     validRegions : ["north","south","east","west","center"],
29934
29935     // private
29936     create : function(target, mgr, config){
29937         target = target.toLowerCase();
29938         if(config.lightweight || config.basic){
29939             return new Roo.BasicLayoutRegion(mgr, config, target);
29940         }
29941         switch(target){
29942             case "north":
29943                 return new Roo.NorthLayoutRegion(mgr, config);
29944             case "south":
29945                 return new Roo.SouthLayoutRegion(mgr, config);
29946             case "east":
29947                 return new Roo.EastLayoutRegion(mgr, config);
29948             case "west":
29949                 return new Roo.WestLayoutRegion(mgr, config);
29950             case "center":
29951                 return new Roo.CenterLayoutRegion(mgr, config);
29952         }
29953         throw 'Layout region "'+target+'" not supported.';
29954     }
29955 };/*
29956  * Based on:
29957  * Ext JS Library 1.1.1
29958  * Copyright(c) 2006-2007, Ext JS, LLC.
29959  *
29960  * Originally Released Under LGPL - original licence link has changed is not relivant.
29961  *
29962  * Fork - LGPL
29963  * <script type="text/javascript">
29964  */
29965  
29966 /**
29967  * @class Roo.BasicLayoutRegion
29968  * @extends Roo.util.Observable
29969  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29970  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29971  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29972  */
29973 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29974     this.mgr = mgr;
29975     this.position  = pos;
29976     this.events = {
29977         /**
29978          * @scope Roo.BasicLayoutRegion
29979          */
29980         
29981         /**
29982          * @event beforeremove
29983          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29984          * @param {Roo.LayoutRegion} this
29985          * @param {Roo.ContentPanel} panel The panel
29986          * @param {Object} e The cancel event object
29987          */
29988         "beforeremove" : true,
29989         /**
29990          * @event invalidated
29991          * Fires when the layout for this region is changed.
29992          * @param {Roo.LayoutRegion} this
29993          */
29994         "invalidated" : true,
29995         /**
29996          * @event visibilitychange
29997          * Fires when this region is shown or hidden 
29998          * @param {Roo.LayoutRegion} this
29999          * @param {Boolean} visibility true or false
30000          */
30001         "visibilitychange" : true,
30002         /**
30003          * @event paneladded
30004          * Fires when a panel is added. 
30005          * @param {Roo.LayoutRegion} this
30006          * @param {Roo.ContentPanel} panel The panel
30007          */
30008         "paneladded" : true,
30009         /**
30010          * @event panelremoved
30011          * Fires when a panel is removed. 
30012          * @param {Roo.LayoutRegion} this
30013          * @param {Roo.ContentPanel} panel The panel
30014          */
30015         "panelremoved" : true,
30016         /**
30017          * @event beforecollapse
30018          * Fires when this region before collapse.
30019          * @param {Roo.LayoutRegion} this
30020          */
30021         "beforecollapse" : true,
30022         /**
30023          * @event collapsed
30024          * Fires when this region is collapsed.
30025          * @param {Roo.LayoutRegion} this
30026          */
30027         "collapsed" : true,
30028         /**
30029          * @event expanded
30030          * Fires when this region is expanded.
30031          * @param {Roo.LayoutRegion} this
30032          */
30033         "expanded" : true,
30034         /**
30035          * @event slideshow
30036          * Fires when this region is slid into view.
30037          * @param {Roo.LayoutRegion} this
30038          */
30039         "slideshow" : true,
30040         /**
30041          * @event slidehide
30042          * Fires when this region slides out of view. 
30043          * @param {Roo.LayoutRegion} this
30044          */
30045         "slidehide" : true,
30046         /**
30047          * @event panelactivated
30048          * Fires when a panel is activated. 
30049          * @param {Roo.LayoutRegion} this
30050          * @param {Roo.ContentPanel} panel The activated panel
30051          */
30052         "panelactivated" : true,
30053         /**
30054          * @event resized
30055          * Fires when the user resizes this region. 
30056          * @param {Roo.LayoutRegion} this
30057          * @param {Number} newSize The new size (width for east/west, height for north/south)
30058          */
30059         "resized" : true
30060     };
30061     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30062     this.panels = new Roo.util.MixedCollection();
30063     this.panels.getKey = this.getPanelId.createDelegate(this);
30064     this.box = null;
30065     this.activePanel = null;
30066     // ensure listeners are added...
30067     
30068     if (config.listeners || config.events) {
30069         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30070             listeners : config.listeners || {},
30071             events : config.events || {}
30072         });
30073     }
30074     
30075     if(skipConfig !== true){
30076         this.applyConfig(config);
30077     }
30078 };
30079
30080 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30081     getPanelId : function(p){
30082         return p.getId();
30083     },
30084     
30085     applyConfig : function(config){
30086         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30087         this.config = config;
30088         
30089     },
30090     
30091     /**
30092      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30093      * the width, for horizontal (north, south) the height.
30094      * @param {Number} newSize The new width or height
30095      */
30096     resizeTo : function(newSize){
30097         var el = this.el ? this.el :
30098                  (this.activePanel ? this.activePanel.getEl() : null);
30099         if(el){
30100             switch(this.position){
30101                 case "east":
30102                 case "west":
30103                     el.setWidth(newSize);
30104                     this.fireEvent("resized", this, newSize);
30105                 break;
30106                 case "north":
30107                 case "south":
30108                     el.setHeight(newSize);
30109                     this.fireEvent("resized", this, newSize);
30110                 break;                
30111             }
30112         }
30113     },
30114     
30115     getBox : function(){
30116         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30117     },
30118     
30119     getMargins : function(){
30120         return this.margins;
30121     },
30122     
30123     updateBox : function(box){
30124         this.box = box;
30125         var el = this.activePanel.getEl();
30126         el.dom.style.left = box.x + "px";
30127         el.dom.style.top = box.y + "px";
30128         this.activePanel.setSize(box.width, box.height);
30129     },
30130     
30131     /**
30132      * Returns the container element for this region.
30133      * @return {Roo.Element}
30134      */
30135     getEl : function(){
30136         return this.activePanel;
30137     },
30138     
30139     /**
30140      * Returns true if this region is currently visible.
30141      * @return {Boolean}
30142      */
30143     isVisible : function(){
30144         return this.activePanel ? true : false;
30145     },
30146     
30147     setActivePanel : function(panel){
30148         panel = this.getPanel(panel);
30149         if(this.activePanel && this.activePanel != panel){
30150             this.activePanel.setActiveState(false);
30151             this.activePanel.getEl().setLeftTop(-10000,-10000);
30152         }
30153         this.activePanel = panel;
30154         panel.setActiveState(true);
30155         if(this.box){
30156             panel.setSize(this.box.width, this.box.height);
30157         }
30158         this.fireEvent("panelactivated", this, panel);
30159         this.fireEvent("invalidated");
30160     },
30161     
30162     /**
30163      * Show the specified panel.
30164      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30165      * @return {Roo.ContentPanel} The shown panel or null
30166      */
30167     showPanel : function(panel){
30168         if(panel = this.getPanel(panel)){
30169             this.setActivePanel(panel);
30170         }
30171         return panel;
30172     },
30173     
30174     /**
30175      * Get the active panel for this region.
30176      * @return {Roo.ContentPanel} The active panel or null
30177      */
30178     getActivePanel : function(){
30179         return this.activePanel;
30180     },
30181     
30182     /**
30183      * Add the passed ContentPanel(s)
30184      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30185      * @return {Roo.ContentPanel} The panel added (if only one was added)
30186      */
30187     add : function(panel){
30188         if(arguments.length > 1){
30189             for(var i = 0, len = arguments.length; i < len; i++) {
30190                 this.add(arguments[i]);
30191             }
30192             return null;
30193         }
30194         if(this.hasPanel(panel)){
30195             this.showPanel(panel);
30196             return panel;
30197         }
30198         var el = panel.getEl();
30199         if(el.dom.parentNode != this.mgr.el.dom){
30200             this.mgr.el.dom.appendChild(el.dom);
30201         }
30202         if(panel.setRegion){
30203             panel.setRegion(this);
30204         }
30205         this.panels.add(panel);
30206         el.setStyle("position", "absolute");
30207         if(!panel.background){
30208             this.setActivePanel(panel);
30209             if(this.config.initialSize && this.panels.getCount()==1){
30210                 this.resizeTo(this.config.initialSize);
30211             }
30212         }
30213         this.fireEvent("paneladded", this, panel);
30214         return panel;
30215     },
30216     
30217     /**
30218      * Returns true if the panel is in this region.
30219      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30220      * @return {Boolean}
30221      */
30222     hasPanel : function(panel){
30223         if(typeof panel == "object"){ // must be panel obj
30224             panel = panel.getId();
30225         }
30226         return this.getPanel(panel) ? true : false;
30227     },
30228     
30229     /**
30230      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30231      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30232      * @param {Boolean} preservePanel Overrides the config preservePanel option
30233      * @return {Roo.ContentPanel} The panel that was removed
30234      */
30235     remove : function(panel, preservePanel){
30236         panel = this.getPanel(panel);
30237         if(!panel){
30238             return null;
30239         }
30240         var e = {};
30241         this.fireEvent("beforeremove", this, panel, e);
30242         if(e.cancel === true){
30243             return null;
30244         }
30245         var panelId = panel.getId();
30246         this.panels.removeKey(panelId);
30247         return panel;
30248     },
30249     
30250     /**
30251      * Returns the panel specified or null if it's not in this region.
30252      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30253      * @return {Roo.ContentPanel}
30254      */
30255     getPanel : function(id){
30256         if(typeof id == "object"){ // must be panel obj
30257             return id;
30258         }
30259         return this.panels.get(id);
30260     },
30261     
30262     /**
30263      * Returns this regions position (north/south/east/west/center).
30264      * @return {String} 
30265      */
30266     getPosition: function(){
30267         return this.position;    
30268     }
30269 });/*
30270  * Based on:
30271  * Ext JS Library 1.1.1
30272  * Copyright(c) 2006-2007, Ext JS, LLC.
30273  *
30274  * Originally Released Under LGPL - original licence link has changed is not relivant.
30275  *
30276  * Fork - LGPL
30277  * <script type="text/javascript">
30278  */
30279  
30280 /**
30281  * @class Roo.LayoutRegion
30282  * @extends Roo.BasicLayoutRegion
30283  * This class represents a region in a layout manager.
30284  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30285  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30286  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30287  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30288  * @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})
30289  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30290  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30291  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30292  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30293  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30294  * @cfg {String}    title           The title for the region (overrides panel titles)
30295  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30296  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30297  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30298  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30299  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30300  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30301  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30302  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30303  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30304  * @cfg {Boolean}   showPin         True to show a pin button
30305  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30306  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30307  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30308  * @cfg {Number}    width           For East/West panels
30309  * @cfg {Number}    height          For North/South panels
30310  * @cfg {Boolean}   split           To show the splitter
30311  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30312  */
30313 Roo.LayoutRegion = function(mgr, config, pos){
30314     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30315     var dh = Roo.DomHelper;
30316     /** This region's container element 
30317     * @type Roo.Element */
30318     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30319     /** This region's title element 
30320     * @type Roo.Element */
30321
30322     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30323         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30324         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30325     ]}, true);
30326     this.titleEl.enableDisplayMode();
30327     /** This region's title text element 
30328     * @type HTMLElement */
30329     this.titleTextEl = this.titleEl.dom.firstChild;
30330     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30331     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30332     this.closeBtn.enableDisplayMode();
30333     this.closeBtn.on("click", this.closeClicked, this);
30334     this.closeBtn.hide();
30335
30336     this.createBody(config);
30337     this.visible = true;
30338     this.collapsed = false;
30339
30340     if(config.hideWhenEmpty){
30341         this.hide();
30342         this.on("paneladded", this.validateVisibility, this);
30343         this.on("panelremoved", this.validateVisibility, this);
30344     }
30345     this.applyConfig(config);
30346 };
30347
30348 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30349
30350     createBody : function(){
30351         /** This region's body element 
30352         * @type Roo.Element */
30353         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30354     },
30355
30356     applyConfig : function(c){
30357         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30358             var dh = Roo.DomHelper;
30359             if(c.titlebar !== false){
30360                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30361                 this.collapseBtn.on("click", this.collapse, this);
30362                 this.collapseBtn.enableDisplayMode();
30363
30364                 if(c.showPin === true || this.showPin){
30365                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30366                     this.stickBtn.enableDisplayMode();
30367                     this.stickBtn.on("click", this.expand, this);
30368                     this.stickBtn.hide();
30369                 }
30370             }
30371             /** This region's collapsed element
30372             * @type Roo.Element */
30373             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30374                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30375             ]}, true);
30376             if(c.floatable !== false){
30377                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30378                this.collapsedEl.on("click", this.collapseClick, this);
30379             }
30380
30381             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30382                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30383                    id: "message", unselectable: "on", style:{"float":"left"}});
30384                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30385              }
30386             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30387             this.expandBtn.on("click", this.expand, this);
30388         }
30389         if(this.collapseBtn){
30390             this.collapseBtn.setVisible(c.collapsible == true);
30391         }
30392         this.cmargins = c.cmargins || this.cmargins ||
30393                          (this.position == "west" || this.position == "east" ?
30394                              {top: 0, left: 2, right:2, bottom: 0} :
30395                              {top: 2, left: 0, right:0, bottom: 2});
30396         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30397         this.bottomTabs = c.tabPosition != "top";
30398         this.autoScroll = c.autoScroll || false;
30399         if(this.autoScroll){
30400             this.bodyEl.setStyle("overflow", "auto");
30401         }else{
30402             this.bodyEl.setStyle("overflow", "hidden");
30403         }
30404         //if(c.titlebar !== false){
30405             if((!c.titlebar && !c.title) || c.titlebar === false){
30406                 this.titleEl.hide();
30407             }else{
30408                 this.titleEl.show();
30409                 if(c.title){
30410                     this.titleTextEl.innerHTML = c.title;
30411                 }
30412             }
30413         //}
30414         this.duration = c.duration || .30;
30415         this.slideDuration = c.slideDuration || .45;
30416         this.config = c;
30417         if(c.collapsed){
30418             this.collapse(true);
30419         }
30420         if(c.hidden){
30421             this.hide();
30422         }
30423     },
30424     /**
30425      * Returns true if this region is currently visible.
30426      * @return {Boolean}
30427      */
30428     isVisible : function(){
30429         return this.visible;
30430     },
30431
30432     /**
30433      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30434      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30435      */
30436     setCollapsedTitle : function(title){
30437         title = title || "&#160;";
30438         if(this.collapsedTitleTextEl){
30439             this.collapsedTitleTextEl.innerHTML = title;
30440         }
30441     },
30442
30443     getBox : function(){
30444         var b;
30445         if(!this.collapsed){
30446             b = this.el.getBox(false, true);
30447         }else{
30448             b = this.collapsedEl.getBox(false, true);
30449         }
30450         return b;
30451     },
30452
30453     getMargins : function(){
30454         return this.collapsed ? this.cmargins : this.margins;
30455     },
30456
30457     highlight : function(){
30458         this.el.addClass("x-layout-panel-dragover");
30459     },
30460
30461     unhighlight : function(){
30462         this.el.removeClass("x-layout-panel-dragover");
30463     },
30464
30465     updateBox : function(box){
30466         this.box = box;
30467         if(!this.collapsed){
30468             this.el.dom.style.left = box.x + "px";
30469             this.el.dom.style.top = box.y + "px";
30470             this.updateBody(box.width, box.height);
30471         }else{
30472             this.collapsedEl.dom.style.left = box.x + "px";
30473             this.collapsedEl.dom.style.top = box.y + "px";
30474             this.collapsedEl.setSize(box.width, box.height);
30475         }
30476         if(this.tabs){
30477             this.tabs.autoSizeTabs();
30478         }
30479     },
30480
30481     updateBody : function(w, h){
30482         if(w !== null){
30483             this.el.setWidth(w);
30484             w -= this.el.getBorderWidth("rl");
30485             if(this.config.adjustments){
30486                 w += this.config.adjustments[0];
30487             }
30488         }
30489         if(h !== null){
30490             this.el.setHeight(h);
30491             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30492             h -= this.el.getBorderWidth("tb");
30493             if(this.config.adjustments){
30494                 h += this.config.adjustments[1];
30495             }
30496             this.bodyEl.setHeight(h);
30497             if(this.tabs){
30498                 h = this.tabs.syncHeight(h);
30499             }
30500         }
30501         if(this.panelSize){
30502             w = w !== null ? w : this.panelSize.width;
30503             h = h !== null ? h : this.panelSize.height;
30504         }
30505         if(this.activePanel){
30506             var el = this.activePanel.getEl();
30507             w = w !== null ? w : el.getWidth();
30508             h = h !== null ? h : el.getHeight();
30509             this.panelSize = {width: w, height: h};
30510             this.activePanel.setSize(w, h);
30511         }
30512         if(Roo.isIE && this.tabs){
30513             this.tabs.el.repaint();
30514         }
30515     },
30516
30517     /**
30518      * Returns the container element for this region.
30519      * @return {Roo.Element}
30520      */
30521     getEl : function(){
30522         return this.el;
30523     },
30524
30525     /**
30526      * Hides this region.
30527      */
30528     hide : function(){
30529         if(!this.collapsed){
30530             this.el.dom.style.left = "-2000px";
30531             this.el.hide();
30532         }else{
30533             this.collapsedEl.dom.style.left = "-2000px";
30534             this.collapsedEl.hide();
30535         }
30536         this.visible = false;
30537         this.fireEvent("visibilitychange", this, false);
30538     },
30539
30540     /**
30541      * Shows this region if it was previously hidden.
30542      */
30543     show : function(){
30544         if(!this.collapsed){
30545             this.el.show();
30546         }else{
30547             this.collapsedEl.show();
30548         }
30549         this.visible = true;
30550         this.fireEvent("visibilitychange", this, true);
30551     },
30552
30553     closeClicked : function(){
30554         if(this.activePanel){
30555             this.remove(this.activePanel);
30556         }
30557     },
30558
30559     collapseClick : function(e){
30560         if(this.isSlid){
30561            e.stopPropagation();
30562            this.slideIn();
30563         }else{
30564            e.stopPropagation();
30565            this.slideOut();
30566         }
30567     },
30568
30569     /**
30570      * Collapses this region.
30571      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30572      */
30573     collapse : function(skipAnim, skipCheck){
30574         if(this.collapsed) {
30575             return;
30576         }
30577         
30578         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30579             
30580             this.collapsed = true;
30581             if(this.split){
30582                 this.split.el.hide();
30583             }
30584             if(this.config.animate && skipAnim !== true){
30585                 this.fireEvent("invalidated", this);
30586                 this.animateCollapse();
30587             }else{
30588                 this.el.setLocation(-20000,-20000);
30589                 this.el.hide();
30590                 this.collapsedEl.show();
30591                 this.fireEvent("collapsed", this);
30592                 this.fireEvent("invalidated", this);
30593             }
30594         }
30595         
30596     },
30597
30598     animateCollapse : function(){
30599         // overridden
30600     },
30601
30602     /**
30603      * Expands this region if it was previously collapsed.
30604      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30605      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30606      */
30607     expand : function(e, skipAnim){
30608         if(e) {
30609             e.stopPropagation();
30610         }
30611         if(!this.collapsed || this.el.hasActiveFx()) {
30612             return;
30613         }
30614         if(this.isSlid){
30615             this.afterSlideIn();
30616             skipAnim = true;
30617         }
30618         this.collapsed = false;
30619         if(this.config.animate && skipAnim !== true){
30620             this.animateExpand();
30621         }else{
30622             this.el.show();
30623             if(this.split){
30624                 this.split.el.show();
30625             }
30626             this.collapsedEl.setLocation(-2000,-2000);
30627             this.collapsedEl.hide();
30628             this.fireEvent("invalidated", this);
30629             this.fireEvent("expanded", this);
30630         }
30631     },
30632
30633     animateExpand : function(){
30634         // overridden
30635     },
30636
30637     initTabs : function()
30638     {
30639         this.bodyEl.setStyle("overflow", "hidden");
30640         var ts = new Roo.TabPanel(
30641                 this.bodyEl.dom,
30642                 {
30643                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30644                     disableTooltips: this.config.disableTabTips,
30645                     toolbar : this.config.toolbar
30646                 }
30647         );
30648         if(this.config.hideTabs){
30649             ts.stripWrap.setDisplayed(false);
30650         }
30651         this.tabs = ts;
30652         ts.resizeTabs = this.config.resizeTabs === true;
30653         ts.minTabWidth = this.config.minTabWidth || 40;
30654         ts.maxTabWidth = this.config.maxTabWidth || 250;
30655         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30656         ts.monitorResize = false;
30657         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30658         ts.bodyEl.addClass('x-layout-tabs-body');
30659         this.panels.each(this.initPanelAsTab, this);
30660     },
30661
30662     initPanelAsTab : function(panel){
30663         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30664                     this.config.closeOnTab && panel.isClosable());
30665         if(panel.tabTip !== undefined){
30666             ti.setTooltip(panel.tabTip);
30667         }
30668         ti.on("activate", function(){
30669               this.setActivePanel(panel);
30670         }, this);
30671         if(this.config.closeOnTab){
30672             ti.on("beforeclose", function(t, e){
30673                 e.cancel = true;
30674                 this.remove(panel);
30675             }, this);
30676         }
30677         return ti;
30678     },
30679
30680     updatePanelTitle : function(panel, title){
30681         if(this.activePanel == panel){
30682             this.updateTitle(title);
30683         }
30684         if(this.tabs){
30685             var ti = this.tabs.getTab(panel.getEl().id);
30686             ti.setText(title);
30687             if(panel.tabTip !== undefined){
30688                 ti.setTooltip(panel.tabTip);
30689             }
30690         }
30691     },
30692
30693     updateTitle : function(title){
30694         if(this.titleTextEl && !this.config.title){
30695             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30696         }
30697     },
30698
30699     setActivePanel : function(panel){
30700         panel = this.getPanel(panel);
30701         if(this.activePanel && this.activePanel != panel){
30702             this.activePanel.setActiveState(false);
30703         }
30704         this.activePanel = panel;
30705         panel.setActiveState(true);
30706         if(this.panelSize){
30707             panel.setSize(this.panelSize.width, this.panelSize.height);
30708         }
30709         if(this.closeBtn){
30710             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30711         }
30712         this.updateTitle(panel.getTitle());
30713         if(this.tabs){
30714             this.fireEvent("invalidated", this);
30715         }
30716         this.fireEvent("panelactivated", this, panel);
30717     },
30718
30719     /**
30720      * Shows the specified panel.
30721      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30722      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30723      */
30724     showPanel : function(panel)
30725     {
30726         panel = this.getPanel(panel);
30727         if(panel){
30728             if(this.tabs){
30729                 var tab = this.tabs.getTab(panel.getEl().id);
30730                 if(tab.isHidden()){
30731                     this.tabs.unhideTab(tab.id);
30732                 }
30733                 tab.activate();
30734             }else{
30735                 this.setActivePanel(panel);
30736             }
30737         }
30738         return panel;
30739     },
30740
30741     /**
30742      * Get the active panel for this region.
30743      * @return {Roo.ContentPanel} The active panel or null
30744      */
30745     getActivePanel : function(){
30746         return this.activePanel;
30747     },
30748
30749     validateVisibility : function(){
30750         if(this.panels.getCount() < 1){
30751             this.updateTitle("&#160;");
30752             this.closeBtn.hide();
30753             this.hide();
30754         }else{
30755             if(!this.isVisible()){
30756                 this.show();
30757             }
30758         }
30759     },
30760
30761     /**
30762      * Adds the passed ContentPanel(s) to this region.
30763      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30764      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30765      */
30766     add : function(panel){
30767         if(arguments.length > 1){
30768             for(var i = 0, len = arguments.length; i < len; i++) {
30769                 this.add(arguments[i]);
30770             }
30771             return null;
30772         }
30773         if(this.hasPanel(panel)){
30774             this.showPanel(panel);
30775             return panel;
30776         }
30777         panel.setRegion(this);
30778         this.panels.add(panel);
30779         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30780             this.bodyEl.dom.appendChild(panel.getEl().dom);
30781             if(panel.background !== true){
30782                 this.setActivePanel(panel);
30783             }
30784             this.fireEvent("paneladded", this, panel);
30785             return panel;
30786         }
30787         if(!this.tabs){
30788             this.initTabs();
30789         }else{
30790             this.initPanelAsTab(panel);
30791         }
30792         if(panel.background !== true){
30793             this.tabs.activate(panel.getEl().id);
30794         }
30795         this.fireEvent("paneladded", this, panel);
30796         return panel;
30797     },
30798
30799     /**
30800      * Hides the tab for the specified panel.
30801      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30802      */
30803     hidePanel : function(panel){
30804         if(this.tabs && (panel = this.getPanel(panel))){
30805             this.tabs.hideTab(panel.getEl().id);
30806         }
30807     },
30808
30809     /**
30810      * Unhides the tab for a previously hidden panel.
30811      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30812      */
30813     unhidePanel : function(panel){
30814         if(this.tabs && (panel = this.getPanel(panel))){
30815             this.tabs.unhideTab(panel.getEl().id);
30816         }
30817     },
30818
30819     clearPanels : function(){
30820         while(this.panels.getCount() > 0){
30821              this.remove(this.panels.first());
30822         }
30823     },
30824
30825     /**
30826      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30827      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30828      * @param {Boolean} preservePanel Overrides the config preservePanel option
30829      * @return {Roo.ContentPanel} The panel that was removed
30830      */
30831     remove : function(panel, preservePanel){
30832         panel = this.getPanel(panel);
30833         if(!panel){
30834             return null;
30835         }
30836         var e = {};
30837         this.fireEvent("beforeremove", this, panel, e);
30838         if(e.cancel === true){
30839             return null;
30840         }
30841         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30842         var panelId = panel.getId();
30843         this.panels.removeKey(panelId);
30844         if(preservePanel){
30845             document.body.appendChild(panel.getEl().dom);
30846         }
30847         if(this.tabs){
30848             this.tabs.removeTab(panel.getEl().id);
30849         }else if (!preservePanel){
30850             this.bodyEl.dom.removeChild(panel.getEl().dom);
30851         }
30852         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30853             var p = this.panels.first();
30854             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30855             tempEl.appendChild(p.getEl().dom);
30856             this.bodyEl.update("");
30857             this.bodyEl.dom.appendChild(p.getEl().dom);
30858             tempEl = null;
30859             this.updateTitle(p.getTitle());
30860             this.tabs = null;
30861             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30862             this.setActivePanel(p);
30863         }
30864         panel.setRegion(null);
30865         if(this.activePanel == panel){
30866             this.activePanel = null;
30867         }
30868         if(this.config.autoDestroy !== false && preservePanel !== true){
30869             try{panel.destroy();}catch(e){}
30870         }
30871         this.fireEvent("panelremoved", this, panel);
30872         return panel;
30873     },
30874
30875     /**
30876      * Returns the TabPanel component used by this region
30877      * @return {Roo.TabPanel}
30878      */
30879     getTabs : function(){
30880         return this.tabs;
30881     },
30882
30883     createTool : function(parentEl, className){
30884         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30885             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30886         btn.addClassOnOver("x-layout-tools-button-over");
30887         return btn;
30888     }
30889 });/*
30890  * Based on:
30891  * Ext JS Library 1.1.1
30892  * Copyright(c) 2006-2007, Ext JS, LLC.
30893  *
30894  * Originally Released Under LGPL - original licence link has changed is not relivant.
30895  *
30896  * Fork - LGPL
30897  * <script type="text/javascript">
30898  */
30899  
30900
30901
30902 /**
30903  * @class Roo.SplitLayoutRegion
30904  * @extends Roo.LayoutRegion
30905  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30906  */
30907 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30908     this.cursor = cursor;
30909     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30910 };
30911
30912 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30913     splitTip : "Drag to resize.",
30914     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30915     useSplitTips : false,
30916
30917     applyConfig : function(config){
30918         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30919         if(config.split){
30920             if(!this.split){
30921                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30922                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30923                 /** The SplitBar for this region 
30924                 * @type Roo.SplitBar */
30925                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30926                 this.split.on("moved", this.onSplitMove, this);
30927                 this.split.useShim = config.useShim === true;
30928                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30929                 if(this.useSplitTips){
30930                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30931                 }
30932                 if(config.collapsible){
30933                     this.split.el.on("dblclick", this.collapse,  this);
30934                 }
30935             }
30936             if(typeof config.minSize != "undefined"){
30937                 this.split.minSize = config.minSize;
30938             }
30939             if(typeof config.maxSize != "undefined"){
30940                 this.split.maxSize = config.maxSize;
30941             }
30942             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30943                 this.hideSplitter();
30944             }
30945         }
30946     },
30947
30948     getHMaxSize : function(){
30949          var cmax = this.config.maxSize || 10000;
30950          var center = this.mgr.getRegion("center");
30951          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30952     },
30953
30954     getVMaxSize : function(){
30955          var cmax = this.config.maxSize || 10000;
30956          var center = this.mgr.getRegion("center");
30957          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30958     },
30959
30960     onSplitMove : function(split, newSize){
30961         this.fireEvent("resized", this, newSize);
30962     },
30963     
30964     /** 
30965      * Returns the {@link Roo.SplitBar} for this region.
30966      * @return {Roo.SplitBar}
30967      */
30968     getSplitBar : function(){
30969         return this.split;
30970     },
30971     
30972     hide : function(){
30973         this.hideSplitter();
30974         Roo.SplitLayoutRegion.superclass.hide.call(this);
30975     },
30976
30977     hideSplitter : function(){
30978         if(this.split){
30979             this.split.el.setLocation(-2000,-2000);
30980             this.split.el.hide();
30981         }
30982     },
30983
30984     show : function(){
30985         if(this.split){
30986             this.split.el.show();
30987         }
30988         Roo.SplitLayoutRegion.superclass.show.call(this);
30989     },
30990     
30991     beforeSlide: function(){
30992         if(Roo.isGecko){// firefox overflow auto bug workaround
30993             this.bodyEl.clip();
30994             if(this.tabs) {
30995                 this.tabs.bodyEl.clip();
30996             }
30997             if(this.activePanel){
30998                 this.activePanel.getEl().clip();
30999                 
31000                 if(this.activePanel.beforeSlide){
31001                     this.activePanel.beforeSlide();
31002                 }
31003             }
31004         }
31005     },
31006     
31007     afterSlide : function(){
31008         if(Roo.isGecko){// firefox overflow auto bug workaround
31009             this.bodyEl.unclip();
31010             if(this.tabs) {
31011                 this.tabs.bodyEl.unclip();
31012             }
31013             if(this.activePanel){
31014                 this.activePanel.getEl().unclip();
31015                 if(this.activePanel.afterSlide){
31016                     this.activePanel.afterSlide();
31017                 }
31018             }
31019         }
31020     },
31021
31022     initAutoHide : function(){
31023         if(this.autoHide !== false){
31024             if(!this.autoHideHd){
31025                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31026                 this.autoHideHd = {
31027                     "mouseout": function(e){
31028                         if(!e.within(this.el, true)){
31029                             st.delay(500);
31030                         }
31031                     },
31032                     "mouseover" : function(e){
31033                         st.cancel();
31034                     },
31035                     scope : this
31036                 };
31037             }
31038             this.el.on(this.autoHideHd);
31039         }
31040     },
31041
31042     clearAutoHide : function(){
31043         if(this.autoHide !== false){
31044             this.el.un("mouseout", this.autoHideHd.mouseout);
31045             this.el.un("mouseover", this.autoHideHd.mouseover);
31046         }
31047     },
31048
31049     clearMonitor : function(){
31050         Roo.get(document).un("click", this.slideInIf, this);
31051     },
31052
31053     // these names are backwards but not changed for compat
31054     slideOut : function(){
31055         if(this.isSlid || this.el.hasActiveFx()){
31056             return;
31057         }
31058         this.isSlid = true;
31059         if(this.collapseBtn){
31060             this.collapseBtn.hide();
31061         }
31062         this.closeBtnState = this.closeBtn.getStyle('display');
31063         this.closeBtn.hide();
31064         if(this.stickBtn){
31065             this.stickBtn.show();
31066         }
31067         this.el.show();
31068         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31069         this.beforeSlide();
31070         this.el.setStyle("z-index", 10001);
31071         this.el.slideIn(this.getSlideAnchor(), {
31072             callback: function(){
31073                 this.afterSlide();
31074                 this.initAutoHide();
31075                 Roo.get(document).on("click", this.slideInIf, this);
31076                 this.fireEvent("slideshow", this);
31077             },
31078             scope: this,
31079             block: true
31080         });
31081     },
31082
31083     afterSlideIn : function(){
31084         this.clearAutoHide();
31085         this.isSlid = false;
31086         this.clearMonitor();
31087         this.el.setStyle("z-index", "");
31088         if(this.collapseBtn){
31089             this.collapseBtn.show();
31090         }
31091         this.closeBtn.setStyle('display', this.closeBtnState);
31092         if(this.stickBtn){
31093             this.stickBtn.hide();
31094         }
31095         this.fireEvent("slidehide", this);
31096     },
31097
31098     slideIn : function(cb){
31099         if(!this.isSlid || this.el.hasActiveFx()){
31100             Roo.callback(cb);
31101             return;
31102         }
31103         this.isSlid = false;
31104         this.beforeSlide();
31105         this.el.slideOut(this.getSlideAnchor(), {
31106             callback: function(){
31107                 this.el.setLeftTop(-10000, -10000);
31108                 this.afterSlide();
31109                 this.afterSlideIn();
31110                 Roo.callback(cb);
31111             },
31112             scope: this,
31113             block: true
31114         });
31115     },
31116     
31117     slideInIf : function(e){
31118         if(!e.within(this.el)){
31119             this.slideIn();
31120         }
31121     },
31122
31123     animateCollapse : function(){
31124         this.beforeSlide();
31125         this.el.setStyle("z-index", 20000);
31126         var anchor = this.getSlideAnchor();
31127         this.el.slideOut(anchor, {
31128             callback : function(){
31129                 this.el.setStyle("z-index", "");
31130                 this.collapsedEl.slideIn(anchor, {duration:.3});
31131                 this.afterSlide();
31132                 this.el.setLocation(-10000,-10000);
31133                 this.el.hide();
31134                 this.fireEvent("collapsed", this);
31135             },
31136             scope: this,
31137             block: true
31138         });
31139     },
31140
31141     animateExpand : function(){
31142         this.beforeSlide();
31143         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31144         this.el.setStyle("z-index", 20000);
31145         this.collapsedEl.hide({
31146             duration:.1
31147         });
31148         this.el.slideIn(this.getSlideAnchor(), {
31149             callback : function(){
31150                 this.el.setStyle("z-index", "");
31151                 this.afterSlide();
31152                 if(this.split){
31153                     this.split.el.show();
31154                 }
31155                 this.fireEvent("invalidated", this);
31156                 this.fireEvent("expanded", this);
31157             },
31158             scope: this,
31159             block: true
31160         });
31161     },
31162
31163     anchors : {
31164         "west" : "left",
31165         "east" : "right",
31166         "north" : "top",
31167         "south" : "bottom"
31168     },
31169
31170     sanchors : {
31171         "west" : "l",
31172         "east" : "r",
31173         "north" : "t",
31174         "south" : "b"
31175     },
31176
31177     canchors : {
31178         "west" : "tl-tr",
31179         "east" : "tr-tl",
31180         "north" : "tl-bl",
31181         "south" : "bl-tl"
31182     },
31183
31184     getAnchor : function(){
31185         return this.anchors[this.position];
31186     },
31187
31188     getCollapseAnchor : function(){
31189         return this.canchors[this.position];
31190     },
31191
31192     getSlideAnchor : function(){
31193         return this.sanchors[this.position];
31194     },
31195
31196     getAlignAdj : function(){
31197         var cm = this.cmargins;
31198         switch(this.position){
31199             case "west":
31200                 return [0, 0];
31201             break;
31202             case "east":
31203                 return [0, 0];
31204             break;
31205             case "north":
31206                 return [0, 0];
31207             break;
31208             case "south":
31209                 return [0, 0];
31210             break;
31211         }
31212     },
31213
31214     getExpandAdj : function(){
31215         var c = this.collapsedEl, cm = this.cmargins;
31216         switch(this.position){
31217             case "west":
31218                 return [-(cm.right+c.getWidth()+cm.left), 0];
31219             break;
31220             case "east":
31221                 return [cm.right+c.getWidth()+cm.left, 0];
31222             break;
31223             case "north":
31224                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31225             break;
31226             case "south":
31227                 return [0, cm.top+cm.bottom+c.getHeight()];
31228             break;
31229         }
31230     }
31231 });/*
31232  * Based on:
31233  * Ext JS Library 1.1.1
31234  * Copyright(c) 2006-2007, Ext JS, LLC.
31235  *
31236  * Originally Released Under LGPL - original licence link has changed is not relivant.
31237  *
31238  * Fork - LGPL
31239  * <script type="text/javascript">
31240  */
31241 /*
31242  * These classes are private internal classes
31243  */
31244 Roo.CenterLayoutRegion = function(mgr, config){
31245     Roo.LayoutRegion.call(this, mgr, config, "center");
31246     this.visible = true;
31247     this.minWidth = config.minWidth || 20;
31248     this.minHeight = config.minHeight || 20;
31249 };
31250
31251 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31252     hide : function(){
31253         // center panel can't be hidden
31254     },
31255     
31256     show : function(){
31257         // center panel can't be hidden
31258     },
31259     
31260     getMinWidth: function(){
31261         return this.minWidth;
31262     },
31263     
31264     getMinHeight: function(){
31265         return this.minHeight;
31266     }
31267 });
31268
31269
31270 Roo.NorthLayoutRegion = function(mgr, config){
31271     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31272     if(this.split){
31273         this.split.placement = Roo.SplitBar.TOP;
31274         this.split.orientation = Roo.SplitBar.VERTICAL;
31275         this.split.el.addClass("x-layout-split-v");
31276     }
31277     var size = config.initialSize || config.height;
31278     if(typeof size != "undefined"){
31279         this.el.setHeight(size);
31280     }
31281 };
31282 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31283     orientation: Roo.SplitBar.VERTICAL,
31284     getBox : function(){
31285         if(this.collapsed){
31286             return this.collapsedEl.getBox();
31287         }
31288         var box = this.el.getBox();
31289         if(this.split){
31290             box.height += this.split.el.getHeight();
31291         }
31292         return box;
31293     },
31294     
31295     updateBox : function(box){
31296         if(this.split && !this.collapsed){
31297             box.height -= this.split.el.getHeight();
31298             this.split.el.setLeft(box.x);
31299             this.split.el.setTop(box.y+box.height);
31300             this.split.el.setWidth(box.width);
31301         }
31302         if(this.collapsed){
31303             this.updateBody(box.width, null);
31304         }
31305         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31306     }
31307 });
31308
31309 Roo.SouthLayoutRegion = function(mgr, config){
31310     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31311     if(this.split){
31312         this.split.placement = Roo.SplitBar.BOTTOM;
31313         this.split.orientation = Roo.SplitBar.VERTICAL;
31314         this.split.el.addClass("x-layout-split-v");
31315     }
31316     var size = config.initialSize || config.height;
31317     if(typeof size != "undefined"){
31318         this.el.setHeight(size);
31319     }
31320 };
31321 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31322     orientation: Roo.SplitBar.VERTICAL,
31323     getBox : function(){
31324         if(this.collapsed){
31325             return this.collapsedEl.getBox();
31326         }
31327         var box = this.el.getBox();
31328         if(this.split){
31329             var sh = this.split.el.getHeight();
31330             box.height += sh;
31331             box.y -= sh;
31332         }
31333         return box;
31334     },
31335     
31336     updateBox : function(box){
31337         if(this.split && !this.collapsed){
31338             var sh = this.split.el.getHeight();
31339             box.height -= sh;
31340             box.y += sh;
31341             this.split.el.setLeft(box.x);
31342             this.split.el.setTop(box.y-sh);
31343             this.split.el.setWidth(box.width);
31344         }
31345         if(this.collapsed){
31346             this.updateBody(box.width, null);
31347         }
31348         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31349     }
31350 });
31351
31352 Roo.EastLayoutRegion = function(mgr, config){
31353     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31354     if(this.split){
31355         this.split.placement = Roo.SplitBar.RIGHT;
31356         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31357         this.split.el.addClass("x-layout-split-h");
31358     }
31359     var size = config.initialSize || config.width;
31360     if(typeof size != "undefined"){
31361         this.el.setWidth(size);
31362     }
31363 };
31364 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31365     orientation: Roo.SplitBar.HORIZONTAL,
31366     getBox : function(){
31367         if(this.collapsed){
31368             return this.collapsedEl.getBox();
31369         }
31370         var box = this.el.getBox();
31371         if(this.split){
31372             var sw = this.split.el.getWidth();
31373             box.width += sw;
31374             box.x -= sw;
31375         }
31376         return box;
31377     },
31378
31379     updateBox : function(box){
31380         if(this.split && !this.collapsed){
31381             var sw = this.split.el.getWidth();
31382             box.width -= sw;
31383             this.split.el.setLeft(box.x);
31384             this.split.el.setTop(box.y);
31385             this.split.el.setHeight(box.height);
31386             box.x += sw;
31387         }
31388         if(this.collapsed){
31389             this.updateBody(null, box.height);
31390         }
31391         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31392     }
31393 });
31394
31395 Roo.WestLayoutRegion = function(mgr, config){
31396     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31397     if(this.split){
31398         this.split.placement = Roo.SplitBar.LEFT;
31399         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31400         this.split.el.addClass("x-layout-split-h");
31401     }
31402     var size = config.initialSize || config.width;
31403     if(typeof size != "undefined"){
31404         this.el.setWidth(size);
31405     }
31406 };
31407 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31408     orientation: Roo.SplitBar.HORIZONTAL,
31409     getBox : function(){
31410         if(this.collapsed){
31411             return this.collapsedEl.getBox();
31412         }
31413         var box = this.el.getBox();
31414         if(this.split){
31415             box.width += this.split.el.getWidth();
31416         }
31417         return box;
31418     },
31419     
31420     updateBox : function(box){
31421         if(this.split && !this.collapsed){
31422             var sw = this.split.el.getWidth();
31423             box.width -= sw;
31424             this.split.el.setLeft(box.x+box.width);
31425             this.split.el.setTop(box.y);
31426             this.split.el.setHeight(box.height);
31427         }
31428         if(this.collapsed){
31429             this.updateBody(null, box.height);
31430         }
31431         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31432     }
31433 });
31434 /*
31435  * Based on:
31436  * Ext JS Library 1.1.1
31437  * Copyright(c) 2006-2007, Ext JS, LLC.
31438  *
31439  * Originally Released Under LGPL - original licence link has changed is not relivant.
31440  *
31441  * Fork - LGPL
31442  * <script type="text/javascript">
31443  */
31444  
31445  
31446 /*
31447  * Private internal class for reading and applying state
31448  */
31449 Roo.LayoutStateManager = function(layout){
31450      // default empty state
31451      this.state = {
31452         north: {},
31453         south: {},
31454         east: {},
31455         west: {}       
31456     };
31457 };
31458
31459 Roo.LayoutStateManager.prototype = {
31460     init : function(layout, provider){
31461         this.provider = provider;
31462         var state = provider.get(layout.id+"-layout-state");
31463         if(state){
31464             var wasUpdating = layout.isUpdating();
31465             if(!wasUpdating){
31466                 layout.beginUpdate();
31467             }
31468             for(var key in state){
31469                 if(typeof state[key] != "function"){
31470                     var rstate = state[key];
31471                     var r = layout.getRegion(key);
31472                     if(r && rstate){
31473                         if(rstate.size){
31474                             r.resizeTo(rstate.size);
31475                         }
31476                         if(rstate.collapsed == true){
31477                             r.collapse(true);
31478                         }else{
31479                             r.expand(null, true);
31480                         }
31481                     }
31482                 }
31483             }
31484             if(!wasUpdating){
31485                 layout.endUpdate();
31486             }
31487             this.state = state; 
31488         }
31489         this.layout = layout;
31490         layout.on("regionresized", this.onRegionResized, this);
31491         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31492         layout.on("regionexpanded", this.onRegionExpanded, this);
31493     },
31494     
31495     storeState : function(){
31496         this.provider.set(this.layout.id+"-layout-state", this.state);
31497     },
31498     
31499     onRegionResized : function(region, newSize){
31500         this.state[region.getPosition()].size = newSize;
31501         this.storeState();
31502     },
31503     
31504     onRegionCollapsed : function(region){
31505         this.state[region.getPosition()].collapsed = true;
31506         this.storeState();
31507     },
31508     
31509     onRegionExpanded : function(region){
31510         this.state[region.getPosition()].collapsed = false;
31511         this.storeState();
31512     }
31513 };/*
31514  * Based on:
31515  * Ext JS Library 1.1.1
31516  * Copyright(c) 2006-2007, Ext JS, LLC.
31517  *
31518  * Originally Released Under LGPL - original licence link has changed is not relivant.
31519  *
31520  * Fork - LGPL
31521  * <script type="text/javascript">
31522  */
31523 /**
31524  * @class Roo.ContentPanel
31525  * @extends Roo.util.Observable
31526  * A basic ContentPanel element.
31527  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31528  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31529  * @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
31530  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31531  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31532  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31533  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31534  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31535  * @cfg {String} title          The title for this panel
31536  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31537  * @cfg {String} url            Calls {@link #setUrl} with this value
31538  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31539  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31540  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31541  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31542
31543  * @constructor
31544  * Create a new ContentPanel.
31545  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31546  * @param {String/Object} config A string to set only the title or a config object
31547  * @param {String} content (optional) Set the HTML content for this panel
31548  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31549  */
31550 Roo.ContentPanel = function(el, config, content){
31551     
31552      
31553     /*
31554     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31555         config = el;
31556         el = Roo.id();
31557     }
31558     if (config && config.parentLayout) { 
31559         el = config.parentLayout.el.createChild(); 
31560     }
31561     */
31562     if(el.autoCreate){ // xtype is available if this is called from factory
31563         config = el;
31564         el = Roo.id();
31565     }
31566     this.el = Roo.get(el);
31567     if(!this.el && config && config.autoCreate){
31568         if(typeof config.autoCreate == "object"){
31569             if(!config.autoCreate.id){
31570                 config.autoCreate.id = config.id||el;
31571             }
31572             this.el = Roo.DomHelper.append(document.body,
31573                         config.autoCreate, true);
31574         }else{
31575             this.el = Roo.DomHelper.append(document.body,
31576                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31577         }
31578     }
31579     this.closable = false;
31580     this.loaded = false;
31581     this.active = false;
31582     if(typeof config == "string"){
31583         this.title = config;
31584     }else{
31585         Roo.apply(this, config);
31586     }
31587     
31588     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31589         this.wrapEl = this.el.wrap();
31590         this.toolbar.container = this.el.insertSibling(false, 'before');
31591         this.toolbar = new Roo.Toolbar(this.toolbar);
31592     }
31593     
31594     // xtype created footer. - not sure if will work as we normally have to render first..
31595     if (this.footer && !this.footer.el && this.footer.xtype) {
31596         if (!this.wrapEl) {
31597             this.wrapEl = this.el.wrap();
31598         }
31599     
31600         this.footer.container = this.wrapEl.createChild();
31601          
31602         this.footer = Roo.factory(this.footer, Roo);
31603         
31604     }
31605     
31606     if(this.resizeEl){
31607         this.resizeEl = Roo.get(this.resizeEl, true);
31608     }else{
31609         this.resizeEl = this.el;
31610     }
31611     // handle view.xtype
31612     
31613  
31614     
31615     
31616     this.addEvents({
31617         /**
31618          * @event activate
31619          * Fires when this panel is activated. 
31620          * @param {Roo.ContentPanel} this
31621          */
31622         "activate" : true,
31623         /**
31624          * @event deactivate
31625          * Fires when this panel is activated. 
31626          * @param {Roo.ContentPanel} this
31627          */
31628         "deactivate" : true,
31629
31630         /**
31631          * @event resize
31632          * Fires when this panel is resized if fitToFrame is true.
31633          * @param {Roo.ContentPanel} this
31634          * @param {Number} width The width after any component adjustments
31635          * @param {Number} height The height after any component adjustments
31636          */
31637         "resize" : true,
31638         
31639          /**
31640          * @event render
31641          * Fires when this tab is created
31642          * @param {Roo.ContentPanel} this
31643          */
31644         "render" : true
31645          
31646         
31647     });
31648     
31649
31650     
31651     
31652     if(this.autoScroll){
31653         this.resizeEl.setStyle("overflow", "auto");
31654     } else {
31655         // fix randome scrolling
31656         this.el.on('scroll', function() {
31657             Roo.log('fix random scolling');
31658             this.scrollTo('top',0); 
31659         });
31660     }
31661     content = content || this.content;
31662     if(content){
31663         this.setContent(content);
31664     }
31665     if(config && config.url){
31666         this.setUrl(this.url, this.params, this.loadOnce);
31667     }
31668     
31669     
31670     
31671     Roo.ContentPanel.superclass.constructor.call(this);
31672     
31673     if (this.view && typeof(this.view.xtype) != 'undefined') {
31674         this.view.el = this.el.appendChild(document.createElement("div"));
31675         this.view = Roo.factory(this.view); 
31676         this.view.render  &&  this.view.render(false, '');  
31677     }
31678     
31679     
31680     this.fireEvent('render', this);
31681 };
31682
31683 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31684     tabTip:'',
31685     setRegion : function(region){
31686         this.region = region;
31687         if(region){
31688            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31689         }else{
31690            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31691         } 
31692     },
31693     
31694     /**
31695      * Returns the toolbar for this Panel if one was configured. 
31696      * @return {Roo.Toolbar} 
31697      */
31698     getToolbar : function(){
31699         return this.toolbar;
31700     },
31701     
31702     setActiveState : function(active){
31703         this.active = active;
31704         if(!active){
31705             this.fireEvent("deactivate", this);
31706         }else{
31707             this.fireEvent("activate", this);
31708         }
31709     },
31710     /**
31711      * Updates this panel's element
31712      * @param {String} content The new content
31713      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31714     */
31715     setContent : function(content, loadScripts){
31716         this.el.update(content, loadScripts);
31717     },
31718
31719     ignoreResize : function(w, h){
31720         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31721             return true;
31722         }else{
31723             this.lastSize = {width: w, height: h};
31724             return false;
31725         }
31726     },
31727     /**
31728      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31729      * @return {Roo.UpdateManager} The UpdateManager
31730      */
31731     getUpdateManager : function(){
31732         return this.el.getUpdateManager();
31733     },
31734      /**
31735      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31736      * @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:
31737 <pre><code>
31738 panel.load({
31739     url: "your-url.php",
31740     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31741     callback: yourFunction,
31742     scope: yourObject, //(optional scope)
31743     discardUrl: false,
31744     nocache: false,
31745     text: "Loading...",
31746     timeout: 30,
31747     scripts: false
31748 });
31749 </code></pre>
31750      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31751      * 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.
31752      * @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}
31753      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31754      * @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.
31755      * @return {Roo.ContentPanel} this
31756      */
31757     load : function(){
31758         var um = this.el.getUpdateManager();
31759         um.update.apply(um, arguments);
31760         return this;
31761     },
31762
31763
31764     /**
31765      * 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.
31766      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31767      * @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)
31768      * @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)
31769      * @return {Roo.UpdateManager} The UpdateManager
31770      */
31771     setUrl : function(url, params, loadOnce){
31772         if(this.refreshDelegate){
31773             this.removeListener("activate", this.refreshDelegate);
31774         }
31775         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31776         this.on("activate", this.refreshDelegate);
31777         return this.el.getUpdateManager();
31778     },
31779     
31780     _handleRefresh : function(url, params, loadOnce){
31781         if(!loadOnce || !this.loaded){
31782             var updater = this.el.getUpdateManager();
31783             updater.update(url, params, this._setLoaded.createDelegate(this));
31784         }
31785     },
31786     
31787     _setLoaded : function(){
31788         this.loaded = true;
31789     }, 
31790     
31791     /**
31792      * Returns this panel's id
31793      * @return {String} 
31794      */
31795     getId : function(){
31796         return this.el.id;
31797     },
31798     
31799     /** 
31800      * Returns this panel's element - used by regiosn to add.
31801      * @return {Roo.Element} 
31802      */
31803     getEl : function(){
31804         return this.wrapEl || this.el;
31805     },
31806     
31807     adjustForComponents : function(width, height)
31808     {
31809         //Roo.log('adjustForComponents ');
31810         if(this.resizeEl != this.el){
31811             width -= this.el.getFrameWidth('lr');
31812             height -= this.el.getFrameWidth('tb');
31813         }
31814         if(this.toolbar){
31815             var te = this.toolbar.getEl();
31816             height -= te.getHeight();
31817             te.setWidth(width);
31818         }
31819         if(this.footer){
31820             var te = this.footer.getEl();
31821             //Roo.log("footer:" + te.getHeight());
31822             
31823             height -= te.getHeight();
31824             te.setWidth(width);
31825         }
31826         
31827         
31828         if(this.adjustments){
31829             width += this.adjustments[0];
31830             height += this.adjustments[1];
31831         }
31832         return {"width": width, "height": height};
31833     },
31834     
31835     setSize : function(width, height){
31836         if(this.fitToFrame && !this.ignoreResize(width, height)){
31837             if(this.fitContainer && this.resizeEl != this.el){
31838                 this.el.setSize(width, height);
31839             }
31840             var size = this.adjustForComponents(width, height);
31841             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31842             this.fireEvent('resize', this, size.width, size.height);
31843         }
31844     },
31845     
31846     /**
31847      * Returns this panel's title
31848      * @return {String} 
31849      */
31850     getTitle : function(){
31851         return this.title;
31852     },
31853     
31854     /**
31855      * Set this panel's title
31856      * @param {String} title
31857      */
31858     setTitle : function(title){
31859         this.title = title;
31860         if(this.region){
31861             this.region.updatePanelTitle(this, title);
31862         }
31863     },
31864     
31865     /**
31866      * Returns true is this panel was configured to be closable
31867      * @return {Boolean} 
31868      */
31869     isClosable : function(){
31870         return this.closable;
31871     },
31872     
31873     beforeSlide : function(){
31874         this.el.clip();
31875         this.resizeEl.clip();
31876     },
31877     
31878     afterSlide : function(){
31879         this.el.unclip();
31880         this.resizeEl.unclip();
31881     },
31882     
31883     /**
31884      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31885      *   Will fail silently if the {@link #setUrl} method has not been called.
31886      *   This does not activate the panel, just updates its content.
31887      */
31888     refresh : function(){
31889         if(this.refreshDelegate){
31890            this.loaded = false;
31891            this.refreshDelegate();
31892         }
31893     },
31894     
31895     /**
31896      * Destroys this panel
31897      */
31898     destroy : function(){
31899         this.el.removeAllListeners();
31900         var tempEl = document.createElement("span");
31901         tempEl.appendChild(this.el.dom);
31902         tempEl.innerHTML = "";
31903         this.el.remove();
31904         this.el = null;
31905     },
31906     
31907     /**
31908      * form - if the content panel contains a form - this is a reference to it.
31909      * @type {Roo.form.Form}
31910      */
31911     form : false,
31912     /**
31913      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31914      *    This contains a reference to it.
31915      * @type {Roo.View}
31916      */
31917     view : false,
31918     
31919       /**
31920      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31921      * <pre><code>
31922
31923 layout.addxtype({
31924        xtype : 'Form',
31925        items: [ .... ]
31926    }
31927 );
31928
31929 </code></pre>
31930      * @param {Object} cfg Xtype definition of item to add.
31931      */
31932     
31933     addxtype : function(cfg) {
31934         // add form..
31935         if (cfg.xtype.match(/^Form$/)) {
31936             
31937             var el;
31938             //if (this.footer) {
31939             //    el = this.footer.container.insertSibling(false, 'before');
31940             //} else {
31941                 el = this.el.createChild();
31942             //}
31943
31944             this.form = new  Roo.form.Form(cfg);
31945             
31946             
31947             if ( this.form.allItems.length) {
31948                 this.form.render(el.dom);
31949             }
31950             return this.form;
31951         }
31952         // should only have one of theses..
31953         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31954             // views.. should not be just added - used named prop 'view''
31955             
31956             cfg.el = this.el.appendChild(document.createElement("div"));
31957             // factory?
31958             
31959             var ret = new Roo.factory(cfg);
31960              
31961              ret.render && ret.render(false, ''); // render blank..
31962             this.view = ret;
31963             return ret;
31964         }
31965         return false;
31966     }
31967 });
31968
31969 /**
31970  * @class Roo.GridPanel
31971  * @extends Roo.ContentPanel
31972  * @constructor
31973  * Create a new GridPanel.
31974  * @param {Roo.grid.Grid} grid The grid for this panel
31975  * @param {String/Object} config A string to set only the panel's title, or a config object
31976  */
31977 Roo.GridPanel = function(grid, config){
31978     
31979   
31980     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31981         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31982         
31983     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31984     
31985     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31986     
31987     if(this.toolbar){
31988         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31989     }
31990     // xtype created footer. - not sure if will work as we normally have to render first..
31991     if (this.footer && !this.footer.el && this.footer.xtype) {
31992         
31993         this.footer.container = this.grid.getView().getFooterPanel(true);
31994         this.footer.dataSource = this.grid.dataSource;
31995         this.footer = Roo.factory(this.footer, Roo);
31996         
31997     }
31998     
31999     grid.monitorWindowResize = false; // turn off autosizing
32000     grid.autoHeight = false;
32001     grid.autoWidth = false;
32002     this.grid = grid;
32003     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32004 };
32005
32006 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32007     getId : function(){
32008         return this.grid.id;
32009     },
32010     
32011     /**
32012      * Returns the grid for this panel
32013      * @return {Roo.grid.Grid} 
32014      */
32015     getGrid : function(){
32016         return this.grid;    
32017     },
32018     
32019     setSize : function(width, height){
32020         if(!this.ignoreResize(width, height)){
32021             var grid = this.grid;
32022             var size = this.adjustForComponents(width, height);
32023             grid.getGridEl().setSize(size.width, size.height);
32024             grid.autoSize();
32025         }
32026     },
32027     
32028     beforeSlide : function(){
32029         this.grid.getView().scroller.clip();
32030     },
32031     
32032     afterSlide : function(){
32033         this.grid.getView().scroller.unclip();
32034     },
32035     
32036     destroy : function(){
32037         this.grid.destroy();
32038         delete this.grid;
32039         Roo.GridPanel.superclass.destroy.call(this); 
32040     }
32041 });
32042
32043
32044 /**
32045  * @class Roo.NestedLayoutPanel
32046  * @extends Roo.ContentPanel
32047  * @constructor
32048  * Create a new NestedLayoutPanel.
32049  * 
32050  * 
32051  * @param {Roo.BorderLayout} layout The layout for this panel
32052  * @param {String/Object} config A string to set only the title or a config object
32053  */
32054 Roo.NestedLayoutPanel = function(layout, config)
32055 {
32056     // construct with only one argument..
32057     /* FIXME - implement nicer consturctors
32058     if (layout.layout) {
32059         config = layout;
32060         layout = config.layout;
32061         delete config.layout;
32062     }
32063     if (layout.xtype && !layout.getEl) {
32064         // then layout needs constructing..
32065         layout = Roo.factory(layout, Roo);
32066     }
32067     */
32068     
32069     
32070     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32071     
32072     layout.monitorWindowResize = false; // turn off autosizing
32073     this.layout = layout;
32074     this.layout.getEl().addClass("x-layout-nested-layout");
32075     
32076     
32077     
32078     
32079 };
32080
32081 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32082
32083     setSize : function(width, height){
32084         if(!this.ignoreResize(width, height)){
32085             var size = this.adjustForComponents(width, height);
32086             var el = this.layout.getEl();
32087             el.setSize(size.width, size.height);
32088             var touch = el.dom.offsetWidth;
32089             this.layout.layout();
32090             // ie requires a double layout on the first pass
32091             if(Roo.isIE && !this.initialized){
32092                 this.initialized = true;
32093                 this.layout.layout();
32094             }
32095         }
32096     },
32097     
32098     // activate all subpanels if not currently active..
32099     
32100     setActiveState : function(active){
32101         this.active = active;
32102         if(!active){
32103             this.fireEvent("deactivate", this);
32104             return;
32105         }
32106         
32107         this.fireEvent("activate", this);
32108         // not sure if this should happen before or after..
32109         if (!this.layout) {
32110             return; // should not happen..
32111         }
32112         var reg = false;
32113         for (var r in this.layout.regions) {
32114             reg = this.layout.getRegion(r);
32115             if (reg.getActivePanel()) {
32116                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32117                 reg.setActivePanel(reg.getActivePanel());
32118                 continue;
32119             }
32120             if (!reg.panels.length) {
32121                 continue;
32122             }
32123             reg.showPanel(reg.getPanel(0));
32124         }
32125         
32126         
32127         
32128         
32129     },
32130     
32131     /**
32132      * Returns the nested BorderLayout for this panel
32133      * @return {Roo.BorderLayout} 
32134      */
32135     getLayout : function(){
32136         return this.layout;
32137     },
32138     
32139      /**
32140      * Adds a xtype elements to the layout of the nested panel
32141      * <pre><code>
32142
32143 panel.addxtype({
32144        xtype : 'ContentPanel',
32145        region: 'west',
32146        items: [ .... ]
32147    }
32148 );
32149
32150 panel.addxtype({
32151         xtype : 'NestedLayoutPanel',
32152         region: 'west',
32153         layout: {
32154            center: { },
32155            west: { }   
32156         },
32157         items : [ ... list of content panels or nested layout panels.. ]
32158    }
32159 );
32160 </code></pre>
32161      * @param {Object} cfg Xtype definition of item to add.
32162      */
32163     addxtype : function(cfg) {
32164         return this.layout.addxtype(cfg);
32165     
32166     }
32167 });
32168
32169 Roo.ScrollPanel = function(el, config, content){
32170     config = config || {};
32171     config.fitToFrame = true;
32172     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32173     
32174     this.el.dom.style.overflow = "hidden";
32175     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32176     this.el.removeClass("x-layout-inactive-content");
32177     this.el.on("mousewheel", this.onWheel, this);
32178
32179     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32180     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32181     up.unselectable(); down.unselectable();
32182     up.on("click", this.scrollUp, this);
32183     down.on("click", this.scrollDown, this);
32184     up.addClassOnOver("x-scroller-btn-over");
32185     down.addClassOnOver("x-scroller-btn-over");
32186     up.addClassOnClick("x-scroller-btn-click");
32187     down.addClassOnClick("x-scroller-btn-click");
32188     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32189
32190     this.resizeEl = this.el;
32191     this.el = wrap; this.up = up; this.down = down;
32192 };
32193
32194 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32195     increment : 100,
32196     wheelIncrement : 5,
32197     scrollUp : function(){
32198         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32199     },
32200
32201     scrollDown : function(){
32202         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32203     },
32204
32205     afterScroll : function(){
32206         var el = this.resizeEl;
32207         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32208         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32209         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32210     },
32211
32212     setSize : function(){
32213         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32214         this.afterScroll();
32215     },
32216
32217     onWheel : function(e){
32218         var d = e.getWheelDelta();
32219         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32220         this.afterScroll();
32221         e.stopEvent();
32222     },
32223
32224     setContent : function(content, loadScripts){
32225         this.resizeEl.update(content, loadScripts);
32226     }
32227
32228 });
32229
32230
32231
32232
32233
32234
32235
32236
32237
32238 /**
32239  * @class Roo.TreePanel
32240  * @extends Roo.ContentPanel
32241  * @constructor
32242  * Create a new TreePanel. - defaults to fit/scoll contents.
32243  * @param {String/Object} config A string to set only the panel's title, or a config object
32244  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32245  */
32246 Roo.TreePanel = function(config){
32247     var el = config.el;
32248     var tree = config.tree;
32249     delete config.tree; 
32250     delete config.el; // hopefull!
32251     
32252     // wrapper for IE7 strict & safari scroll issue
32253     
32254     var treeEl = el.createChild();
32255     config.resizeEl = treeEl;
32256     
32257     
32258     
32259     Roo.TreePanel.superclass.constructor.call(this, el, config);
32260  
32261  
32262     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32263     //console.log(tree);
32264     this.on('activate', function()
32265     {
32266         if (this.tree.rendered) {
32267             return;
32268         }
32269         //console.log('render tree');
32270         this.tree.render();
32271     });
32272     // this should not be needed.. - it's actually the 'el' that resizes?
32273     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32274     
32275     //this.on('resize',  function (cp, w, h) {
32276     //        this.tree.innerCt.setWidth(w);
32277     //        this.tree.innerCt.setHeight(h);
32278     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32279     //});
32280
32281         
32282     
32283 };
32284
32285 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32286     fitToFrame : true,
32287     autoScroll : true
32288 });
32289
32290
32291
32292
32293
32294
32295
32296
32297
32298
32299
32300 /*
32301  * Based on:
32302  * Ext JS Library 1.1.1
32303  * Copyright(c) 2006-2007, Ext JS, LLC.
32304  *
32305  * Originally Released Under LGPL - original licence link has changed is not relivant.
32306  *
32307  * Fork - LGPL
32308  * <script type="text/javascript">
32309  */
32310  
32311
32312 /**
32313  * @class Roo.ReaderLayout
32314  * @extends Roo.BorderLayout
32315  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32316  * center region containing two nested regions (a top one for a list view and one for item preview below),
32317  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32318  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32319  * expedites the setup of the overall layout and regions for this common application style.
32320  * Example:
32321  <pre><code>
32322 var reader = new Roo.ReaderLayout();
32323 var CP = Roo.ContentPanel;  // shortcut for adding
32324
32325 reader.beginUpdate();
32326 reader.add("north", new CP("north", "North"));
32327 reader.add("west", new CP("west", {title: "West"}));
32328 reader.add("east", new CP("east", {title: "East"}));
32329
32330 reader.regions.listView.add(new CP("listView", "List"));
32331 reader.regions.preview.add(new CP("preview", "Preview"));
32332 reader.endUpdate();
32333 </code></pre>
32334 * @constructor
32335 * Create a new ReaderLayout
32336 * @param {Object} config Configuration options
32337 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32338 * document.body if omitted)
32339 */
32340 Roo.ReaderLayout = function(config, renderTo){
32341     var c = config || {size:{}};
32342     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32343         north: c.north !== false ? Roo.apply({
32344             split:false,
32345             initialSize: 32,
32346             titlebar: false
32347         }, c.north) : false,
32348         west: c.west !== false ? Roo.apply({
32349             split:true,
32350             initialSize: 200,
32351             minSize: 175,
32352             maxSize: 400,
32353             titlebar: true,
32354             collapsible: true,
32355             animate: true,
32356             margins:{left:5,right:0,bottom:5,top:5},
32357             cmargins:{left:5,right:5,bottom:5,top:5}
32358         }, c.west) : false,
32359         east: c.east !== false ? Roo.apply({
32360             split:true,
32361             initialSize: 200,
32362             minSize: 175,
32363             maxSize: 400,
32364             titlebar: true,
32365             collapsible: true,
32366             animate: true,
32367             margins:{left:0,right:5,bottom:5,top:5},
32368             cmargins:{left:5,right:5,bottom:5,top:5}
32369         }, c.east) : false,
32370         center: Roo.apply({
32371             tabPosition: 'top',
32372             autoScroll:false,
32373             closeOnTab: true,
32374             titlebar:false,
32375             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32376         }, c.center)
32377     });
32378
32379     this.el.addClass('x-reader');
32380
32381     this.beginUpdate();
32382
32383     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32384         south: c.preview !== false ? Roo.apply({
32385             split:true,
32386             initialSize: 200,
32387             minSize: 100,
32388             autoScroll:true,
32389             collapsible:true,
32390             titlebar: true,
32391             cmargins:{top:5,left:0, right:0, bottom:0}
32392         }, c.preview) : false,
32393         center: Roo.apply({
32394             autoScroll:false,
32395             titlebar:false,
32396             minHeight:200
32397         }, c.listView)
32398     });
32399     this.add('center', new Roo.NestedLayoutPanel(inner,
32400             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32401
32402     this.endUpdate();
32403
32404     this.regions.preview = inner.getRegion('south');
32405     this.regions.listView = inner.getRegion('center');
32406 };
32407
32408 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32409  * Based on:
32410  * Ext JS Library 1.1.1
32411  * Copyright(c) 2006-2007, Ext JS, LLC.
32412  *
32413  * Originally Released Under LGPL - original licence link has changed is not relivant.
32414  *
32415  * Fork - LGPL
32416  * <script type="text/javascript">
32417  */
32418  
32419 /**
32420  * @class Roo.grid.Grid
32421  * @extends Roo.util.Observable
32422  * This class represents the primary interface of a component based grid control.
32423  * <br><br>Usage:<pre><code>
32424  var grid = new Roo.grid.Grid("my-container-id", {
32425      ds: myDataStore,
32426      cm: myColModel,
32427      selModel: mySelectionModel,
32428      autoSizeColumns: true,
32429      monitorWindowResize: false,
32430      trackMouseOver: true
32431  });
32432  // set any options
32433  grid.render();
32434  * </code></pre>
32435  * <b>Common Problems:</b><br/>
32436  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32437  * element will correct this<br/>
32438  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32439  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32440  * are unpredictable.<br/>
32441  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32442  * grid to calculate dimensions/offsets.<br/>
32443   * @constructor
32444  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32445  * The container MUST have some type of size defined for the grid to fill. The container will be
32446  * automatically set to position relative if it isn't already.
32447  * @param {Object} config A config object that sets properties on this grid.
32448  */
32449 Roo.grid.Grid = function(container, config){
32450         // initialize the container
32451         this.container = Roo.get(container);
32452         this.container.update("");
32453         this.container.setStyle("overflow", "hidden");
32454     this.container.addClass('x-grid-container');
32455
32456     this.id = this.container.id;
32457
32458     Roo.apply(this, config);
32459     // check and correct shorthanded configs
32460     if(this.ds){
32461         this.dataSource = this.ds;
32462         delete this.ds;
32463     }
32464     if(this.cm){
32465         this.colModel = this.cm;
32466         delete this.cm;
32467     }
32468     if(this.sm){
32469         this.selModel = this.sm;
32470         delete this.sm;
32471     }
32472
32473     if (this.selModel) {
32474         this.selModel = Roo.factory(this.selModel, Roo.grid);
32475         this.sm = this.selModel;
32476         this.sm.xmodule = this.xmodule || false;
32477     }
32478     if (typeof(this.colModel.config) == 'undefined') {
32479         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32480         this.cm = this.colModel;
32481         this.cm.xmodule = this.xmodule || false;
32482     }
32483     if (this.dataSource) {
32484         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32485         this.ds = this.dataSource;
32486         this.ds.xmodule = this.xmodule || false;
32487          
32488     }
32489     
32490     
32491     
32492     if(this.width){
32493         this.container.setWidth(this.width);
32494     }
32495
32496     if(this.height){
32497         this.container.setHeight(this.height);
32498     }
32499     /** @private */
32500         this.addEvents({
32501         // raw events
32502         /**
32503          * @event click
32504          * The raw click event for the entire grid.
32505          * @param {Roo.EventObject} e
32506          */
32507         "click" : true,
32508         /**
32509          * @event dblclick
32510          * The raw dblclick event for the entire grid.
32511          * @param {Roo.EventObject} e
32512          */
32513         "dblclick" : true,
32514         /**
32515          * @event contextmenu
32516          * The raw contextmenu event for the entire grid.
32517          * @param {Roo.EventObject} e
32518          */
32519         "contextmenu" : true,
32520         /**
32521          * @event mousedown
32522          * The raw mousedown event for the entire grid.
32523          * @param {Roo.EventObject} e
32524          */
32525         "mousedown" : true,
32526         /**
32527          * @event mouseup
32528          * The raw mouseup event for the entire grid.
32529          * @param {Roo.EventObject} e
32530          */
32531         "mouseup" : true,
32532         /**
32533          * @event mouseover
32534          * The raw mouseover event for the entire grid.
32535          * @param {Roo.EventObject} e
32536          */
32537         "mouseover" : true,
32538         /**
32539          * @event mouseout
32540          * The raw mouseout event for the entire grid.
32541          * @param {Roo.EventObject} e
32542          */
32543         "mouseout" : true,
32544         /**
32545          * @event keypress
32546          * The raw keypress event for the entire grid.
32547          * @param {Roo.EventObject} e
32548          */
32549         "keypress" : true,
32550         /**
32551          * @event keydown
32552          * The raw keydown event for the entire grid.
32553          * @param {Roo.EventObject} e
32554          */
32555         "keydown" : true,
32556
32557         // custom events
32558
32559         /**
32560          * @event cellclick
32561          * Fires when a cell is clicked
32562          * @param {Grid} this
32563          * @param {Number} rowIndex
32564          * @param {Number} columnIndex
32565          * @param {Roo.EventObject} e
32566          */
32567         "cellclick" : true,
32568         /**
32569          * @event celldblclick
32570          * Fires when a cell is double clicked
32571          * @param {Grid} this
32572          * @param {Number} rowIndex
32573          * @param {Number} columnIndex
32574          * @param {Roo.EventObject} e
32575          */
32576         "celldblclick" : true,
32577         /**
32578          * @event rowclick
32579          * Fires when a row is clicked
32580          * @param {Grid} this
32581          * @param {Number} rowIndex
32582          * @param {Roo.EventObject} e
32583          */
32584         "rowclick" : true,
32585         /**
32586          * @event rowdblclick
32587          * Fires when a row is double clicked
32588          * @param {Grid} this
32589          * @param {Number} rowIndex
32590          * @param {Roo.EventObject} e
32591          */
32592         "rowdblclick" : true,
32593         /**
32594          * @event headerclick
32595          * Fires when a header is clicked
32596          * @param {Grid} this
32597          * @param {Number} columnIndex
32598          * @param {Roo.EventObject} e
32599          */
32600         "headerclick" : true,
32601         /**
32602          * @event headerdblclick
32603          * Fires when a header cell is double clicked
32604          * @param {Grid} this
32605          * @param {Number} columnIndex
32606          * @param {Roo.EventObject} e
32607          */
32608         "headerdblclick" : true,
32609         /**
32610          * @event rowcontextmenu
32611          * Fires when a row is right clicked
32612          * @param {Grid} this
32613          * @param {Number} rowIndex
32614          * @param {Roo.EventObject} e
32615          */
32616         "rowcontextmenu" : true,
32617         /**
32618          * @event cellcontextmenu
32619          * Fires when a cell is right clicked
32620          * @param {Grid} this
32621          * @param {Number} rowIndex
32622          * @param {Number} cellIndex
32623          * @param {Roo.EventObject} e
32624          */
32625          "cellcontextmenu" : true,
32626         /**
32627          * @event headercontextmenu
32628          * Fires when a header is right clicked
32629          * @param {Grid} this
32630          * @param {Number} columnIndex
32631          * @param {Roo.EventObject} e
32632          */
32633         "headercontextmenu" : true,
32634         /**
32635          * @event bodyscroll
32636          * Fires when the body element is scrolled
32637          * @param {Number} scrollLeft
32638          * @param {Number} scrollTop
32639          */
32640         "bodyscroll" : true,
32641         /**
32642          * @event columnresize
32643          * Fires when the user resizes a column
32644          * @param {Number} columnIndex
32645          * @param {Number} newSize
32646          */
32647         "columnresize" : true,
32648         /**
32649          * @event columnmove
32650          * Fires when the user moves a column
32651          * @param {Number} oldIndex
32652          * @param {Number} newIndex
32653          */
32654         "columnmove" : true,
32655         /**
32656          * @event startdrag
32657          * Fires when row(s) start being dragged
32658          * @param {Grid} this
32659          * @param {Roo.GridDD} dd The drag drop object
32660          * @param {event} e The raw browser event
32661          */
32662         "startdrag" : true,
32663         /**
32664          * @event enddrag
32665          * Fires when a drag operation is complete
32666          * @param {Grid} this
32667          * @param {Roo.GridDD} dd The drag drop object
32668          * @param {event} e The raw browser event
32669          */
32670         "enddrag" : true,
32671         /**
32672          * @event dragdrop
32673          * Fires when dragged row(s) are dropped on a valid DD target
32674          * @param {Grid} this
32675          * @param {Roo.GridDD} dd The drag drop object
32676          * @param {String} targetId The target drag drop object
32677          * @param {event} e The raw browser event
32678          */
32679         "dragdrop" : true,
32680         /**
32681          * @event dragover
32682          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32683          * @param {Grid} this
32684          * @param {Roo.GridDD} dd The drag drop object
32685          * @param {String} targetId The target drag drop object
32686          * @param {event} e The raw browser event
32687          */
32688         "dragover" : true,
32689         /**
32690          * @event dragenter
32691          *  Fires when the dragged row(s) first cross another DD target while being dragged
32692          * @param {Grid} this
32693          * @param {Roo.GridDD} dd The drag drop object
32694          * @param {String} targetId The target drag drop object
32695          * @param {event} e The raw browser event
32696          */
32697         "dragenter" : true,
32698         /**
32699          * @event dragout
32700          * Fires when the dragged row(s) leave another DD target while being dragged
32701          * @param {Grid} this
32702          * @param {Roo.GridDD} dd The drag drop object
32703          * @param {String} targetId The target drag drop object
32704          * @param {event} e The raw browser event
32705          */
32706         "dragout" : true,
32707         /**
32708          * @event rowclass
32709          * Fires when a row is rendered, so you can change add a style to it.
32710          * @param {GridView} gridview   The grid view
32711          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32712          */
32713         'rowclass' : true,
32714
32715         /**
32716          * @event render
32717          * Fires when the grid is rendered
32718          * @param {Grid} grid
32719          */
32720         'render' : true
32721     });
32722
32723     Roo.grid.Grid.superclass.constructor.call(this);
32724 };
32725 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32726     
32727     /**
32728      * @cfg {String} ddGroup - drag drop group.
32729      */
32730
32731     /**
32732      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32733      */
32734     minColumnWidth : 25,
32735
32736     /**
32737      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32738      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32739      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32740      */
32741     autoSizeColumns : false,
32742
32743     /**
32744      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32745      */
32746     autoSizeHeaders : true,
32747
32748     /**
32749      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32750      */
32751     monitorWindowResize : true,
32752
32753     /**
32754      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32755      * rows measured to get a columns size. Default is 0 (all rows).
32756      */
32757     maxRowsToMeasure : 0,
32758
32759     /**
32760      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32761      */
32762     trackMouseOver : true,
32763
32764     /**
32765     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32766     */
32767     
32768     /**
32769     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32770     */
32771     enableDragDrop : false,
32772     
32773     /**
32774     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32775     */
32776     enableColumnMove : true,
32777     
32778     /**
32779     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32780     */
32781     enableColumnHide : true,
32782     
32783     /**
32784     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32785     */
32786     enableRowHeightSync : false,
32787     
32788     /**
32789     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32790     */
32791     stripeRows : true,
32792     
32793     /**
32794     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32795     */
32796     autoHeight : false,
32797
32798     /**
32799      * @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.
32800      */
32801     autoExpandColumn : false,
32802
32803     /**
32804     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32805     * Default is 50.
32806     */
32807     autoExpandMin : 50,
32808
32809     /**
32810     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32811     */
32812     autoExpandMax : 1000,
32813
32814     /**
32815     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32816     */
32817     view : null,
32818
32819     /**
32820     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32821     */
32822     loadMask : false,
32823     /**
32824     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32825     */
32826     dropTarget: false,
32827     
32828    
32829     
32830     // private
32831     rendered : false,
32832
32833     /**
32834     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32835     * of a fixed width. Default is false.
32836     */
32837     /**
32838     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32839     */
32840     /**
32841      * Called once after all setup has been completed and the grid is ready to be rendered.
32842      * @return {Roo.grid.Grid} this
32843      */
32844     render : function()
32845     {
32846         var c = this.container;
32847         // try to detect autoHeight/width mode
32848         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32849             this.autoHeight = true;
32850         }
32851         var view = this.getView();
32852         view.init(this);
32853
32854         c.on("click", this.onClick, this);
32855         c.on("dblclick", this.onDblClick, this);
32856         c.on("contextmenu", this.onContextMenu, this);
32857         c.on("keydown", this.onKeyDown, this);
32858         if (Roo.isTouch) {
32859             c.on("touchstart", this.onTouchStart, this);
32860         }
32861
32862         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32863
32864         this.getSelectionModel().init(this);
32865
32866         view.render();
32867
32868         if(this.loadMask){
32869             this.loadMask = new Roo.LoadMask(this.container,
32870                     Roo.apply({store:this.dataSource}, this.loadMask));
32871         }
32872         
32873         
32874         if (this.toolbar && this.toolbar.xtype) {
32875             this.toolbar.container = this.getView().getHeaderPanel(true);
32876             this.toolbar = new Roo.Toolbar(this.toolbar);
32877         }
32878         if (this.footer && this.footer.xtype) {
32879             this.footer.dataSource = this.getDataSource();
32880             this.footer.container = this.getView().getFooterPanel(true);
32881             this.footer = Roo.factory(this.footer, Roo);
32882         }
32883         if (this.dropTarget && this.dropTarget.xtype) {
32884             delete this.dropTarget.xtype;
32885             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32886         }
32887         
32888         
32889         this.rendered = true;
32890         this.fireEvent('render', this);
32891         return this;
32892     },
32893
32894         /**
32895          * Reconfigures the grid to use a different Store and Column Model.
32896          * The View will be bound to the new objects and refreshed.
32897          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32898          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32899          */
32900     reconfigure : function(dataSource, colModel){
32901         if(this.loadMask){
32902             this.loadMask.destroy();
32903             this.loadMask = new Roo.LoadMask(this.container,
32904                     Roo.apply({store:dataSource}, this.loadMask));
32905         }
32906         this.view.bind(dataSource, colModel);
32907         this.dataSource = dataSource;
32908         this.colModel = colModel;
32909         this.view.refresh(true);
32910     },
32911
32912     // private
32913     onKeyDown : function(e){
32914         this.fireEvent("keydown", e);
32915     },
32916
32917     /**
32918      * Destroy this grid.
32919      * @param {Boolean} removeEl True to remove the element
32920      */
32921     destroy : function(removeEl, keepListeners){
32922         if(this.loadMask){
32923             this.loadMask.destroy();
32924         }
32925         var c = this.container;
32926         c.removeAllListeners();
32927         this.view.destroy();
32928         this.colModel.purgeListeners();
32929         if(!keepListeners){
32930             this.purgeListeners();
32931         }
32932         c.update("");
32933         if(removeEl === true){
32934             c.remove();
32935         }
32936     },
32937
32938     // private
32939     processEvent : function(name, e){
32940         // does this fire select???
32941         //Roo.log('grid:processEvent '  + name);
32942         
32943         if (name != 'touchstart' ) {
32944             this.fireEvent(name, e);    
32945         }
32946         
32947         var t = e.getTarget();
32948         var v = this.view;
32949         var header = v.findHeaderIndex(t);
32950         if(header !== false){
32951             var ename = name == 'touchstart' ? 'click' : name;
32952              
32953             this.fireEvent("header" + ename, this, header, e);
32954         }else{
32955             var row = v.findRowIndex(t);
32956             var cell = v.findCellIndex(t);
32957             if (name == 'touchstart') {
32958                 // first touch is always a click.
32959                 // hopefull this happens after selection is updated.?
32960                 name = false;
32961                 
32962                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32963                     var cs = this.selModel.getSelectedCell();
32964                     if (row == cs[0] && cell == cs[1]){
32965                         name = 'dblclick';
32966                     }
32967                 }
32968                 if (typeof(this.selModel.getSelections) != 'undefined') {
32969                     var cs = this.selModel.getSelections();
32970                     var ds = this.dataSource;
32971                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32972                         name = 'dblclick';
32973                     }
32974                 }
32975                 if (!name) {
32976                     return;
32977                 }
32978             }
32979             
32980             
32981             if(row !== false){
32982                 this.fireEvent("row" + name, this, row, e);
32983                 if(cell !== false){
32984                     this.fireEvent("cell" + name, this, row, cell, e);
32985                 }
32986             }
32987         }
32988     },
32989
32990     // private
32991     onClick : function(e){
32992         this.processEvent("click", e);
32993     },
32994    // private
32995     onTouchStart : function(e){
32996         this.processEvent("touchstart", e);
32997     },
32998
32999     // private
33000     onContextMenu : function(e, t){
33001         this.processEvent("contextmenu", e);
33002     },
33003
33004     // private
33005     onDblClick : function(e){
33006         this.processEvent("dblclick", e);
33007     },
33008
33009     // private
33010     walkCells : function(row, col, step, fn, scope){
33011         var cm = this.colModel, clen = cm.getColumnCount();
33012         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33013         if(step < 0){
33014             if(col < 0){
33015                 row--;
33016                 first = false;
33017             }
33018             while(row >= 0){
33019                 if(!first){
33020                     col = clen-1;
33021                 }
33022                 first = false;
33023                 while(col >= 0){
33024                     if(fn.call(scope || this, row, col, cm) === true){
33025                         return [row, col];
33026                     }
33027                     col--;
33028                 }
33029                 row--;
33030             }
33031         } else {
33032             if(col >= clen){
33033                 row++;
33034                 first = false;
33035             }
33036             while(row < rlen){
33037                 if(!first){
33038                     col = 0;
33039                 }
33040                 first = false;
33041                 while(col < clen){
33042                     if(fn.call(scope || this, row, col, cm) === true){
33043                         return [row, col];
33044                     }
33045                     col++;
33046                 }
33047                 row++;
33048             }
33049         }
33050         return null;
33051     },
33052
33053     // private
33054     getSelections : function(){
33055         return this.selModel.getSelections();
33056     },
33057
33058     /**
33059      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33060      * but if manual update is required this method will initiate it.
33061      */
33062     autoSize : function(){
33063         if(this.rendered){
33064             this.view.layout();
33065             if(this.view.adjustForScroll){
33066                 this.view.adjustForScroll();
33067             }
33068         }
33069     },
33070
33071     /**
33072      * Returns the grid's underlying element.
33073      * @return {Element} The element
33074      */
33075     getGridEl : function(){
33076         return this.container;
33077     },
33078
33079     // private for compatibility, overridden by editor grid
33080     stopEditing : function(){},
33081
33082     /**
33083      * Returns the grid's SelectionModel.
33084      * @return {SelectionModel}
33085      */
33086     getSelectionModel : function(){
33087         if(!this.selModel){
33088             this.selModel = new Roo.grid.RowSelectionModel();
33089         }
33090         return this.selModel;
33091     },
33092
33093     /**
33094      * Returns the grid's DataSource.
33095      * @return {DataSource}
33096      */
33097     getDataSource : function(){
33098         return this.dataSource;
33099     },
33100
33101     /**
33102      * Returns the grid's ColumnModel.
33103      * @return {ColumnModel}
33104      */
33105     getColumnModel : function(){
33106         return this.colModel;
33107     },
33108
33109     /**
33110      * Returns the grid's GridView object.
33111      * @return {GridView}
33112      */
33113     getView : function(){
33114         if(!this.view){
33115             this.view = new Roo.grid.GridView(this.viewConfig);
33116         }
33117         return this.view;
33118     },
33119     /**
33120      * Called to get grid's drag proxy text, by default returns this.ddText.
33121      * @return {String}
33122      */
33123     getDragDropText : function(){
33124         var count = this.selModel.getCount();
33125         return String.format(this.ddText, count, count == 1 ? '' : 's');
33126     }
33127 });
33128 /**
33129  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33130  * %0 is replaced with the number of selected rows.
33131  * @type String
33132  */
33133 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33134  * Based on:
33135  * Ext JS Library 1.1.1
33136  * Copyright(c) 2006-2007, Ext JS, LLC.
33137  *
33138  * Originally Released Under LGPL - original licence link has changed is not relivant.
33139  *
33140  * Fork - LGPL
33141  * <script type="text/javascript">
33142  */
33143  
33144 Roo.grid.AbstractGridView = function(){
33145         this.grid = null;
33146         
33147         this.events = {
33148             "beforerowremoved" : true,
33149             "beforerowsinserted" : true,
33150             "beforerefresh" : true,
33151             "rowremoved" : true,
33152             "rowsinserted" : true,
33153             "rowupdated" : true,
33154             "refresh" : true
33155         };
33156     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33157 };
33158
33159 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33160     rowClass : "x-grid-row",
33161     cellClass : "x-grid-cell",
33162     tdClass : "x-grid-td",
33163     hdClass : "x-grid-hd",
33164     splitClass : "x-grid-hd-split",
33165     
33166     init: function(grid){
33167         this.grid = grid;
33168                 var cid = this.grid.getGridEl().id;
33169         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33170         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33171         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33172         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33173         },
33174         
33175     getColumnRenderers : function(){
33176         var renderers = [];
33177         var cm = this.grid.colModel;
33178         var colCount = cm.getColumnCount();
33179         for(var i = 0; i < colCount; i++){
33180             renderers[i] = cm.getRenderer(i);
33181         }
33182         return renderers;
33183     },
33184     
33185     getColumnIds : function(){
33186         var ids = [];
33187         var cm = this.grid.colModel;
33188         var colCount = cm.getColumnCount();
33189         for(var i = 0; i < colCount; i++){
33190             ids[i] = cm.getColumnId(i);
33191         }
33192         return ids;
33193     },
33194     
33195     getDataIndexes : function(){
33196         if(!this.indexMap){
33197             this.indexMap = this.buildIndexMap();
33198         }
33199         return this.indexMap.colToData;
33200     },
33201     
33202     getColumnIndexByDataIndex : function(dataIndex){
33203         if(!this.indexMap){
33204             this.indexMap = this.buildIndexMap();
33205         }
33206         return this.indexMap.dataToCol[dataIndex];
33207     },
33208     
33209     /**
33210      * Set a css style for a column dynamically. 
33211      * @param {Number} colIndex The index of the column
33212      * @param {String} name The css property name
33213      * @param {String} value The css value
33214      */
33215     setCSSStyle : function(colIndex, name, value){
33216         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33217         Roo.util.CSS.updateRule(selector, name, value);
33218     },
33219     
33220     generateRules : function(cm){
33221         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33222         Roo.util.CSS.removeStyleSheet(rulesId);
33223         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33224             var cid = cm.getColumnId(i);
33225             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33226                          this.tdSelector, cid, " {\n}\n",
33227                          this.hdSelector, cid, " {\n}\n",
33228                          this.splitSelector, cid, " {\n}\n");
33229         }
33230         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33231     }
33232 });/*
33233  * Based on:
33234  * Ext JS Library 1.1.1
33235  * Copyright(c) 2006-2007, Ext JS, LLC.
33236  *
33237  * Originally Released Under LGPL - original licence link has changed is not relivant.
33238  *
33239  * Fork - LGPL
33240  * <script type="text/javascript">
33241  */
33242
33243 // private
33244 // This is a support class used internally by the Grid components
33245 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33246     this.grid = grid;
33247     this.view = grid.getView();
33248     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33249     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33250     if(hd2){
33251         this.setHandleElId(Roo.id(hd));
33252         this.setOuterHandleElId(Roo.id(hd2));
33253     }
33254     this.scroll = false;
33255 };
33256 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33257     maxDragWidth: 120,
33258     getDragData : function(e){
33259         var t = Roo.lib.Event.getTarget(e);
33260         var h = this.view.findHeaderCell(t);
33261         if(h){
33262             return {ddel: h.firstChild, header:h};
33263         }
33264         return false;
33265     },
33266
33267     onInitDrag : function(e){
33268         this.view.headersDisabled = true;
33269         var clone = this.dragData.ddel.cloneNode(true);
33270         clone.id = Roo.id();
33271         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33272         this.proxy.update(clone);
33273         return true;
33274     },
33275
33276     afterValidDrop : function(){
33277         var v = this.view;
33278         setTimeout(function(){
33279             v.headersDisabled = false;
33280         }, 50);
33281     },
33282
33283     afterInvalidDrop : function(){
33284         var v = this.view;
33285         setTimeout(function(){
33286             v.headersDisabled = false;
33287         }, 50);
33288     }
33289 });
33290 /*
33291  * Based on:
33292  * Ext JS Library 1.1.1
33293  * Copyright(c) 2006-2007, Ext JS, LLC.
33294  *
33295  * Originally Released Under LGPL - original licence link has changed is not relivant.
33296  *
33297  * Fork - LGPL
33298  * <script type="text/javascript">
33299  */
33300 // private
33301 // This is a support class used internally by the Grid components
33302 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33303     this.grid = grid;
33304     this.view = grid.getView();
33305     // split the proxies so they don't interfere with mouse events
33306     this.proxyTop = Roo.DomHelper.append(document.body, {
33307         cls:"col-move-top", html:"&#160;"
33308     }, true);
33309     this.proxyBottom = Roo.DomHelper.append(document.body, {
33310         cls:"col-move-bottom", html:"&#160;"
33311     }, true);
33312     this.proxyTop.hide = this.proxyBottom.hide = function(){
33313         this.setLeftTop(-100,-100);
33314         this.setStyle("visibility", "hidden");
33315     };
33316     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33317     // temporarily disabled
33318     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33319     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33320 };
33321 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33322     proxyOffsets : [-4, -9],
33323     fly: Roo.Element.fly,
33324
33325     getTargetFromEvent : function(e){
33326         var t = Roo.lib.Event.getTarget(e);
33327         var cindex = this.view.findCellIndex(t);
33328         if(cindex !== false){
33329             return this.view.getHeaderCell(cindex);
33330         }
33331         return null;
33332     },
33333
33334     nextVisible : function(h){
33335         var v = this.view, cm = this.grid.colModel;
33336         h = h.nextSibling;
33337         while(h){
33338             if(!cm.isHidden(v.getCellIndex(h))){
33339                 return h;
33340             }
33341             h = h.nextSibling;
33342         }
33343         return null;
33344     },
33345
33346     prevVisible : function(h){
33347         var v = this.view, cm = this.grid.colModel;
33348         h = h.prevSibling;
33349         while(h){
33350             if(!cm.isHidden(v.getCellIndex(h))){
33351                 return h;
33352             }
33353             h = h.prevSibling;
33354         }
33355         return null;
33356     },
33357
33358     positionIndicator : function(h, n, e){
33359         var x = Roo.lib.Event.getPageX(e);
33360         var r = Roo.lib.Dom.getRegion(n.firstChild);
33361         var px, pt, py = r.top + this.proxyOffsets[1];
33362         if((r.right - x) <= (r.right-r.left)/2){
33363             px = r.right+this.view.borderWidth;
33364             pt = "after";
33365         }else{
33366             px = r.left;
33367             pt = "before";
33368         }
33369         var oldIndex = this.view.getCellIndex(h);
33370         var newIndex = this.view.getCellIndex(n);
33371
33372         if(this.grid.colModel.isFixed(newIndex)){
33373             return false;
33374         }
33375
33376         var locked = this.grid.colModel.isLocked(newIndex);
33377
33378         if(pt == "after"){
33379             newIndex++;
33380         }
33381         if(oldIndex < newIndex){
33382             newIndex--;
33383         }
33384         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33385             return false;
33386         }
33387         px +=  this.proxyOffsets[0];
33388         this.proxyTop.setLeftTop(px, py);
33389         this.proxyTop.show();
33390         if(!this.bottomOffset){
33391             this.bottomOffset = this.view.mainHd.getHeight();
33392         }
33393         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33394         this.proxyBottom.show();
33395         return pt;
33396     },
33397
33398     onNodeEnter : function(n, dd, e, data){
33399         if(data.header != n){
33400             this.positionIndicator(data.header, n, e);
33401         }
33402     },
33403
33404     onNodeOver : function(n, dd, e, data){
33405         var result = false;
33406         if(data.header != n){
33407             result = this.positionIndicator(data.header, n, e);
33408         }
33409         if(!result){
33410             this.proxyTop.hide();
33411             this.proxyBottom.hide();
33412         }
33413         return result ? this.dropAllowed : this.dropNotAllowed;
33414     },
33415
33416     onNodeOut : function(n, dd, e, data){
33417         this.proxyTop.hide();
33418         this.proxyBottom.hide();
33419     },
33420
33421     onNodeDrop : function(n, dd, e, data){
33422         var h = data.header;
33423         if(h != n){
33424             var cm = this.grid.colModel;
33425             var x = Roo.lib.Event.getPageX(e);
33426             var r = Roo.lib.Dom.getRegion(n.firstChild);
33427             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33428             var oldIndex = this.view.getCellIndex(h);
33429             var newIndex = this.view.getCellIndex(n);
33430             var locked = cm.isLocked(newIndex);
33431             if(pt == "after"){
33432                 newIndex++;
33433             }
33434             if(oldIndex < newIndex){
33435                 newIndex--;
33436             }
33437             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33438                 return false;
33439             }
33440             cm.setLocked(oldIndex, locked, true);
33441             cm.moveColumn(oldIndex, newIndex);
33442             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33443             return true;
33444         }
33445         return false;
33446     }
33447 });
33448 /*
33449  * Based on:
33450  * Ext JS Library 1.1.1
33451  * Copyright(c) 2006-2007, Ext JS, LLC.
33452  *
33453  * Originally Released Under LGPL - original licence link has changed is not relivant.
33454  *
33455  * Fork - LGPL
33456  * <script type="text/javascript">
33457  */
33458   
33459 /**
33460  * @class Roo.grid.GridView
33461  * @extends Roo.util.Observable
33462  *
33463  * @constructor
33464  * @param {Object} config
33465  */
33466 Roo.grid.GridView = function(config){
33467     Roo.grid.GridView.superclass.constructor.call(this);
33468     this.el = null;
33469
33470     Roo.apply(this, config);
33471 };
33472
33473 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33474
33475     unselectable :  'unselectable="on"',
33476     unselectableCls :  'x-unselectable',
33477     
33478     
33479     rowClass : "x-grid-row",
33480
33481     cellClass : "x-grid-col",
33482
33483     tdClass : "x-grid-td",
33484
33485     hdClass : "x-grid-hd",
33486
33487     splitClass : "x-grid-split",
33488
33489     sortClasses : ["sort-asc", "sort-desc"],
33490
33491     enableMoveAnim : false,
33492
33493     hlColor: "C3DAF9",
33494
33495     dh : Roo.DomHelper,
33496
33497     fly : Roo.Element.fly,
33498
33499     css : Roo.util.CSS,
33500
33501     borderWidth: 1,
33502
33503     splitOffset: 3,
33504
33505     scrollIncrement : 22,
33506
33507     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33508
33509     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33510
33511     bind : function(ds, cm){
33512         if(this.ds){
33513             this.ds.un("load", this.onLoad, this);
33514             this.ds.un("datachanged", this.onDataChange, this);
33515             this.ds.un("add", this.onAdd, this);
33516             this.ds.un("remove", this.onRemove, this);
33517             this.ds.un("update", this.onUpdate, this);
33518             this.ds.un("clear", this.onClear, this);
33519         }
33520         if(ds){
33521             ds.on("load", this.onLoad, this);
33522             ds.on("datachanged", this.onDataChange, this);
33523             ds.on("add", this.onAdd, this);
33524             ds.on("remove", this.onRemove, this);
33525             ds.on("update", this.onUpdate, this);
33526             ds.on("clear", this.onClear, this);
33527         }
33528         this.ds = ds;
33529
33530         if(this.cm){
33531             this.cm.un("widthchange", this.onColWidthChange, this);
33532             this.cm.un("headerchange", this.onHeaderChange, this);
33533             this.cm.un("hiddenchange", this.onHiddenChange, this);
33534             this.cm.un("columnmoved", this.onColumnMove, this);
33535             this.cm.un("columnlockchange", this.onColumnLock, this);
33536         }
33537         if(cm){
33538             this.generateRules(cm);
33539             cm.on("widthchange", this.onColWidthChange, this);
33540             cm.on("headerchange", this.onHeaderChange, this);
33541             cm.on("hiddenchange", this.onHiddenChange, this);
33542             cm.on("columnmoved", this.onColumnMove, this);
33543             cm.on("columnlockchange", this.onColumnLock, this);
33544         }
33545         this.cm = cm;
33546     },
33547
33548     init: function(grid){
33549         Roo.grid.GridView.superclass.init.call(this, grid);
33550
33551         this.bind(grid.dataSource, grid.colModel);
33552
33553         grid.on("headerclick", this.handleHeaderClick, this);
33554
33555         if(grid.trackMouseOver){
33556             grid.on("mouseover", this.onRowOver, this);
33557             grid.on("mouseout", this.onRowOut, this);
33558         }
33559         grid.cancelTextSelection = function(){};
33560         this.gridId = grid.id;
33561
33562         var tpls = this.templates || {};
33563
33564         if(!tpls.master){
33565             tpls.master = new Roo.Template(
33566                '<div class="x-grid" hidefocus="true">',
33567                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33568                   '<div class="x-grid-topbar"></div>',
33569                   '<div class="x-grid-scroller"><div></div></div>',
33570                   '<div class="x-grid-locked">',
33571                       '<div class="x-grid-header">{lockedHeader}</div>',
33572                       '<div class="x-grid-body">{lockedBody}</div>',
33573                   "</div>",
33574                   '<div class="x-grid-viewport">',
33575                       '<div class="x-grid-header">{header}</div>',
33576                       '<div class="x-grid-body">{body}</div>',
33577                   "</div>",
33578                   '<div class="x-grid-bottombar"></div>',
33579                  
33580                   '<div class="x-grid-resize-proxy">&#160;</div>',
33581                "</div>"
33582             );
33583             tpls.master.disableformats = true;
33584         }
33585
33586         if(!tpls.header){
33587             tpls.header = new Roo.Template(
33588                '<table border="0" cellspacing="0" cellpadding="0">',
33589                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33590                "</table>{splits}"
33591             );
33592             tpls.header.disableformats = true;
33593         }
33594         tpls.header.compile();
33595
33596         if(!tpls.hcell){
33597             tpls.hcell = new Roo.Template(
33598                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33599                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33600                 "</div></td>"
33601              );
33602              tpls.hcell.disableFormats = true;
33603         }
33604         tpls.hcell.compile();
33605
33606         if(!tpls.hsplit){
33607             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33608                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33609             tpls.hsplit.disableFormats = true;
33610         }
33611         tpls.hsplit.compile();
33612
33613         if(!tpls.body){
33614             tpls.body = new Roo.Template(
33615                '<table border="0" cellspacing="0" cellpadding="0">',
33616                "<tbody>{rows}</tbody>",
33617                "</table>"
33618             );
33619             tpls.body.disableFormats = true;
33620         }
33621         tpls.body.compile();
33622
33623         if(!tpls.row){
33624             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33625             tpls.row.disableFormats = true;
33626         }
33627         tpls.row.compile();
33628
33629         if(!tpls.cell){
33630             tpls.cell = new Roo.Template(
33631                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33632                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33633                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33634                 "</td>"
33635             );
33636             tpls.cell.disableFormats = true;
33637         }
33638         tpls.cell.compile();
33639
33640         this.templates = tpls;
33641     },
33642
33643     // remap these for backwards compat
33644     onColWidthChange : function(){
33645         this.updateColumns.apply(this, arguments);
33646     },
33647     onHeaderChange : function(){
33648         this.updateHeaders.apply(this, arguments);
33649     }, 
33650     onHiddenChange : function(){
33651         this.handleHiddenChange.apply(this, arguments);
33652     },
33653     onColumnMove : function(){
33654         this.handleColumnMove.apply(this, arguments);
33655     },
33656     onColumnLock : function(){
33657         this.handleLockChange.apply(this, arguments);
33658     },
33659
33660     onDataChange : function(){
33661         this.refresh();
33662         this.updateHeaderSortState();
33663     },
33664
33665     onClear : function(){
33666         this.refresh();
33667     },
33668
33669     onUpdate : function(ds, record){
33670         this.refreshRow(record);
33671     },
33672
33673     refreshRow : function(record){
33674         var ds = this.ds, index;
33675         if(typeof record == 'number'){
33676             index = record;
33677             record = ds.getAt(index);
33678         }else{
33679             index = ds.indexOf(record);
33680         }
33681         this.insertRows(ds, index, index, true);
33682         this.onRemove(ds, record, index+1, true);
33683         this.syncRowHeights(index, index);
33684         this.layout();
33685         this.fireEvent("rowupdated", this, index, record);
33686     },
33687
33688     onAdd : function(ds, records, index){
33689         this.insertRows(ds, index, index + (records.length-1));
33690     },
33691
33692     onRemove : function(ds, record, index, isUpdate){
33693         if(isUpdate !== true){
33694             this.fireEvent("beforerowremoved", this, index, record);
33695         }
33696         var bt = this.getBodyTable(), lt = this.getLockedTable();
33697         if(bt.rows[index]){
33698             bt.firstChild.removeChild(bt.rows[index]);
33699         }
33700         if(lt.rows[index]){
33701             lt.firstChild.removeChild(lt.rows[index]);
33702         }
33703         if(isUpdate !== true){
33704             this.stripeRows(index);
33705             this.syncRowHeights(index, index);
33706             this.layout();
33707             this.fireEvent("rowremoved", this, index, record);
33708         }
33709     },
33710
33711     onLoad : function(){
33712         this.scrollToTop();
33713     },
33714
33715     /**
33716      * Scrolls the grid to the top
33717      */
33718     scrollToTop : function(){
33719         if(this.scroller){
33720             this.scroller.dom.scrollTop = 0;
33721             this.syncScroll();
33722         }
33723     },
33724
33725     /**
33726      * Gets a panel in the header of the grid that can be used for toolbars etc.
33727      * After modifying the contents of this panel a call to grid.autoSize() may be
33728      * required to register any changes in size.
33729      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33730      * @return Roo.Element
33731      */
33732     getHeaderPanel : function(doShow){
33733         if(doShow){
33734             this.headerPanel.show();
33735         }
33736         return this.headerPanel;
33737     },
33738
33739     /**
33740      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33741      * After modifying the contents of this panel a call to grid.autoSize() may be
33742      * required to register any changes in size.
33743      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33744      * @return Roo.Element
33745      */
33746     getFooterPanel : function(doShow){
33747         if(doShow){
33748             this.footerPanel.show();
33749         }
33750         return this.footerPanel;
33751     },
33752
33753     initElements : function(){
33754         var E = Roo.Element;
33755         var el = this.grid.getGridEl().dom.firstChild;
33756         var cs = el.childNodes;
33757
33758         this.el = new E(el);
33759         
33760          this.focusEl = new E(el.firstChild);
33761         this.focusEl.swallowEvent("click", true);
33762         
33763         this.headerPanel = new E(cs[1]);
33764         this.headerPanel.enableDisplayMode("block");
33765
33766         this.scroller = new E(cs[2]);
33767         this.scrollSizer = new E(this.scroller.dom.firstChild);
33768
33769         this.lockedWrap = new E(cs[3]);
33770         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33771         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33772
33773         this.mainWrap = new E(cs[4]);
33774         this.mainHd = new E(this.mainWrap.dom.firstChild);
33775         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33776
33777         this.footerPanel = new E(cs[5]);
33778         this.footerPanel.enableDisplayMode("block");
33779
33780         this.resizeProxy = new E(cs[6]);
33781
33782         this.headerSelector = String.format(
33783            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33784            this.lockedHd.id, this.mainHd.id
33785         );
33786
33787         this.splitterSelector = String.format(
33788            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33789            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33790         );
33791     },
33792     idToCssName : function(s)
33793     {
33794         return s.replace(/[^a-z0-9]+/ig, '-');
33795     },
33796
33797     getHeaderCell : function(index){
33798         return Roo.DomQuery.select(this.headerSelector)[index];
33799     },
33800
33801     getHeaderCellMeasure : function(index){
33802         return this.getHeaderCell(index).firstChild;
33803     },
33804
33805     getHeaderCellText : function(index){
33806         return this.getHeaderCell(index).firstChild.firstChild;
33807     },
33808
33809     getLockedTable : function(){
33810         return this.lockedBody.dom.firstChild;
33811     },
33812
33813     getBodyTable : function(){
33814         return this.mainBody.dom.firstChild;
33815     },
33816
33817     getLockedRow : function(index){
33818         return this.getLockedTable().rows[index];
33819     },
33820
33821     getRow : function(index){
33822         return this.getBodyTable().rows[index];
33823     },
33824
33825     getRowComposite : function(index){
33826         if(!this.rowEl){
33827             this.rowEl = new Roo.CompositeElementLite();
33828         }
33829         var els = [], lrow, mrow;
33830         if(lrow = this.getLockedRow(index)){
33831             els.push(lrow);
33832         }
33833         if(mrow = this.getRow(index)){
33834             els.push(mrow);
33835         }
33836         this.rowEl.elements = els;
33837         return this.rowEl;
33838     },
33839     /**
33840      * Gets the 'td' of the cell
33841      * 
33842      * @param {Integer} rowIndex row to select
33843      * @param {Integer} colIndex column to select
33844      * 
33845      * @return {Object} 
33846      */
33847     getCell : function(rowIndex, colIndex){
33848         var locked = this.cm.getLockedCount();
33849         var source;
33850         if(colIndex < locked){
33851             source = this.lockedBody.dom.firstChild;
33852         }else{
33853             source = this.mainBody.dom.firstChild;
33854             colIndex -= locked;
33855         }
33856         return source.rows[rowIndex].childNodes[colIndex];
33857     },
33858
33859     getCellText : function(rowIndex, colIndex){
33860         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33861     },
33862
33863     getCellBox : function(cell){
33864         var b = this.fly(cell).getBox();
33865         if(Roo.isOpera){ // opera fails to report the Y
33866             b.y = cell.offsetTop + this.mainBody.getY();
33867         }
33868         return b;
33869     },
33870
33871     getCellIndex : function(cell){
33872         var id = String(cell.className).match(this.cellRE);
33873         if(id){
33874             return parseInt(id[1], 10);
33875         }
33876         return 0;
33877     },
33878
33879     findHeaderIndex : function(n){
33880         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33881         return r ? this.getCellIndex(r) : false;
33882     },
33883
33884     findHeaderCell : function(n){
33885         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33886         return r ? r : false;
33887     },
33888
33889     findRowIndex : function(n){
33890         if(!n){
33891             return false;
33892         }
33893         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33894         return r ? r.rowIndex : false;
33895     },
33896
33897     findCellIndex : function(node){
33898         var stop = this.el.dom;
33899         while(node && node != stop){
33900             if(this.findRE.test(node.className)){
33901                 return this.getCellIndex(node);
33902             }
33903             node = node.parentNode;
33904         }
33905         return false;
33906     },
33907
33908     getColumnId : function(index){
33909         return this.cm.getColumnId(index);
33910     },
33911
33912     getSplitters : function()
33913     {
33914         if(this.splitterSelector){
33915            return Roo.DomQuery.select(this.splitterSelector);
33916         }else{
33917             return null;
33918       }
33919     },
33920
33921     getSplitter : function(index){
33922         return this.getSplitters()[index];
33923     },
33924
33925     onRowOver : function(e, t){
33926         var row;
33927         if((row = this.findRowIndex(t)) !== false){
33928             this.getRowComposite(row).addClass("x-grid-row-over");
33929         }
33930     },
33931
33932     onRowOut : function(e, t){
33933         var row;
33934         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33935             this.getRowComposite(row).removeClass("x-grid-row-over");
33936         }
33937     },
33938
33939     renderHeaders : function(){
33940         var cm = this.cm;
33941         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33942         var cb = [], lb = [], sb = [], lsb = [], p = {};
33943         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33944             p.cellId = "x-grid-hd-0-" + i;
33945             p.splitId = "x-grid-csplit-0-" + i;
33946             p.id = cm.getColumnId(i);
33947             p.value = cm.getColumnHeader(i) || "";
33948             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33949             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33950             if(!cm.isLocked(i)){
33951                 cb[cb.length] = ct.apply(p);
33952                 sb[sb.length] = st.apply(p);
33953             }else{
33954                 lb[lb.length] = ct.apply(p);
33955                 lsb[lsb.length] = st.apply(p);
33956             }
33957         }
33958         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33959                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33960     },
33961
33962     updateHeaders : function(){
33963         var html = this.renderHeaders();
33964         this.lockedHd.update(html[0]);
33965         this.mainHd.update(html[1]);
33966     },
33967
33968     /**
33969      * Focuses the specified row.
33970      * @param {Number} row The row index
33971      */
33972     focusRow : function(row)
33973     {
33974         //Roo.log('GridView.focusRow');
33975         var x = this.scroller.dom.scrollLeft;
33976         this.focusCell(row, 0, false);
33977         this.scroller.dom.scrollLeft = x;
33978     },
33979
33980     /**
33981      * Focuses the specified cell.
33982      * @param {Number} row The row index
33983      * @param {Number} col The column index
33984      * @param {Boolean} hscroll false to disable horizontal scrolling
33985      */
33986     focusCell : function(row, col, hscroll)
33987     {
33988         //Roo.log('GridView.focusCell');
33989         var el = this.ensureVisible(row, col, hscroll);
33990         this.focusEl.alignTo(el, "tl-tl");
33991         if(Roo.isGecko){
33992             this.focusEl.focus();
33993         }else{
33994             this.focusEl.focus.defer(1, this.focusEl);
33995         }
33996     },
33997
33998     /**
33999      * Scrolls the specified cell into view
34000      * @param {Number} row The row index
34001      * @param {Number} col The column index
34002      * @param {Boolean} hscroll false to disable horizontal scrolling
34003      */
34004     ensureVisible : function(row, col, hscroll)
34005     {
34006         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34007         //return null; //disable for testing.
34008         if(typeof row != "number"){
34009             row = row.rowIndex;
34010         }
34011         if(row < 0 && row >= this.ds.getCount()){
34012             return  null;
34013         }
34014         col = (col !== undefined ? col : 0);
34015         var cm = this.grid.colModel;
34016         while(cm.isHidden(col)){
34017             col++;
34018         }
34019
34020         var el = this.getCell(row, col);
34021         if(!el){
34022             return null;
34023         }
34024         var c = this.scroller.dom;
34025
34026         var ctop = parseInt(el.offsetTop, 10);
34027         var cleft = parseInt(el.offsetLeft, 10);
34028         var cbot = ctop + el.offsetHeight;
34029         var cright = cleft + el.offsetWidth;
34030         
34031         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34032         var stop = parseInt(c.scrollTop, 10);
34033         var sleft = parseInt(c.scrollLeft, 10);
34034         var sbot = stop + ch;
34035         var sright = sleft + c.clientWidth;
34036         /*
34037         Roo.log('GridView.ensureVisible:' +
34038                 ' ctop:' + ctop +
34039                 ' c.clientHeight:' + c.clientHeight +
34040                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34041                 ' stop:' + stop +
34042                 ' cbot:' + cbot +
34043                 ' sbot:' + sbot +
34044                 ' ch:' + ch  
34045                 );
34046         */
34047         if(ctop < stop){
34048              c.scrollTop = ctop;
34049             //Roo.log("set scrolltop to ctop DISABLE?");
34050         }else if(cbot > sbot){
34051             //Roo.log("set scrolltop to cbot-ch");
34052             c.scrollTop = cbot-ch;
34053         }
34054         
34055         if(hscroll !== false){
34056             if(cleft < sleft){
34057                 c.scrollLeft = cleft;
34058             }else if(cright > sright){
34059                 c.scrollLeft = cright-c.clientWidth;
34060             }
34061         }
34062          
34063         return el;
34064     },
34065
34066     updateColumns : function(){
34067         this.grid.stopEditing();
34068         var cm = this.grid.colModel, colIds = this.getColumnIds();
34069         //var totalWidth = cm.getTotalWidth();
34070         var pos = 0;
34071         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34072             //if(cm.isHidden(i)) continue;
34073             var w = cm.getColumnWidth(i);
34074             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34075             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34076         }
34077         this.updateSplitters();
34078     },
34079
34080     generateRules : function(cm){
34081         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34082         Roo.util.CSS.removeStyleSheet(rulesId);
34083         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34084             var cid = cm.getColumnId(i);
34085             var align = '';
34086             if(cm.config[i].align){
34087                 align = 'text-align:'+cm.config[i].align+';';
34088             }
34089             var hidden = '';
34090             if(cm.isHidden(i)){
34091                 hidden = 'display:none;';
34092             }
34093             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34094             ruleBuf.push(
34095                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34096                     this.hdSelector, cid, " {\n", align, width, "}\n",
34097                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34098                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34099         }
34100         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34101     },
34102
34103     updateSplitters : function(){
34104         var cm = this.cm, s = this.getSplitters();
34105         if(s){ // splitters not created yet
34106             var pos = 0, locked = true;
34107             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34108                 if(cm.isHidden(i)) {
34109                     continue;
34110                 }
34111                 var w = cm.getColumnWidth(i); // make sure it's a number
34112                 if(!cm.isLocked(i) && locked){
34113                     pos = 0;
34114                     locked = false;
34115                 }
34116                 pos += w;
34117                 s[i].style.left = (pos-this.splitOffset) + "px";
34118             }
34119         }
34120     },
34121
34122     handleHiddenChange : function(colModel, colIndex, hidden){
34123         if(hidden){
34124             this.hideColumn(colIndex);
34125         }else{
34126             this.unhideColumn(colIndex);
34127         }
34128     },
34129
34130     hideColumn : function(colIndex){
34131         var cid = this.getColumnId(colIndex);
34132         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34133         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34134         if(Roo.isSafari){
34135             this.updateHeaders();
34136         }
34137         this.updateSplitters();
34138         this.layout();
34139     },
34140
34141     unhideColumn : function(colIndex){
34142         var cid = this.getColumnId(colIndex);
34143         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34144         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34145
34146         if(Roo.isSafari){
34147             this.updateHeaders();
34148         }
34149         this.updateSplitters();
34150         this.layout();
34151     },
34152
34153     insertRows : function(dm, firstRow, lastRow, isUpdate){
34154         if(firstRow == 0 && lastRow == dm.getCount()-1){
34155             this.refresh();
34156         }else{
34157             if(!isUpdate){
34158                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34159             }
34160             var s = this.getScrollState();
34161             var markup = this.renderRows(firstRow, lastRow);
34162             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34163             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34164             this.restoreScroll(s);
34165             if(!isUpdate){
34166                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34167                 this.syncRowHeights(firstRow, lastRow);
34168                 this.stripeRows(firstRow);
34169                 this.layout();
34170             }
34171         }
34172     },
34173
34174     bufferRows : function(markup, target, index){
34175         var before = null, trows = target.rows, tbody = target.tBodies[0];
34176         if(index < trows.length){
34177             before = trows[index];
34178         }
34179         var b = document.createElement("div");
34180         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34181         var rows = b.firstChild.rows;
34182         for(var i = 0, len = rows.length; i < len; i++){
34183             if(before){
34184                 tbody.insertBefore(rows[0], before);
34185             }else{
34186                 tbody.appendChild(rows[0]);
34187             }
34188         }
34189         b.innerHTML = "";
34190         b = null;
34191     },
34192
34193     deleteRows : function(dm, firstRow, lastRow){
34194         if(dm.getRowCount()<1){
34195             this.fireEvent("beforerefresh", this);
34196             this.mainBody.update("");
34197             this.lockedBody.update("");
34198             this.fireEvent("refresh", this);
34199         }else{
34200             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34201             var bt = this.getBodyTable();
34202             var tbody = bt.firstChild;
34203             var rows = bt.rows;
34204             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34205                 tbody.removeChild(rows[firstRow]);
34206             }
34207             this.stripeRows(firstRow);
34208             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34209         }
34210     },
34211
34212     updateRows : function(dataSource, firstRow, lastRow){
34213         var s = this.getScrollState();
34214         this.refresh();
34215         this.restoreScroll(s);
34216     },
34217
34218     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34219         if(!noRefresh){
34220            this.refresh();
34221         }
34222         this.updateHeaderSortState();
34223     },
34224
34225     getScrollState : function(){
34226         
34227         var sb = this.scroller.dom;
34228         return {left: sb.scrollLeft, top: sb.scrollTop};
34229     },
34230
34231     stripeRows : function(startRow){
34232         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34233             return;
34234         }
34235         startRow = startRow || 0;
34236         var rows = this.getBodyTable().rows;
34237         var lrows = this.getLockedTable().rows;
34238         var cls = ' x-grid-row-alt ';
34239         for(var i = startRow, len = rows.length; i < len; i++){
34240             var row = rows[i], lrow = lrows[i];
34241             var isAlt = ((i+1) % 2 == 0);
34242             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34243             if(isAlt == hasAlt){
34244                 continue;
34245             }
34246             if(isAlt){
34247                 row.className += " x-grid-row-alt";
34248             }else{
34249                 row.className = row.className.replace("x-grid-row-alt", "");
34250             }
34251             if(lrow){
34252                 lrow.className = row.className;
34253             }
34254         }
34255     },
34256
34257     restoreScroll : function(state){
34258         //Roo.log('GridView.restoreScroll');
34259         var sb = this.scroller.dom;
34260         sb.scrollLeft = state.left;
34261         sb.scrollTop = state.top;
34262         this.syncScroll();
34263     },
34264
34265     syncScroll : function(){
34266         //Roo.log('GridView.syncScroll');
34267         var sb = this.scroller.dom;
34268         var sh = this.mainHd.dom;
34269         var bs = this.mainBody.dom;
34270         var lv = this.lockedBody.dom;
34271         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34272         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34273     },
34274
34275     handleScroll : function(e){
34276         this.syncScroll();
34277         var sb = this.scroller.dom;
34278         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34279         e.stopEvent();
34280     },
34281
34282     handleWheel : function(e){
34283         var d = e.getWheelDelta();
34284         this.scroller.dom.scrollTop -= d*22;
34285         // set this here to prevent jumpy scrolling on large tables
34286         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34287         e.stopEvent();
34288     },
34289
34290     renderRows : function(startRow, endRow){
34291         // pull in all the crap needed to render rows
34292         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34293         var colCount = cm.getColumnCount();
34294
34295         if(ds.getCount() < 1){
34296             return ["", ""];
34297         }
34298
34299         // build a map for all the columns
34300         var cs = [];
34301         for(var i = 0; i < colCount; i++){
34302             var name = cm.getDataIndex(i);
34303             cs[i] = {
34304                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34305                 renderer : cm.getRenderer(i),
34306                 id : cm.getColumnId(i),
34307                 locked : cm.isLocked(i),
34308                 has_editor : cm.isCellEditable(i)
34309             };
34310         }
34311
34312         startRow = startRow || 0;
34313         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34314
34315         // records to render
34316         var rs = ds.getRange(startRow, endRow);
34317
34318         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34319     },
34320
34321     // As much as I hate to duplicate code, this was branched because FireFox really hates
34322     // [].join("") on strings. The performance difference was substantial enough to
34323     // branch this function
34324     doRender : Roo.isGecko ?
34325             function(cs, rs, ds, startRow, colCount, stripe){
34326                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34327                 // buffers
34328                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34329                 
34330                 var hasListener = this.grid.hasListener('rowclass');
34331                 var rowcfg = {};
34332                 for(var j = 0, len = rs.length; j < len; j++){
34333                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34334                     for(var i = 0; i < colCount; i++){
34335                         c = cs[i];
34336                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34337                         p.id = c.id;
34338                         p.css = p.attr = "";
34339                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34340                         if(p.value == undefined || p.value === "") {
34341                             p.value = "&#160;";
34342                         }
34343                         if(c.has_editor){
34344                             p.css += ' x-grid-editable-cell';
34345                         }
34346                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34347                             p.css +=  ' x-grid-dirty-cell';
34348                         }
34349                         var markup = ct.apply(p);
34350                         if(!c.locked){
34351                             cb+= markup;
34352                         }else{
34353                             lcb+= markup;
34354                         }
34355                     }
34356                     var alt = [];
34357                     if(stripe && ((rowIndex+1) % 2 == 0)){
34358                         alt.push("x-grid-row-alt")
34359                     }
34360                     if(r.dirty){
34361                         alt.push(  " x-grid-dirty-row");
34362                     }
34363                     rp.cells = lcb;
34364                     if(this.getRowClass){
34365                         alt.push(this.getRowClass(r, rowIndex));
34366                     }
34367                     if (hasListener) {
34368                         rowcfg = {
34369                              
34370                             record: r,
34371                             rowIndex : rowIndex,
34372                             rowClass : ''
34373                         };
34374                         this.grid.fireEvent('rowclass', this, rowcfg);
34375                         alt.push(rowcfg.rowClass);
34376                     }
34377                     rp.alt = alt.join(" ");
34378                     lbuf+= rt.apply(rp);
34379                     rp.cells = cb;
34380                     buf+=  rt.apply(rp);
34381                 }
34382                 return [lbuf, buf];
34383             } :
34384             function(cs, rs, ds, startRow, colCount, stripe){
34385                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34386                 // buffers
34387                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34388                 var hasListener = this.grid.hasListener('rowclass');
34389  
34390                 var rowcfg = {};
34391                 for(var j = 0, len = rs.length; j < len; j++){
34392                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34393                     for(var i = 0; i < colCount; i++){
34394                         c = cs[i];
34395                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34396                         p.id = c.id;
34397                         p.css = p.attr = "";
34398                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34399                         if(p.value == undefined || p.value === "") {
34400                             p.value = "&#160;";
34401                         }
34402                         //Roo.log(c);
34403                          if(c.has_editor){
34404                             p.css += ' x-grid-editable-cell';
34405                         }
34406                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34407                             p.css += ' x-grid-dirty-cell' 
34408                         }
34409                         
34410                         var markup = ct.apply(p);
34411                         if(!c.locked){
34412                             cb[cb.length] = markup;
34413                         }else{
34414                             lcb[lcb.length] = markup;
34415                         }
34416                     }
34417                     var alt = [];
34418                     if(stripe && ((rowIndex+1) % 2 == 0)){
34419                         alt.push( "x-grid-row-alt");
34420                     }
34421                     if(r.dirty){
34422                         alt.push(" x-grid-dirty-row");
34423                     }
34424                     rp.cells = lcb;
34425                     if(this.getRowClass){
34426                         alt.push( this.getRowClass(r, rowIndex));
34427                     }
34428                     if (hasListener) {
34429                         rowcfg = {
34430                              
34431                             record: r,
34432                             rowIndex : rowIndex,
34433                             rowClass : ''
34434                         };
34435                         this.grid.fireEvent('rowclass', this, rowcfg);
34436                         alt.push(rowcfg.rowClass);
34437                     }
34438                     
34439                     rp.alt = alt.join(" ");
34440                     rp.cells = lcb.join("");
34441                     lbuf[lbuf.length] = rt.apply(rp);
34442                     rp.cells = cb.join("");
34443                     buf[buf.length] =  rt.apply(rp);
34444                 }
34445                 return [lbuf.join(""), buf.join("")];
34446             },
34447
34448     renderBody : function(){
34449         var markup = this.renderRows();
34450         var bt = this.templates.body;
34451         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34452     },
34453
34454     /**
34455      * Refreshes the grid
34456      * @param {Boolean} headersToo
34457      */
34458     refresh : function(headersToo){
34459         this.fireEvent("beforerefresh", this);
34460         this.grid.stopEditing();
34461         var result = this.renderBody();
34462         this.lockedBody.update(result[0]);
34463         this.mainBody.update(result[1]);
34464         if(headersToo === true){
34465             this.updateHeaders();
34466             this.updateColumns();
34467             this.updateSplitters();
34468             this.updateHeaderSortState();
34469         }
34470         this.syncRowHeights();
34471         this.layout();
34472         this.fireEvent("refresh", this);
34473     },
34474
34475     handleColumnMove : function(cm, oldIndex, newIndex){
34476         this.indexMap = null;
34477         var s = this.getScrollState();
34478         this.refresh(true);
34479         this.restoreScroll(s);
34480         this.afterMove(newIndex);
34481     },
34482
34483     afterMove : function(colIndex){
34484         if(this.enableMoveAnim && Roo.enableFx){
34485             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34486         }
34487         // if multisort - fix sortOrder, and reload..
34488         if (this.grid.dataSource.multiSort) {
34489             // the we can call sort again..
34490             var dm = this.grid.dataSource;
34491             var cm = this.grid.colModel;
34492             var so = [];
34493             for(var i = 0; i < cm.config.length; i++ ) {
34494                 
34495                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34496                     continue; // dont' bother, it's not in sort list or being set.
34497                 }
34498                 
34499                 so.push(cm.config[i].dataIndex);
34500             };
34501             dm.sortOrder = so;
34502             dm.load(dm.lastOptions);
34503             
34504             
34505         }
34506         
34507     },
34508
34509     updateCell : function(dm, rowIndex, dataIndex){
34510         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34511         if(typeof colIndex == "undefined"){ // not present in grid
34512             return;
34513         }
34514         var cm = this.grid.colModel;
34515         var cell = this.getCell(rowIndex, colIndex);
34516         var cellText = this.getCellText(rowIndex, colIndex);
34517
34518         var p = {
34519             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34520             id : cm.getColumnId(colIndex),
34521             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34522         };
34523         var renderer = cm.getRenderer(colIndex);
34524         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34525         if(typeof val == "undefined" || val === "") {
34526             val = "&#160;";
34527         }
34528         cellText.innerHTML = val;
34529         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34530         this.syncRowHeights(rowIndex, rowIndex);
34531     },
34532
34533     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34534         var maxWidth = 0;
34535         if(this.grid.autoSizeHeaders){
34536             var h = this.getHeaderCellMeasure(colIndex);
34537             maxWidth = Math.max(maxWidth, h.scrollWidth);
34538         }
34539         var tb, index;
34540         if(this.cm.isLocked(colIndex)){
34541             tb = this.getLockedTable();
34542             index = colIndex;
34543         }else{
34544             tb = this.getBodyTable();
34545             index = colIndex - this.cm.getLockedCount();
34546         }
34547         if(tb && tb.rows){
34548             var rows = tb.rows;
34549             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34550             for(var i = 0; i < stopIndex; i++){
34551                 var cell = rows[i].childNodes[index].firstChild;
34552                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34553             }
34554         }
34555         return maxWidth + /*margin for error in IE*/ 5;
34556     },
34557     /**
34558      * Autofit a column to its content.
34559      * @param {Number} colIndex
34560      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34561      */
34562      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34563          if(this.cm.isHidden(colIndex)){
34564              return; // can't calc a hidden column
34565          }
34566         if(forceMinSize){
34567             var cid = this.cm.getColumnId(colIndex);
34568             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34569            if(this.grid.autoSizeHeaders){
34570                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34571            }
34572         }
34573         var newWidth = this.calcColumnWidth(colIndex);
34574         this.cm.setColumnWidth(colIndex,
34575             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34576         if(!suppressEvent){
34577             this.grid.fireEvent("columnresize", colIndex, newWidth);
34578         }
34579     },
34580
34581     /**
34582      * Autofits all columns to their content and then expands to fit any extra space in the grid
34583      */
34584      autoSizeColumns : function(){
34585         var cm = this.grid.colModel;
34586         var colCount = cm.getColumnCount();
34587         for(var i = 0; i < colCount; i++){
34588             this.autoSizeColumn(i, true, true);
34589         }
34590         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34591             this.fitColumns();
34592         }else{
34593             this.updateColumns();
34594             this.layout();
34595         }
34596     },
34597
34598     /**
34599      * Autofits all columns to the grid's width proportionate with their current size
34600      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34601      */
34602     fitColumns : function(reserveScrollSpace){
34603         var cm = this.grid.colModel;
34604         var colCount = cm.getColumnCount();
34605         var cols = [];
34606         var width = 0;
34607         var i, w;
34608         for (i = 0; i < colCount; i++){
34609             if(!cm.isHidden(i) && !cm.isFixed(i)){
34610                 w = cm.getColumnWidth(i);
34611                 cols.push(i);
34612                 cols.push(w);
34613                 width += w;
34614             }
34615         }
34616         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34617         if(reserveScrollSpace){
34618             avail -= 17;
34619         }
34620         var frac = (avail - cm.getTotalWidth())/width;
34621         while (cols.length){
34622             w = cols.pop();
34623             i = cols.pop();
34624             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34625         }
34626         this.updateColumns();
34627         this.layout();
34628     },
34629
34630     onRowSelect : function(rowIndex){
34631         var row = this.getRowComposite(rowIndex);
34632         row.addClass("x-grid-row-selected");
34633     },
34634
34635     onRowDeselect : function(rowIndex){
34636         var row = this.getRowComposite(rowIndex);
34637         row.removeClass("x-grid-row-selected");
34638     },
34639
34640     onCellSelect : function(row, col){
34641         var cell = this.getCell(row, col);
34642         if(cell){
34643             Roo.fly(cell).addClass("x-grid-cell-selected");
34644         }
34645     },
34646
34647     onCellDeselect : function(row, col){
34648         var cell = this.getCell(row, col);
34649         if(cell){
34650             Roo.fly(cell).removeClass("x-grid-cell-selected");
34651         }
34652     },
34653
34654     updateHeaderSortState : function(){
34655         
34656         // sort state can be single { field: xxx, direction : yyy}
34657         // or   { xxx=>ASC , yyy : DESC ..... }
34658         
34659         var mstate = {};
34660         if (!this.ds.multiSort) { 
34661             var state = this.ds.getSortState();
34662             if(!state){
34663                 return;
34664             }
34665             mstate[state.field] = state.direction;
34666             // FIXME... - this is not used here.. but might be elsewhere..
34667             this.sortState = state;
34668             
34669         } else {
34670             mstate = this.ds.sortToggle;
34671         }
34672         //remove existing sort classes..
34673         
34674         var sc = this.sortClasses;
34675         var hds = this.el.select(this.headerSelector).removeClass(sc);
34676         
34677         for(var f in mstate) {
34678         
34679             var sortColumn = this.cm.findColumnIndex(f);
34680             
34681             if(sortColumn != -1){
34682                 var sortDir = mstate[f];        
34683                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34684             }
34685         }
34686         
34687          
34688         
34689     },
34690
34691
34692     handleHeaderClick : function(g, index,e){
34693         
34694         Roo.log("header click");
34695         
34696         if (Roo.isTouch) {
34697             // touch events on header are handled by context
34698             this.handleHdCtx(g,index,e);
34699             return;
34700         }
34701         
34702         
34703         if(this.headersDisabled){
34704             return;
34705         }
34706         var dm = g.dataSource, cm = g.colModel;
34707         if(!cm.isSortable(index)){
34708             return;
34709         }
34710         g.stopEditing();
34711         
34712         if (dm.multiSort) {
34713             // update the sortOrder
34714             var so = [];
34715             for(var i = 0; i < cm.config.length; i++ ) {
34716                 
34717                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34718                     continue; // dont' bother, it's not in sort list or being set.
34719                 }
34720                 
34721                 so.push(cm.config[i].dataIndex);
34722             };
34723             dm.sortOrder = so;
34724         }
34725         
34726         
34727         dm.sort(cm.getDataIndex(index));
34728     },
34729
34730
34731     destroy : function(){
34732         if(this.colMenu){
34733             this.colMenu.removeAll();
34734             Roo.menu.MenuMgr.unregister(this.colMenu);
34735             this.colMenu.getEl().remove();
34736             delete this.colMenu;
34737         }
34738         if(this.hmenu){
34739             this.hmenu.removeAll();
34740             Roo.menu.MenuMgr.unregister(this.hmenu);
34741             this.hmenu.getEl().remove();
34742             delete this.hmenu;
34743         }
34744         if(this.grid.enableColumnMove){
34745             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34746             if(dds){
34747                 for(var dd in dds){
34748                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34749                         var elid = dds[dd].dragElId;
34750                         dds[dd].unreg();
34751                         Roo.get(elid).remove();
34752                     } else if(dds[dd].config.isTarget){
34753                         dds[dd].proxyTop.remove();
34754                         dds[dd].proxyBottom.remove();
34755                         dds[dd].unreg();
34756                     }
34757                     if(Roo.dd.DDM.locationCache[dd]){
34758                         delete Roo.dd.DDM.locationCache[dd];
34759                     }
34760                 }
34761                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34762             }
34763         }
34764         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34765         this.bind(null, null);
34766         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34767     },
34768
34769     handleLockChange : function(){
34770         this.refresh(true);
34771     },
34772
34773     onDenyColumnLock : function(){
34774
34775     },
34776
34777     onDenyColumnHide : function(){
34778
34779     },
34780
34781     handleHdMenuClick : function(item){
34782         var index = this.hdCtxIndex;
34783         var cm = this.cm, ds = this.ds;
34784         switch(item.id){
34785             case "asc":
34786                 ds.sort(cm.getDataIndex(index), "ASC");
34787                 break;
34788             case "desc":
34789                 ds.sort(cm.getDataIndex(index), "DESC");
34790                 break;
34791             case "lock":
34792                 var lc = cm.getLockedCount();
34793                 if(cm.getColumnCount(true) <= lc+1){
34794                     this.onDenyColumnLock();
34795                     return;
34796                 }
34797                 if(lc != index){
34798                     cm.setLocked(index, true, true);
34799                     cm.moveColumn(index, lc);
34800                     this.grid.fireEvent("columnmove", index, lc);
34801                 }else{
34802                     cm.setLocked(index, true);
34803                 }
34804             break;
34805             case "unlock":
34806                 var lc = cm.getLockedCount();
34807                 if((lc-1) != index){
34808                     cm.setLocked(index, false, true);
34809                     cm.moveColumn(index, lc-1);
34810                     this.grid.fireEvent("columnmove", index, lc-1);
34811                 }else{
34812                     cm.setLocked(index, false);
34813                 }
34814             break;
34815             case 'wider': // used to expand cols on touch..
34816             case 'narrow':
34817                 var cw = cm.getColumnWidth(index);
34818                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34819                 cw = Math.max(0, cw);
34820                 cw = Math.min(cw,4000);
34821                 cm.setColumnWidth(index, cw);
34822                 break;
34823                 
34824             default:
34825                 index = cm.getIndexById(item.id.substr(4));
34826                 if(index != -1){
34827                     if(item.checked && cm.getColumnCount(true) <= 1){
34828                         this.onDenyColumnHide();
34829                         return false;
34830                     }
34831                     cm.setHidden(index, item.checked);
34832                 }
34833         }
34834         return true;
34835     },
34836
34837     beforeColMenuShow : function(){
34838         var cm = this.cm,  colCount = cm.getColumnCount();
34839         this.colMenu.removeAll();
34840         for(var i = 0; i < colCount; i++){
34841             this.colMenu.add(new Roo.menu.CheckItem({
34842                 id: "col-"+cm.getColumnId(i),
34843                 text: cm.getColumnHeader(i),
34844                 checked: !cm.isHidden(i),
34845                 hideOnClick:false
34846             }));
34847         }
34848     },
34849
34850     handleHdCtx : function(g, index, e){
34851         e.stopEvent();
34852         var hd = this.getHeaderCell(index);
34853         this.hdCtxIndex = index;
34854         var ms = this.hmenu.items, cm = this.cm;
34855         ms.get("asc").setDisabled(!cm.isSortable(index));
34856         ms.get("desc").setDisabled(!cm.isSortable(index));
34857         if(this.grid.enableColLock !== false){
34858             ms.get("lock").setDisabled(cm.isLocked(index));
34859             ms.get("unlock").setDisabled(!cm.isLocked(index));
34860         }
34861         this.hmenu.show(hd, "tl-bl");
34862     },
34863
34864     handleHdOver : function(e){
34865         var hd = this.findHeaderCell(e.getTarget());
34866         if(hd && !this.headersDisabled){
34867             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34868                this.fly(hd).addClass("x-grid-hd-over");
34869             }
34870         }
34871     },
34872
34873     handleHdOut : function(e){
34874         var hd = this.findHeaderCell(e.getTarget());
34875         if(hd){
34876             this.fly(hd).removeClass("x-grid-hd-over");
34877         }
34878     },
34879
34880     handleSplitDblClick : function(e, t){
34881         var i = this.getCellIndex(t);
34882         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34883             this.autoSizeColumn(i, true);
34884             this.layout();
34885         }
34886     },
34887
34888     render : function(){
34889
34890         var cm = this.cm;
34891         var colCount = cm.getColumnCount();
34892
34893         if(this.grid.monitorWindowResize === true){
34894             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34895         }
34896         var header = this.renderHeaders();
34897         var body = this.templates.body.apply({rows:""});
34898         var html = this.templates.master.apply({
34899             lockedBody: body,
34900             body: body,
34901             lockedHeader: header[0],
34902             header: header[1]
34903         });
34904
34905         //this.updateColumns();
34906
34907         this.grid.getGridEl().dom.innerHTML = html;
34908
34909         this.initElements();
34910         
34911         // a kludge to fix the random scolling effect in webkit
34912         this.el.on("scroll", function() {
34913             this.el.dom.scrollTop=0; // hopefully not recursive..
34914         },this);
34915
34916         this.scroller.on("scroll", this.handleScroll, this);
34917         this.lockedBody.on("mousewheel", this.handleWheel, this);
34918         this.mainBody.on("mousewheel", this.handleWheel, this);
34919
34920         this.mainHd.on("mouseover", this.handleHdOver, this);
34921         this.mainHd.on("mouseout", this.handleHdOut, this);
34922         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34923                 {delegate: "."+this.splitClass});
34924
34925         this.lockedHd.on("mouseover", this.handleHdOver, this);
34926         this.lockedHd.on("mouseout", this.handleHdOut, this);
34927         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34928                 {delegate: "."+this.splitClass});
34929
34930         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34931             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34932         }
34933
34934         this.updateSplitters();
34935
34936         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34937             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34938             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34939         }
34940
34941         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34942             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34943             this.hmenu.add(
34944                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34945                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34946             );
34947             if(this.grid.enableColLock !== false){
34948                 this.hmenu.add('-',
34949                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34950                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34951                 );
34952             }
34953             if (Roo.isTouch) {
34954                  this.hmenu.add('-',
34955                     {id:"wider", text: this.columnsWiderText},
34956                     {id:"narrow", text: this.columnsNarrowText }
34957                 );
34958                 
34959                  
34960             }
34961             
34962             if(this.grid.enableColumnHide !== false){
34963
34964                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34965                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34966                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34967
34968                 this.hmenu.add('-',
34969                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34970                 );
34971             }
34972             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34973
34974             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34975         }
34976
34977         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34978             this.dd = new Roo.grid.GridDragZone(this.grid, {
34979                 ddGroup : this.grid.ddGroup || 'GridDD'
34980             });
34981             
34982         }
34983
34984         /*
34985         for(var i = 0; i < colCount; i++){
34986             if(cm.isHidden(i)){
34987                 this.hideColumn(i);
34988             }
34989             if(cm.config[i].align){
34990                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34991                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34992             }
34993         }*/
34994         
34995         this.updateHeaderSortState();
34996
34997         this.beforeInitialResize();
34998         this.layout(true);
34999
35000         // two part rendering gives faster view to the user
35001         this.renderPhase2.defer(1, this);
35002     },
35003
35004     renderPhase2 : function(){
35005         // render the rows now
35006         this.refresh();
35007         if(this.grid.autoSizeColumns){
35008             this.autoSizeColumns();
35009         }
35010     },
35011
35012     beforeInitialResize : function(){
35013
35014     },
35015
35016     onColumnSplitterMoved : function(i, w){
35017         this.userResized = true;
35018         var cm = this.grid.colModel;
35019         cm.setColumnWidth(i, w, true);
35020         var cid = cm.getColumnId(i);
35021         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35022         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35023         this.updateSplitters();
35024         this.layout();
35025         this.grid.fireEvent("columnresize", i, w);
35026     },
35027
35028     syncRowHeights : function(startIndex, endIndex){
35029         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35030             startIndex = startIndex || 0;
35031             var mrows = this.getBodyTable().rows;
35032             var lrows = this.getLockedTable().rows;
35033             var len = mrows.length-1;
35034             endIndex = Math.min(endIndex || len, len);
35035             for(var i = startIndex; i <= endIndex; i++){
35036                 var m = mrows[i], l = lrows[i];
35037                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35038                 m.style.height = l.style.height = h + "px";
35039             }
35040         }
35041     },
35042
35043     layout : function(initialRender, is2ndPass){
35044         var g = this.grid;
35045         var auto = g.autoHeight;
35046         var scrollOffset = 16;
35047         var c = g.getGridEl(), cm = this.cm,
35048                 expandCol = g.autoExpandColumn,
35049                 gv = this;
35050         //c.beginMeasure();
35051
35052         if(!c.dom.offsetWidth){ // display:none?
35053             if(initialRender){
35054                 this.lockedWrap.show();
35055                 this.mainWrap.show();
35056             }
35057             return;
35058         }
35059
35060         var hasLock = this.cm.isLocked(0);
35061
35062         var tbh = this.headerPanel.getHeight();
35063         var bbh = this.footerPanel.getHeight();
35064
35065         if(auto){
35066             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35067             var newHeight = ch + c.getBorderWidth("tb");
35068             if(g.maxHeight){
35069                 newHeight = Math.min(g.maxHeight, newHeight);
35070             }
35071             c.setHeight(newHeight);
35072         }
35073
35074         if(g.autoWidth){
35075             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35076         }
35077
35078         var s = this.scroller;
35079
35080         var csize = c.getSize(true);
35081
35082         this.el.setSize(csize.width, csize.height);
35083
35084         this.headerPanel.setWidth(csize.width);
35085         this.footerPanel.setWidth(csize.width);
35086
35087         var hdHeight = this.mainHd.getHeight();
35088         var vw = csize.width;
35089         var vh = csize.height - (tbh + bbh);
35090
35091         s.setSize(vw, vh);
35092
35093         var bt = this.getBodyTable();
35094         
35095         if(cm.getLockedCount() == cm.config.length){
35096             bt = this.getLockedTable();
35097         }
35098         
35099         var ltWidth = hasLock ?
35100                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35101
35102         var scrollHeight = bt.offsetHeight;
35103         var scrollWidth = ltWidth + bt.offsetWidth;
35104         var vscroll = false, hscroll = false;
35105
35106         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35107
35108         var lw = this.lockedWrap, mw = this.mainWrap;
35109         var lb = this.lockedBody, mb = this.mainBody;
35110
35111         setTimeout(function(){
35112             var t = s.dom.offsetTop;
35113             var w = s.dom.clientWidth,
35114                 h = s.dom.clientHeight;
35115
35116             lw.setTop(t);
35117             lw.setSize(ltWidth, h);
35118
35119             mw.setLeftTop(ltWidth, t);
35120             mw.setSize(w-ltWidth, h);
35121
35122             lb.setHeight(h-hdHeight);
35123             mb.setHeight(h-hdHeight);
35124
35125             if(is2ndPass !== true && !gv.userResized && expandCol){
35126                 // high speed resize without full column calculation
35127                 
35128                 var ci = cm.getIndexById(expandCol);
35129                 if (ci < 0) {
35130                     ci = cm.findColumnIndex(expandCol);
35131                 }
35132                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35133                 var expandId = cm.getColumnId(ci);
35134                 var  tw = cm.getTotalWidth(false);
35135                 var currentWidth = cm.getColumnWidth(ci);
35136                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35137                 if(currentWidth != cw){
35138                     cm.setColumnWidth(ci, cw, true);
35139                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35140                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35141                     gv.updateSplitters();
35142                     gv.layout(false, true);
35143                 }
35144             }
35145
35146             if(initialRender){
35147                 lw.show();
35148                 mw.show();
35149             }
35150             //c.endMeasure();
35151         }, 10);
35152     },
35153
35154     onWindowResize : function(){
35155         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35156             return;
35157         }
35158         this.layout();
35159     },
35160
35161     appendFooter : function(parentEl){
35162         return null;
35163     },
35164
35165     sortAscText : "Sort Ascending",
35166     sortDescText : "Sort Descending",
35167     lockText : "Lock Column",
35168     unlockText : "Unlock Column",
35169     columnsText : "Columns",
35170  
35171     columnsWiderText : "Wider",
35172     columnsNarrowText : "Thinner"
35173 });
35174
35175
35176 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35177     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35178     this.proxy.el.addClass('x-grid3-col-dd');
35179 };
35180
35181 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35182     handleMouseDown : function(e){
35183
35184     },
35185
35186     callHandleMouseDown : function(e){
35187         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35188     }
35189 });
35190 /*
35191  * Based on:
35192  * Ext JS Library 1.1.1
35193  * Copyright(c) 2006-2007, Ext JS, LLC.
35194  *
35195  * Originally Released Under LGPL - original licence link has changed is not relivant.
35196  *
35197  * Fork - LGPL
35198  * <script type="text/javascript">
35199  */
35200  
35201 // private
35202 // This is a support class used internally by the Grid components
35203 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35204     this.grid = grid;
35205     this.view = grid.getView();
35206     this.proxy = this.view.resizeProxy;
35207     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35208         "gridSplitters" + this.grid.getGridEl().id, {
35209         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35210     });
35211     this.setHandleElId(Roo.id(hd));
35212     this.setOuterHandleElId(Roo.id(hd2));
35213     this.scroll = false;
35214 };
35215 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35216     fly: Roo.Element.fly,
35217
35218     b4StartDrag : function(x, y){
35219         this.view.headersDisabled = true;
35220         this.proxy.setHeight(this.view.mainWrap.getHeight());
35221         var w = this.cm.getColumnWidth(this.cellIndex);
35222         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35223         this.resetConstraints();
35224         this.setXConstraint(minw, 1000);
35225         this.setYConstraint(0, 0);
35226         this.minX = x - minw;
35227         this.maxX = x + 1000;
35228         this.startPos = x;
35229         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35230     },
35231
35232
35233     handleMouseDown : function(e){
35234         ev = Roo.EventObject.setEvent(e);
35235         var t = this.fly(ev.getTarget());
35236         if(t.hasClass("x-grid-split")){
35237             this.cellIndex = this.view.getCellIndex(t.dom);
35238             this.split = t.dom;
35239             this.cm = this.grid.colModel;
35240             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35241                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35242             }
35243         }
35244     },
35245
35246     endDrag : function(e){
35247         this.view.headersDisabled = false;
35248         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35249         var diff = endX - this.startPos;
35250         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35251     },
35252
35253     autoOffset : function(){
35254         this.setDelta(0,0);
35255     }
35256 });/*
35257  * Based on:
35258  * Ext JS Library 1.1.1
35259  * Copyright(c) 2006-2007, Ext JS, LLC.
35260  *
35261  * Originally Released Under LGPL - original licence link has changed is not relivant.
35262  *
35263  * Fork - LGPL
35264  * <script type="text/javascript">
35265  */
35266  
35267 // private
35268 // This is a support class used internally by the Grid components
35269 Roo.grid.GridDragZone = function(grid, config){
35270     this.view = grid.getView();
35271     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35272     if(this.view.lockedBody){
35273         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35274         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35275     }
35276     this.scroll = false;
35277     this.grid = grid;
35278     this.ddel = document.createElement('div');
35279     this.ddel.className = 'x-grid-dd-wrap';
35280 };
35281
35282 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35283     ddGroup : "GridDD",
35284
35285     getDragData : function(e){
35286         var t = Roo.lib.Event.getTarget(e);
35287         var rowIndex = this.view.findRowIndex(t);
35288         var sm = this.grid.selModel;
35289             
35290         //Roo.log(rowIndex);
35291         
35292         if (sm.getSelectedCell) {
35293             // cell selection..
35294             if (!sm.getSelectedCell()) {
35295                 return false;
35296             }
35297             if (rowIndex != sm.getSelectedCell()[0]) {
35298                 return false;
35299             }
35300         
35301         }
35302         
35303         if(rowIndex !== false){
35304             
35305             // if editorgrid.. 
35306             
35307             
35308             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35309                
35310             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35311               //  
35312             //}
35313             if (e.hasModifier()){
35314                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35315             }
35316             
35317             Roo.log("getDragData");
35318             
35319             return {
35320                 grid: this.grid,
35321                 ddel: this.ddel,
35322                 rowIndex: rowIndex,
35323                 selections:sm.getSelections ? sm.getSelections() : (
35324                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35325                 )
35326             };
35327         }
35328         return false;
35329     },
35330
35331     onInitDrag : function(e){
35332         var data = this.dragData;
35333         this.ddel.innerHTML = this.grid.getDragDropText();
35334         this.proxy.update(this.ddel);
35335         // fire start drag?
35336     },
35337
35338     afterRepair : function(){
35339         this.dragging = false;
35340     },
35341
35342     getRepairXY : function(e, data){
35343         return false;
35344     },
35345
35346     onEndDrag : function(data, e){
35347         // fire end drag?
35348     },
35349
35350     onValidDrop : function(dd, e, id){
35351         // fire drag drop?
35352         this.hideProxy();
35353     },
35354
35355     beforeInvalidDrop : function(e, id){
35356
35357     }
35358 });/*
35359  * Based on:
35360  * Ext JS Library 1.1.1
35361  * Copyright(c) 2006-2007, Ext JS, LLC.
35362  *
35363  * Originally Released Under LGPL - original licence link has changed is not relivant.
35364  *
35365  * Fork - LGPL
35366  * <script type="text/javascript">
35367  */
35368  
35369
35370 /**
35371  * @class Roo.grid.ColumnModel
35372  * @extends Roo.util.Observable
35373  * This is the default implementation of a ColumnModel used by the Grid. It defines
35374  * the columns in the grid.
35375  * <br>Usage:<br>
35376  <pre><code>
35377  var colModel = new Roo.grid.ColumnModel([
35378         {header: "Ticker", width: 60, sortable: true, locked: true},
35379         {header: "Company Name", width: 150, sortable: true},
35380         {header: "Market Cap.", width: 100, sortable: true},
35381         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35382         {header: "Employees", width: 100, sortable: true, resizable: false}
35383  ]);
35384  </code></pre>
35385  * <p>
35386  
35387  * The config options listed for this class are options which may appear in each
35388  * individual column definition.
35389  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35390  * @constructor
35391  * @param {Object} config An Array of column config objects. See this class's
35392  * config objects for details.
35393 */
35394 Roo.grid.ColumnModel = function(config){
35395         /**
35396      * The config passed into the constructor
35397      */
35398     this.config = config;
35399     this.lookup = {};
35400
35401     // if no id, create one
35402     // if the column does not have a dataIndex mapping,
35403     // map it to the order it is in the config
35404     for(var i = 0, len = config.length; i < len; i++){
35405         var c = config[i];
35406         if(typeof c.dataIndex == "undefined"){
35407             c.dataIndex = i;
35408         }
35409         if(typeof c.renderer == "string"){
35410             c.renderer = Roo.util.Format[c.renderer];
35411         }
35412         if(typeof c.id == "undefined"){
35413             c.id = Roo.id();
35414         }
35415         if(c.editor && c.editor.xtype){
35416             c.editor  = Roo.factory(c.editor, Roo.grid);
35417         }
35418         if(c.editor && c.editor.isFormField){
35419             c.editor = new Roo.grid.GridEditor(c.editor);
35420         }
35421         this.lookup[c.id] = c;
35422     }
35423
35424     /**
35425      * The width of columns which have no width specified (defaults to 100)
35426      * @type Number
35427      */
35428     this.defaultWidth = 100;
35429
35430     /**
35431      * Default sortable of columns which have no sortable specified (defaults to false)
35432      * @type Boolean
35433      */
35434     this.defaultSortable = false;
35435
35436     this.addEvents({
35437         /**
35438              * @event widthchange
35439              * Fires when the width of a column changes.
35440              * @param {ColumnModel} this
35441              * @param {Number} columnIndex The column index
35442              * @param {Number} newWidth The new width
35443              */
35444             "widthchange": true,
35445         /**
35446              * @event headerchange
35447              * Fires when the text of a header changes.
35448              * @param {ColumnModel} this
35449              * @param {Number} columnIndex The column index
35450              * @param {Number} newText The new header text
35451              */
35452             "headerchange": true,
35453         /**
35454              * @event hiddenchange
35455              * Fires when a column is hidden or "unhidden".
35456              * @param {ColumnModel} this
35457              * @param {Number} columnIndex The column index
35458              * @param {Boolean} hidden true if hidden, false otherwise
35459              */
35460             "hiddenchange": true,
35461             /**
35462          * @event columnmoved
35463          * Fires when a column is moved.
35464          * @param {ColumnModel} this
35465          * @param {Number} oldIndex
35466          * @param {Number} newIndex
35467          */
35468         "columnmoved" : true,
35469         /**
35470          * @event columlockchange
35471          * Fires when a column's locked state is changed
35472          * @param {ColumnModel} this
35473          * @param {Number} colIndex
35474          * @param {Boolean} locked true if locked
35475          */
35476         "columnlockchange" : true
35477     });
35478     Roo.grid.ColumnModel.superclass.constructor.call(this);
35479 };
35480 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35481     /**
35482      * @cfg {String} header The header text to display in the Grid view.
35483      */
35484     /**
35485      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35486      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35487      * specified, the column's index is used as an index into the Record's data Array.
35488      */
35489     /**
35490      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35491      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35492      */
35493     /**
35494      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35495      * Defaults to the value of the {@link #defaultSortable} property.
35496      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35497      */
35498     /**
35499      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35500      */
35501     /**
35502      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35503      */
35504     /**
35505      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35506      */
35507     /**
35508      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35509      */
35510     /**
35511      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35512      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35513      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35514      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35515      */
35516        /**
35517      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35518      */
35519     /**
35520      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35521      */
35522     /**
35523      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35524      */
35525     /**
35526      * @cfg {String} cursor (Optional)
35527      */
35528     /**
35529      * @cfg {String} tooltip (Optional)
35530      */
35531     /**
35532      * @cfg {Number} xs (Optional)
35533      */
35534     /**
35535      * @cfg {Number} sm (Optional)
35536      */
35537     /**
35538      * @cfg {Number} md (Optional)
35539      */
35540     /**
35541      * @cfg {Number} lg (Optional)
35542      */
35543     /**
35544      * Returns the id of the column at the specified index.
35545      * @param {Number} index The column index
35546      * @return {String} the id
35547      */
35548     getColumnId : function(index){
35549         return this.config[index].id;
35550     },
35551
35552     /**
35553      * Returns the column for a specified id.
35554      * @param {String} id The column id
35555      * @return {Object} the column
35556      */
35557     getColumnById : function(id){
35558         return this.lookup[id];
35559     },
35560
35561     
35562     /**
35563      * Returns the column for a specified dataIndex.
35564      * @param {String} dataIndex The column dataIndex
35565      * @return {Object|Boolean} the column or false if not found
35566      */
35567     getColumnByDataIndex: function(dataIndex){
35568         var index = this.findColumnIndex(dataIndex);
35569         return index > -1 ? this.config[index] : false;
35570     },
35571     
35572     /**
35573      * Returns the index for a specified column id.
35574      * @param {String} id The column id
35575      * @return {Number} the index, or -1 if not found
35576      */
35577     getIndexById : function(id){
35578         for(var i = 0, len = this.config.length; i < len; i++){
35579             if(this.config[i].id == id){
35580                 return i;
35581             }
35582         }
35583         return -1;
35584     },
35585     
35586     /**
35587      * Returns the index for a specified column dataIndex.
35588      * @param {String} dataIndex The column dataIndex
35589      * @return {Number} the index, or -1 if not found
35590      */
35591     
35592     findColumnIndex : function(dataIndex){
35593         for(var i = 0, len = this.config.length; i < len; i++){
35594             if(this.config[i].dataIndex == dataIndex){
35595                 return i;
35596             }
35597         }
35598         return -1;
35599     },
35600     
35601     
35602     moveColumn : function(oldIndex, newIndex){
35603         var c = this.config[oldIndex];
35604         this.config.splice(oldIndex, 1);
35605         this.config.splice(newIndex, 0, c);
35606         this.dataMap = null;
35607         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35608     },
35609
35610     isLocked : function(colIndex){
35611         return this.config[colIndex].locked === true;
35612     },
35613
35614     setLocked : function(colIndex, value, suppressEvent){
35615         if(this.isLocked(colIndex) == value){
35616             return;
35617         }
35618         this.config[colIndex].locked = value;
35619         if(!suppressEvent){
35620             this.fireEvent("columnlockchange", this, colIndex, value);
35621         }
35622     },
35623
35624     getTotalLockedWidth : function(){
35625         var totalWidth = 0;
35626         for(var i = 0; i < this.config.length; i++){
35627             if(this.isLocked(i) && !this.isHidden(i)){
35628                 this.totalWidth += this.getColumnWidth(i);
35629             }
35630         }
35631         return totalWidth;
35632     },
35633
35634     getLockedCount : function(){
35635         for(var i = 0, len = this.config.length; i < len; i++){
35636             if(!this.isLocked(i)){
35637                 return i;
35638             }
35639         }
35640         
35641         return this.config.length;
35642     },
35643
35644     /**
35645      * Returns the number of columns.
35646      * @return {Number}
35647      */
35648     getColumnCount : function(visibleOnly){
35649         if(visibleOnly === true){
35650             var c = 0;
35651             for(var i = 0, len = this.config.length; i < len; i++){
35652                 if(!this.isHidden(i)){
35653                     c++;
35654                 }
35655             }
35656             return c;
35657         }
35658         return this.config.length;
35659     },
35660
35661     /**
35662      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35663      * @param {Function} fn
35664      * @param {Object} scope (optional)
35665      * @return {Array} result
35666      */
35667     getColumnsBy : function(fn, scope){
35668         var r = [];
35669         for(var i = 0, len = this.config.length; i < len; i++){
35670             var c = this.config[i];
35671             if(fn.call(scope||this, c, i) === true){
35672                 r[r.length] = c;
35673             }
35674         }
35675         return r;
35676     },
35677
35678     /**
35679      * Returns true if the specified column is sortable.
35680      * @param {Number} col The column index
35681      * @return {Boolean}
35682      */
35683     isSortable : function(col){
35684         if(typeof this.config[col].sortable == "undefined"){
35685             return this.defaultSortable;
35686         }
35687         return this.config[col].sortable;
35688     },
35689
35690     /**
35691      * Returns the rendering (formatting) function defined for the column.
35692      * @param {Number} col The column index.
35693      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35694      */
35695     getRenderer : function(col){
35696         if(!this.config[col].renderer){
35697             return Roo.grid.ColumnModel.defaultRenderer;
35698         }
35699         return this.config[col].renderer;
35700     },
35701
35702     /**
35703      * Sets the rendering (formatting) function for a column.
35704      * @param {Number} col The column index
35705      * @param {Function} fn The function to use to process the cell's raw data
35706      * to return HTML markup for the grid view. The render function is called with
35707      * the following parameters:<ul>
35708      * <li>Data value.</li>
35709      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35710      * <li>css A CSS style string to apply to the table cell.</li>
35711      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35712      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35713      * <li>Row index</li>
35714      * <li>Column index</li>
35715      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35716      */
35717     setRenderer : function(col, fn){
35718         this.config[col].renderer = fn;
35719     },
35720
35721     /**
35722      * Returns the width for the specified column.
35723      * @param {Number} col The column index
35724      * @return {Number}
35725      */
35726     getColumnWidth : function(col){
35727         return this.config[col].width * 1 || this.defaultWidth;
35728     },
35729
35730     /**
35731      * Sets the width for a column.
35732      * @param {Number} col The column index
35733      * @param {Number} width The new width
35734      */
35735     setColumnWidth : function(col, width, suppressEvent){
35736         this.config[col].width = width;
35737         this.totalWidth = null;
35738         if(!suppressEvent){
35739              this.fireEvent("widthchange", this, col, width);
35740         }
35741     },
35742
35743     /**
35744      * Returns the total width of all columns.
35745      * @param {Boolean} includeHidden True to include hidden column widths
35746      * @return {Number}
35747      */
35748     getTotalWidth : function(includeHidden){
35749         if(!this.totalWidth){
35750             this.totalWidth = 0;
35751             for(var i = 0, len = this.config.length; i < len; i++){
35752                 if(includeHidden || !this.isHidden(i)){
35753                     this.totalWidth += this.getColumnWidth(i);
35754                 }
35755             }
35756         }
35757         return this.totalWidth;
35758     },
35759
35760     /**
35761      * Returns the header for the specified column.
35762      * @param {Number} col The column index
35763      * @return {String}
35764      */
35765     getColumnHeader : function(col){
35766         return this.config[col].header;
35767     },
35768
35769     /**
35770      * Sets the header for a column.
35771      * @param {Number} col The column index
35772      * @param {String} header The new header
35773      */
35774     setColumnHeader : function(col, header){
35775         this.config[col].header = header;
35776         this.fireEvent("headerchange", this, col, header);
35777     },
35778
35779     /**
35780      * Returns the tooltip for the specified column.
35781      * @param {Number} col The column index
35782      * @return {String}
35783      */
35784     getColumnTooltip : function(col){
35785             return this.config[col].tooltip;
35786     },
35787     /**
35788      * Sets the tooltip for a column.
35789      * @param {Number} col The column index
35790      * @param {String} tooltip The new tooltip
35791      */
35792     setColumnTooltip : function(col, tooltip){
35793             this.config[col].tooltip = tooltip;
35794     },
35795
35796     /**
35797      * Returns the dataIndex for the specified column.
35798      * @param {Number} col The column index
35799      * @return {Number}
35800      */
35801     getDataIndex : function(col){
35802         return this.config[col].dataIndex;
35803     },
35804
35805     /**
35806      * Sets the dataIndex for a column.
35807      * @param {Number} col The column index
35808      * @param {Number} dataIndex The new dataIndex
35809      */
35810     setDataIndex : function(col, dataIndex){
35811         this.config[col].dataIndex = dataIndex;
35812     },
35813
35814     
35815     
35816     /**
35817      * Returns true if the cell is editable.
35818      * @param {Number} colIndex The column index
35819      * @param {Number} rowIndex The row index - this is nto actually used..?
35820      * @return {Boolean}
35821      */
35822     isCellEditable : function(colIndex, rowIndex){
35823         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35824     },
35825
35826     /**
35827      * Returns the editor defined for the cell/column.
35828      * return false or null to disable editing.
35829      * @param {Number} colIndex The column index
35830      * @param {Number} rowIndex The row index
35831      * @return {Object}
35832      */
35833     getCellEditor : function(colIndex, rowIndex){
35834         return this.config[colIndex].editor;
35835     },
35836
35837     /**
35838      * Sets if a column is editable.
35839      * @param {Number} col The column index
35840      * @param {Boolean} editable True if the column is editable
35841      */
35842     setEditable : function(col, editable){
35843         this.config[col].editable = editable;
35844     },
35845
35846
35847     /**
35848      * Returns true if the column is hidden.
35849      * @param {Number} colIndex The column index
35850      * @return {Boolean}
35851      */
35852     isHidden : function(colIndex){
35853         return this.config[colIndex].hidden;
35854     },
35855
35856
35857     /**
35858      * Returns true if the column width cannot be changed
35859      */
35860     isFixed : function(colIndex){
35861         return this.config[colIndex].fixed;
35862     },
35863
35864     /**
35865      * Returns true if the column can be resized
35866      * @return {Boolean}
35867      */
35868     isResizable : function(colIndex){
35869         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35870     },
35871     /**
35872      * Sets if a column is hidden.
35873      * @param {Number} colIndex The column index
35874      * @param {Boolean} hidden True if the column is hidden
35875      */
35876     setHidden : function(colIndex, hidden){
35877         this.config[colIndex].hidden = hidden;
35878         this.totalWidth = null;
35879         this.fireEvent("hiddenchange", this, colIndex, hidden);
35880     },
35881
35882     /**
35883      * Sets the editor for a column.
35884      * @param {Number} col The column index
35885      * @param {Object} editor The editor object
35886      */
35887     setEditor : function(col, editor){
35888         this.config[col].editor = editor;
35889     }
35890 });
35891
35892 Roo.grid.ColumnModel.defaultRenderer = function(value)
35893 {
35894     if(typeof value == "object") {
35895         return value;
35896     }
35897         if(typeof value == "string" && value.length < 1){
35898             return "&#160;";
35899         }
35900     
35901         return String.format("{0}", value);
35902 };
35903
35904 // Alias for backwards compatibility
35905 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35906 /*
35907  * Based on:
35908  * Ext JS Library 1.1.1
35909  * Copyright(c) 2006-2007, Ext JS, LLC.
35910  *
35911  * Originally Released Under LGPL - original licence link has changed is not relivant.
35912  *
35913  * Fork - LGPL
35914  * <script type="text/javascript">
35915  */
35916
35917 /**
35918  * @class Roo.grid.AbstractSelectionModel
35919  * @extends Roo.util.Observable
35920  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35921  * implemented by descendant classes.  This class should not be directly instantiated.
35922  * @constructor
35923  */
35924 Roo.grid.AbstractSelectionModel = function(){
35925     this.locked = false;
35926     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35927 };
35928
35929 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35930     /** @ignore Called by the grid automatically. Do not call directly. */
35931     init : function(grid){
35932         this.grid = grid;
35933         this.initEvents();
35934     },
35935
35936     /**
35937      * Locks the selections.
35938      */
35939     lock : function(){
35940         this.locked = true;
35941     },
35942
35943     /**
35944      * Unlocks the selections.
35945      */
35946     unlock : function(){
35947         this.locked = false;
35948     },
35949
35950     /**
35951      * Returns true if the selections are locked.
35952      * @return {Boolean}
35953      */
35954     isLocked : function(){
35955         return this.locked;
35956     }
35957 });/*
35958  * Based on:
35959  * Ext JS Library 1.1.1
35960  * Copyright(c) 2006-2007, Ext JS, LLC.
35961  *
35962  * Originally Released Under LGPL - original licence link has changed is not relivant.
35963  *
35964  * Fork - LGPL
35965  * <script type="text/javascript">
35966  */
35967 /**
35968  * @extends Roo.grid.AbstractSelectionModel
35969  * @class Roo.grid.RowSelectionModel
35970  * The default SelectionModel used by {@link Roo.grid.Grid}.
35971  * It supports multiple selections and keyboard selection/navigation. 
35972  * @constructor
35973  * @param {Object} config
35974  */
35975 Roo.grid.RowSelectionModel = function(config){
35976     Roo.apply(this, config);
35977     this.selections = new Roo.util.MixedCollection(false, function(o){
35978         return o.id;
35979     });
35980
35981     this.last = false;
35982     this.lastActive = false;
35983
35984     this.addEvents({
35985         /**
35986              * @event selectionchange
35987              * Fires when the selection changes
35988              * @param {SelectionModel} this
35989              */
35990             "selectionchange" : true,
35991         /**
35992              * @event afterselectionchange
35993              * Fires after the selection changes (eg. by key press or clicking)
35994              * @param {SelectionModel} this
35995              */
35996             "afterselectionchange" : true,
35997         /**
35998              * @event beforerowselect
35999              * Fires when a row is selected being selected, return false to cancel.
36000              * @param {SelectionModel} this
36001              * @param {Number} rowIndex The selected index
36002              * @param {Boolean} keepExisting False if other selections will be cleared
36003              */
36004             "beforerowselect" : true,
36005         /**
36006              * @event rowselect
36007              * Fires when a row is selected.
36008              * @param {SelectionModel} this
36009              * @param {Number} rowIndex The selected index
36010              * @param {Roo.data.Record} r The record
36011              */
36012             "rowselect" : true,
36013         /**
36014              * @event rowdeselect
36015              * Fires when a row is deselected.
36016              * @param {SelectionModel} this
36017              * @param {Number} rowIndex The selected index
36018              */
36019         "rowdeselect" : true
36020     });
36021     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36022     this.locked = false;
36023 };
36024
36025 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36026     /**
36027      * @cfg {Boolean} singleSelect
36028      * True to allow selection of only one row at a time (defaults to false)
36029      */
36030     singleSelect : false,
36031
36032     // private
36033     initEvents : function(){
36034
36035         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36036             this.grid.on("mousedown", this.handleMouseDown, this);
36037         }else{ // allow click to work like normal
36038             this.grid.on("rowclick", this.handleDragableRowClick, this);
36039         }
36040
36041         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36042             "up" : function(e){
36043                 if(!e.shiftKey){
36044                     this.selectPrevious(e.shiftKey);
36045                 }else if(this.last !== false && this.lastActive !== false){
36046                     var last = this.last;
36047                     this.selectRange(this.last,  this.lastActive-1);
36048                     this.grid.getView().focusRow(this.lastActive);
36049                     if(last !== false){
36050                         this.last = last;
36051                     }
36052                 }else{
36053                     this.selectFirstRow();
36054                 }
36055                 this.fireEvent("afterselectionchange", this);
36056             },
36057             "down" : function(e){
36058                 if(!e.shiftKey){
36059                     this.selectNext(e.shiftKey);
36060                 }else if(this.last !== false && this.lastActive !== false){
36061                     var last = this.last;
36062                     this.selectRange(this.last,  this.lastActive+1);
36063                     this.grid.getView().focusRow(this.lastActive);
36064                     if(last !== false){
36065                         this.last = last;
36066                     }
36067                 }else{
36068                     this.selectFirstRow();
36069                 }
36070                 this.fireEvent("afterselectionchange", this);
36071             },
36072             scope: this
36073         });
36074
36075         var view = this.grid.view;
36076         view.on("refresh", this.onRefresh, this);
36077         view.on("rowupdated", this.onRowUpdated, this);
36078         view.on("rowremoved", this.onRemove, this);
36079     },
36080
36081     // private
36082     onRefresh : function(){
36083         var ds = this.grid.dataSource, i, v = this.grid.view;
36084         var s = this.selections;
36085         s.each(function(r){
36086             if((i = ds.indexOfId(r.id)) != -1){
36087                 v.onRowSelect(i);
36088                 s.add(ds.getAt(i)); // updating the selection relate data
36089             }else{
36090                 s.remove(r);
36091             }
36092         });
36093     },
36094
36095     // private
36096     onRemove : function(v, index, r){
36097         this.selections.remove(r);
36098     },
36099
36100     // private
36101     onRowUpdated : function(v, index, r){
36102         if(this.isSelected(r)){
36103             v.onRowSelect(index);
36104         }
36105     },
36106
36107     /**
36108      * Select records.
36109      * @param {Array} records The records to select
36110      * @param {Boolean} keepExisting (optional) True to keep existing selections
36111      */
36112     selectRecords : function(records, keepExisting){
36113         if(!keepExisting){
36114             this.clearSelections();
36115         }
36116         var ds = this.grid.dataSource;
36117         for(var i = 0, len = records.length; i < len; i++){
36118             this.selectRow(ds.indexOf(records[i]), true);
36119         }
36120     },
36121
36122     /**
36123      * Gets the number of selected rows.
36124      * @return {Number}
36125      */
36126     getCount : function(){
36127         return this.selections.length;
36128     },
36129
36130     /**
36131      * Selects the first row in the grid.
36132      */
36133     selectFirstRow : function(){
36134         this.selectRow(0);
36135     },
36136
36137     /**
36138      * Select the last row.
36139      * @param {Boolean} keepExisting (optional) True to keep existing selections
36140      */
36141     selectLastRow : function(keepExisting){
36142         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36143     },
36144
36145     /**
36146      * Selects the row immediately following the last selected row.
36147      * @param {Boolean} keepExisting (optional) True to keep existing selections
36148      */
36149     selectNext : function(keepExisting){
36150         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36151             this.selectRow(this.last+1, keepExisting);
36152             this.grid.getView().focusRow(this.last);
36153         }
36154     },
36155
36156     /**
36157      * Selects the row that precedes the last selected row.
36158      * @param {Boolean} keepExisting (optional) True to keep existing selections
36159      */
36160     selectPrevious : function(keepExisting){
36161         if(this.last){
36162             this.selectRow(this.last-1, keepExisting);
36163             this.grid.getView().focusRow(this.last);
36164         }
36165     },
36166
36167     /**
36168      * Returns the selected records
36169      * @return {Array} Array of selected records
36170      */
36171     getSelections : function(){
36172         return [].concat(this.selections.items);
36173     },
36174
36175     /**
36176      * Returns the first selected record.
36177      * @return {Record}
36178      */
36179     getSelected : function(){
36180         return this.selections.itemAt(0);
36181     },
36182
36183
36184     /**
36185      * Clears all selections.
36186      */
36187     clearSelections : function(fast){
36188         if(this.locked) {
36189             return;
36190         }
36191         if(fast !== true){
36192             var ds = this.grid.dataSource;
36193             var s = this.selections;
36194             s.each(function(r){
36195                 this.deselectRow(ds.indexOfId(r.id));
36196             }, this);
36197             s.clear();
36198         }else{
36199             this.selections.clear();
36200         }
36201         this.last = false;
36202     },
36203
36204
36205     /**
36206      * Selects all rows.
36207      */
36208     selectAll : function(){
36209         if(this.locked) {
36210             return;
36211         }
36212         this.selections.clear();
36213         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36214             this.selectRow(i, true);
36215         }
36216     },
36217
36218     /**
36219      * Returns True if there is a selection.
36220      * @return {Boolean}
36221      */
36222     hasSelection : function(){
36223         return this.selections.length > 0;
36224     },
36225
36226     /**
36227      * Returns True if the specified row is selected.
36228      * @param {Number/Record} record The record or index of the record to check
36229      * @return {Boolean}
36230      */
36231     isSelected : function(index){
36232         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36233         return (r && this.selections.key(r.id) ? true : false);
36234     },
36235
36236     /**
36237      * Returns True if the specified record id is selected.
36238      * @param {String} id The id of record to check
36239      * @return {Boolean}
36240      */
36241     isIdSelected : function(id){
36242         return (this.selections.key(id) ? true : false);
36243     },
36244
36245     // private
36246     handleMouseDown : function(e, t){
36247         var view = this.grid.getView(), rowIndex;
36248         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36249             return;
36250         };
36251         if(e.shiftKey && this.last !== false){
36252             var last = this.last;
36253             this.selectRange(last, rowIndex, e.ctrlKey);
36254             this.last = last; // reset the last
36255             view.focusRow(rowIndex);
36256         }else{
36257             var isSelected = this.isSelected(rowIndex);
36258             if(e.button !== 0 && isSelected){
36259                 view.focusRow(rowIndex);
36260             }else if(e.ctrlKey && isSelected){
36261                 this.deselectRow(rowIndex);
36262             }else if(!isSelected){
36263                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36264                 view.focusRow(rowIndex);
36265             }
36266         }
36267         this.fireEvent("afterselectionchange", this);
36268     },
36269     // private
36270     handleDragableRowClick :  function(grid, rowIndex, e) 
36271     {
36272         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36273             this.selectRow(rowIndex, false);
36274             grid.view.focusRow(rowIndex);
36275              this.fireEvent("afterselectionchange", this);
36276         }
36277     },
36278     
36279     /**
36280      * Selects multiple rows.
36281      * @param {Array} rows Array of the indexes of the row to select
36282      * @param {Boolean} keepExisting (optional) True to keep existing selections
36283      */
36284     selectRows : function(rows, keepExisting){
36285         if(!keepExisting){
36286             this.clearSelections();
36287         }
36288         for(var i = 0, len = rows.length; i < len; i++){
36289             this.selectRow(rows[i], true);
36290         }
36291     },
36292
36293     /**
36294      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36295      * @param {Number} startRow The index of the first row in the range
36296      * @param {Number} endRow The index of the last row in the range
36297      * @param {Boolean} keepExisting (optional) True to retain existing selections
36298      */
36299     selectRange : function(startRow, endRow, keepExisting){
36300         if(this.locked) {
36301             return;
36302         }
36303         if(!keepExisting){
36304             this.clearSelections();
36305         }
36306         if(startRow <= endRow){
36307             for(var i = startRow; i <= endRow; i++){
36308                 this.selectRow(i, true);
36309             }
36310         }else{
36311             for(var i = startRow; i >= endRow; i--){
36312                 this.selectRow(i, true);
36313             }
36314         }
36315     },
36316
36317     /**
36318      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36319      * @param {Number} startRow The index of the first row in the range
36320      * @param {Number} endRow The index of the last row in the range
36321      */
36322     deselectRange : function(startRow, endRow, preventViewNotify){
36323         if(this.locked) {
36324             return;
36325         }
36326         for(var i = startRow; i <= endRow; i++){
36327             this.deselectRow(i, preventViewNotify);
36328         }
36329     },
36330
36331     /**
36332      * Selects a row.
36333      * @param {Number} row The index of the row to select
36334      * @param {Boolean} keepExisting (optional) True to keep existing selections
36335      */
36336     selectRow : function(index, keepExisting, preventViewNotify){
36337         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36338             return;
36339         }
36340         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36341             if(!keepExisting || this.singleSelect){
36342                 this.clearSelections();
36343             }
36344             var r = this.grid.dataSource.getAt(index);
36345             this.selections.add(r);
36346             this.last = this.lastActive = index;
36347             if(!preventViewNotify){
36348                 this.grid.getView().onRowSelect(index);
36349             }
36350             this.fireEvent("rowselect", this, index, r);
36351             this.fireEvent("selectionchange", this);
36352         }
36353     },
36354
36355     /**
36356      * Deselects a row.
36357      * @param {Number} row The index of the row to deselect
36358      */
36359     deselectRow : function(index, preventViewNotify){
36360         if(this.locked) {
36361             return;
36362         }
36363         if(this.last == index){
36364             this.last = false;
36365         }
36366         if(this.lastActive == index){
36367             this.lastActive = false;
36368         }
36369         var r = this.grid.dataSource.getAt(index);
36370         this.selections.remove(r);
36371         if(!preventViewNotify){
36372             this.grid.getView().onRowDeselect(index);
36373         }
36374         this.fireEvent("rowdeselect", this, index);
36375         this.fireEvent("selectionchange", this);
36376     },
36377
36378     // private
36379     restoreLast : function(){
36380         if(this._last){
36381             this.last = this._last;
36382         }
36383     },
36384
36385     // private
36386     acceptsNav : function(row, col, cm){
36387         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36388     },
36389
36390     // private
36391     onEditorKey : function(field, e){
36392         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36393         if(k == e.TAB){
36394             e.stopEvent();
36395             ed.completeEdit();
36396             if(e.shiftKey){
36397                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36398             }else{
36399                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36400             }
36401         }else if(k == e.ENTER && !e.ctrlKey){
36402             e.stopEvent();
36403             ed.completeEdit();
36404             if(e.shiftKey){
36405                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36406             }else{
36407                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36408             }
36409         }else if(k == e.ESC){
36410             ed.cancelEdit();
36411         }
36412         if(newCell){
36413             g.startEditing(newCell[0], newCell[1]);
36414         }
36415     }
36416 });/*
36417  * Based on:
36418  * Ext JS Library 1.1.1
36419  * Copyright(c) 2006-2007, Ext JS, LLC.
36420  *
36421  * Originally Released Under LGPL - original licence link has changed is not relivant.
36422  *
36423  * Fork - LGPL
36424  * <script type="text/javascript">
36425  */
36426 /**
36427  * @class Roo.grid.CellSelectionModel
36428  * @extends Roo.grid.AbstractSelectionModel
36429  * This class provides the basic implementation for cell selection in a grid.
36430  * @constructor
36431  * @param {Object} config The object containing the configuration of this model.
36432  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36433  */
36434 Roo.grid.CellSelectionModel = function(config){
36435     Roo.apply(this, config);
36436
36437     this.selection = null;
36438
36439     this.addEvents({
36440         /**
36441              * @event beforerowselect
36442              * Fires before a cell is selected.
36443              * @param {SelectionModel} this
36444              * @param {Number} rowIndex The selected row index
36445              * @param {Number} colIndex The selected cell index
36446              */
36447             "beforecellselect" : true,
36448         /**
36449              * @event cellselect
36450              * Fires when a cell is selected.
36451              * @param {SelectionModel} this
36452              * @param {Number} rowIndex The selected row index
36453              * @param {Number} colIndex The selected cell index
36454              */
36455             "cellselect" : true,
36456         /**
36457              * @event selectionchange
36458              * Fires when the active selection changes.
36459              * @param {SelectionModel} this
36460              * @param {Object} selection null for no selection or an object (o) with two properties
36461                 <ul>
36462                 <li>o.record: the record object for the row the selection is in</li>
36463                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36464                 </ul>
36465              */
36466             "selectionchange" : true,
36467         /**
36468              * @event tabend
36469              * Fires when the tab (or enter) was pressed on the last editable cell
36470              * You can use this to trigger add new row.
36471              * @param {SelectionModel} this
36472              */
36473             "tabend" : true,
36474          /**
36475              * @event beforeeditnext
36476              * Fires before the next editable sell is made active
36477              * You can use this to skip to another cell or fire the tabend
36478              *    if you set cell to false
36479              * @param {Object} eventdata object : { cell : [ row, col ] } 
36480              */
36481             "beforeeditnext" : true
36482     });
36483     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36484 };
36485
36486 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36487     
36488     enter_is_tab: false,
36489
36490     /** @ignore */
36491     initEvents : function(){
36492         this.grid.on("mousedown", this.handleMouseDown, this);
36493         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36494         var view = this.grid.view;
36495         view.on("refresh", this.onViewChange, this);
36496         view.on("rowupdated", this.onRowUpdated, this);
36497         view.on("beforerowremoved", this.clearSelections, this);
36498         view.on("beforerowsinserted", this.clearSelections, this);
36499         if(this.grid.isEditor){
36500             this.grid.on("beforeedit", this.beforeEdit,  this);
36501         }
36502     },
36503
36504         //private
36505     beforeEdit : function(e){
36506         this.select(e.row, e.column, false, true, e.record);
36507     },
36508
36509         //private
36510     onRowUpdated : function(v, index, r){
36511         if(this.selection && this.selection.record == r){
36512             v.onCellSelect(index, this.selection.cell[1]);
36513         }
36514     },
36515
36516         //private
36517     onViewChange : function(){
36518         this.clearSelections(true);
36519     },
36520
36521         /**
36522          * Returns the currently selected cell,.
36523          * @return {Array} The selected cell (row, column) or null if none selected.
36524          */
36525     getSelectedCell : function(){
36526         return this.selection ? this.selection.cell : null;
36527     },
36528
36529     /**
36530      * Clears all selections.
36531      * @param {Boolean} true to prevent the gridview from being notified about the change.
36532      */
36533     clearSelections : function(preventNotify){
36534         var s = this.selection;
36535         if(s){
36536             if(preventNotify !== true){
36537                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36538             }
36539             this.selection = null;
36540             this.fireEvent("selectionchange", this, null);
36541         }
36542     },
36543
36544     /**
36545      * Returns true if there is a selection.
36546      * @return {Boolean}
36547      */
36548     hasSelection : function(){
36549         return this.selection ? true : false;
36550     },
36551
36552     /** @ignore */
36553     handleMouseDown : function(e, t){
36554         var v = this.grid.getView();
36555         if(this.isLocked()){
36556             return;
36557         };
36558         var row = v.findRowIndex(t);
36559         var cell = v.findCellIndex(t);
36560         if(row !== false && cell !== false){
36561             this.select(row, cell);
36562         }
36563     },
36564
36565     /**
36566      * Selects a cell.
36567      * @param {Number} rowIndex
36568      * @param {Number} collIndex
36569      */
36570     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36571         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36572             this.clearSelections();
36573             r = r || this.grid.dataSource.getAt(rowIndex);
36574             this.selection = {
36575                 record : r,
36576                 cell : [rowIndex, colIndex]
36577             };
36578             if(!preventViewNotify){
36579                 var v = this.grid.getView();
36580                 v.onCellSelect(rowIndex, colIndex);
36581                 if(preventFocus !== true){
36582                     v.focusCell(rowIndex, colIndex);
36583                 }
36584             }
36585             this.fireEvent("cellselect", this, rowIndex, colIndex);
36586             this.fireEvent("selectionchange", this, this.selection);
36587         }
36588     },
36589
36590         //private
36591     isSelectable : function(rowIndex, colIndex, cm){
36592         return !cm.isHidden(colIndex);
36593     },
36594
36595     /** @ignore */
36596     handleKeyDown : function(e){
36597         //Roo.log('Cell Sel Model handleKeyDown');
36598         if(!e.isNavKeyPress()){
36599             return;
36600         }
36601         var g = this.grid, s = this.selection;
36602         if(!s){
36603             e.stopEvent();
36604             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36605             if(cell){
36606                 this.select(cell[0], cell[1]);
36607             }
36608             return;
36609         }
36610         var sm = this;
36611         var walk = function(row, col, step){
36612             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36613         };
36614         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36615         var newCell;
36616
36617       
36618
36619         switch(k){
36620             case e.TAB:
36621                 // handled by onEditorKey
36622                 if (g.isEditor && g.editing) {
36623                     return;
36624                 }
36625                 if(e.shiftKey) {
36626                     newCell = walk(r, c-1, -1);
36627                 } else {
36628                     newCell = walk(r, c+1, 1);
36629                 }
36630                 break;
36631             
36632             case e.DOWN:
36633                newCell = walk(r+1, c, 1);
36634                 break;
36635             
36636             case e.UP:
36637                 newCell = walk(r-1, c, -1);
36638                 break;
36639             
36640             case e.RIGHT:
36641                 newCell = walk(r, c+1, 1);
36642                 break;
36643             
36644             case e.LEFT:
36645                 newCell = walk(r, c-1, -1);
36646                 break;
36647             
36648             case e.ENTER:
36649                 
36650                 if(g.isEditor && !g.editing){
36651                    g.startEditing(r, c);
36652                    e.stopEvent();
36653                    return;
36654                 }
36655                 
36656                 
36657              break;
36658         };
36659         if(newCell){
36660             this.select(newCell[0], newCell[1]);
36661             e.stopEvent();
36662             
36663         }
36664     },
36665
36666     acceptsNav : function(row, col, cm){
36667         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36668     },
36669     /**
36670      * Selects a cell.
36671      * @param {Number} field (not used) - as it's normally used as a listener
36672      * @param {Number} e - event - fake it by using
36673      *
36674      * var e = Roo.EventObjectImpl.prototype;
36675      * e.keyCode = e.TAB
36676      *
36677      * 
36678      */
36679     onEditorKey : function(field, e){
36680         
36681         var k = e.getKey(),
36682             newCell,
36683             g = this.grid,
36684             ed = g.activeEditor,
36685             forward = false;
36686         ///Roo.log('onEditorKey' + k);
36687         
36688         
36689         if (this.enter_is_tab && k == e.ENTER) {
36690             k = e.TAB;
36691         }
36692         
36693         if(k == e.TAB){
36694             if(e.shiftKey){
36695                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36696             }else{
36697                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36698                 forward = true;
36699             }
36700             
36701             e.stopEvent();
36702             
36703         } else if(k == e.ENTER &&  !e.ctrlKey){
36704             ed.completeEdit();
36705             e.stopEvent();
36706             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36707         
36708                 } else if(k == e.ESC){
36709             ed.cancelEdit();
36710         }
36711                 
36712         if (newCell) {
36713             var ecall = { cell : newCell, forward : forward };
36714             this.fireEvent('beforeeditnext', ecall );
36715             newCell = ecall.cell;
36716                         forward = ecall.forward;
36717         }
36718                 
36719         if(newCell){
36720             //Roo.log('next cell after edit');
36721             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36722         } else if (forward) {
36723             // tabbed past last
36724             this.fireEvent.defer(100, this, ['tabend',this]);
36725         }
36726     }
36727 });/*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737  
36738 /**
36739  * @class Roo.grid.EditorGrid
36740  * @extends Roo.grid.Grid
36741  * Class for creating and editable grid.
36742  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36743  * The container MUST have some type of size defined for the grid to fill. The container will be 
36744  * automatically set to position relative if it isn't already.
36745  * @param {Object} dataSource The data model to bind to
36746  * @param {Object} colModel The column model with info about this grid's columns
36747  */
36748 Roo.grid.EditorGrid = function(container, config){
36749     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36750     this.getGridEl().addClass("xedit-grid");
36751
36752     if(!this.selModel){
36753         this.selModel = new Roo.grid.CellSelectionModel();
36754     }
36755
36756     this.activeEditor = null;
36757
36758         this.addEvents({
36759             /**
36760              * @event beforeedit
36761              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36762              * <ul style="padding:5px;padding-left:16px;">
36763              * <li>grid - This grid</li>
36764              * <li>record - The record being edited</li>
36765              * <li>field - The field name being edited</li>
36766              * <li>value - The value for the field being edited.</li>
36767              * <li>row - The grid row index</li>
36768              * <li>column - The grid column index</li>
36769              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36770              * </ul>
36771              * @param {Object} e An edit event (see above for description)
36772              */
36773             "beforeedit" : true,
36774             /**
36775              * @event afteredit
36776              * Fires after a cell is edited. <br />
36777              * <ul style="padding:5px;padding-left:16px;">
36778              * <li>grid - This grid</li>
36779              * <li>record - The record being edited</li>
36780              * <li>field - The field name being edited</li>
36781              * <li>value - The value being set</li>
36782              * <li>originalValue - The original value for the field, before the edit.</li>
36783              * <li>row - The grid row index</li>
36784              * <li>column - The grid column index</li>
36785              * </ul>
36786              * @param {Object} e An edit event (see above for description)
36787              */
36788             "afteredit" : true,
36789             /**
36790              * @event validateedit
36791              * Fires after a cell is edited, but before the value is set in the record. 
36792          * You can use this to modify the value being set in the field, Return false
36793              * to cancel the change. The edit event object has the following properties <br />
36794              * <ul style="padding:5px;padding-left:16px;">
36795          * <li>editor - This editor</li>
36796              * <li>grid - This grid</li>
36797              * <li>record - The record being edited</li>
36798              * <li>field - The field name being edited</li>
36799              * <li>value - The value being set</li>
36800              * <li>originalValue - The original value for the field, before the edit.</li>
36801              * <li>row - The grid row index</li>
36802              * <li>column - The grid column index</li>
36803              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36804              * </ul>
36805              * @param {Object} e An edit event (see above for description)
36806              */
36807             "validateedit" : true
36808         });
36809     this.on("bodyscroll", this.stopEditing,  this);
36810     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36811 };
36812
36813 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36814     /**
36815      * @cfg {Number} clicksToEdit
36816      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36817      */
36818     clicksToEdit: 2,
36819
36820     // private
36821     isEditor : true,
36822     // private
36823     trackMouseOver: false, // causes very odd FF errors
36824
36825     onCellDblClick : function(g, row, col){
36826         this.startEditing(row, col);
36827     },
36828
36829     onEditComplete : function(ed, value, startValue){
36830         this.editing = false;
36831         this.activeEditor = null;
36832         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36833         var r = ed.record;
36834         var field = this.colModel.getDataIndex(ed.col);
36835         var e = {
36836             grid: this,
36837             record: r,
36838             field: field,
36839             originalValue: startValue,
36840             value: value,
36841             row: ed.row,
36842             column: ed.col,
36843             cancel:false,
36844             editor: ed
36845         };
36846         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36847         cell.show();
36848           
36849         if(String(value) !== String(startValue)){
36850             
36851             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36852                 r.set(field, e.value);
36853                 // if we are dealing with a combo box..
36854                 // then we also set the 'name' colum to be the displayField
36855                 if (ed.field.displayField && ed.field.name) {
36856                     r.set(ed.field.name, ed.field.el.dom.value);
36857                 }
36858                 
36859                 delete e.cancel; //?? why!!!
36860                 this.fireEvent("afteredit", e);
36861             }
36862         } else {
36863             this.fireEvent("afteredit", e); // always fire it!
36864         }
36865         this.view.focusCell(ed.row, ed.col);
36866     },
36867
36868     /**
36869      * Starts editing the specified for the specified row/column
36870      * @param {Number} rowIndex
36871      * @param {Number} colIndex
36872      */
36873     startEditing : function(row, col){
36874         this.stopEditing();
36875         if(this.colModel.isCellEditable(col, row)){
36876             this.view.ensureVisible(row, col, true);
36877           
36878             var r = this.dataSource.getAt(row);
36879             var field = this.colModel.getDataIndex(col);
36880             var cell = Roo.get(this.view.getCell(row,col));
36881             var e = {
36882                 grid: this,
36883                 record: r,
36884                 field: field,
36885                 value: r.data[field],
36886                 row: row,
36887                 column: col,
36888                 cancel:false 
36889             };
36890             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36891                 this.editing = true;
36892                 var ed = this.colModel.getCellEditor(col, row);
36893                 
36894                 if (!ed) {
36895                     return;
36896                 }
36897                 if(!ed.rendered){
36898                     ed.render(ed.parentEl || document.body);
36899                 }
36900                 ed.field.reset();
36901                
36902                 cell.hide();
36903                 
36904                 (function(){ // complex but required for focus issues in safari, ie and opera
36905                     ed.row = row;
36906                     ed.col = col;
36907                     ed.record = r;
36908                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36909                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36910                     this.activeEditor = ed;
36911                     var v = r.data[field];
36912                     ed.startEdit(this.view.getCell(row, col), v);
36913                     // combo's with 'displayField and name set
36914                     if (ed.field.displayField && ed.field.name) {
36915                         ed.field.el.dom.value = r.data[ed.field.name];
36916                     }
36917                     
36918                     
36919                 }).defer(50, this);
36920             }
36921         }
36922     },
36923         
36924     /**
36925      * Stops any active editing
36926      */
36927     stopEditing : function(){
36928         if(this.activeEditor){
36929             this.activeEditor.completeEdit();
36930         }
36931         this.activeEditor = null;
36932     },
36933         
36934          /**
36935      * Called to get grid's drag proxy text, by default returns this.ddText.
36936      * @return {String}
36937      */
36938     getDragDropText : function(){
36939         var count = this.selModel.getSelectedCell() ? 1 : 0;
36940         return String.format(this.ddText, count, count == 1 ? '' : 's');
36941     }
36942         
36943 });/*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953
36954 // private - not really -- you end up using it !
36955 // This is a support class used internally by the Grid components
36956
36957 /**
36958  * @class Roo.grid.GridEditor
36959  * @extends Roo.Editor
36960  * Class for creating and editable grid elements.
36961  * @param {Object} config any settings (must include field)
36962  */
36963 Roo.grid.GridEditor = function(field, config){
36964     if (!config && field.field) {
36965         config = field;
36966         field = Roo.factory(config.field, Roo.form);
36967     }
36968     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36969     field.monitorTab = false;
36970 };
36971
36972 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36973     
36974     /**
36975      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36976      */
36977     
36978     alignment: "tl-tl",
36979     autoSize: "width",
36980     hideEl : false,
36981     cls: "x-small-editor x-grid-editor",
36982     shim:false,
36983     shadow:"frame"
36984 });/*
36985  * Based on:
36986  * Ext JS Library 1.1.1
36987  * Copyright(c) 2006-2007, Ext JS, LLC.
36988  *
36989  * Originally Released Under LGPL - original licence link has changed is not relivant.
36990  *
36991  * Fork - LGPL
36992  * <script type="text/javascript">
36993  */
36994   
36995
36996   
36997 Roo.grid.PropertyRecord = Roo.data.Record.create([
36998     {name:'name',type:'string'},  'value'
36999 ]);
37000
37001
37002 Roo.grid.PropertyStore = function(grid, source){
37003     this.grid = grid;
37004     this.store = new Roo.data.Store({
37005         recordType : Roo.grid.PropertyRecord
37006     });
37007     this.store.on('update', this.onUpdate,  this);
37008     if(source){
37009         this.setSource(source);
37010     }
37011     Roo.grid.PropertyStore.superclass.constructor.call(this);
37012 };
37013
37014
37015
37016 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37017     setSource : function(o){
37018         this.source = o;
37019         this.store.removeAll();
37020         var data = [];
37021         for(var k in o){
37022             if(this.isEditableValue(o[k])){
37023                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37024             }
37025         }
37026         this.store.loadRecords({records: data}, {}, true);
37027     },
37028
37029     onUpdate : function(ds, record, type){
37030         if(type == Roo.data.Record.EDIT){
37031             var v = record.data['value'];
37032             var oldValue = record.modified['value'];
37033             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37034                 this.source[record.id] = v;
37035                 record.commit();
37036                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37037             }else{
37038                 record.reject();
37039             }
37040         }
37041     },
37042
37043     getProperty : function(row){
37044        return this.store.getAt(row);
37045     },
37046
37047     isEditableValue: function(val){
37048         if(val && val instanceof Date){
37049             return true;
37050         }else if(typeof val == 'object' || typeof val == 'function'){
37051             return false;
37052         }
37053         return true;
37054     },
37055
37056     setValue : function(prop, value){
37057         this.source[prop] = value;
37058         this.store.getById(prop).set('value', value);
37059     },
37060
37061     getSource : function(){
37062         return this.source;
37063     }
37064 });
37065
37066 Roo.grid.PropertyColumnModel = function(grid, store){
37067     this.grid = grid;
37068     var g = Roo.grid;
37069     g.PropertyColumnModel.superclass.constructor.call(this, [
37070         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37071         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37072     ]);
37073     this.store = store;
37074     this.bselect = Roo.DomHelper.append(document.body, {
37075         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37076             {tag: 'option', value: 'true', html: 'true'},
37077             {tag: 'option', value: 'false', html: 'false'}
37078         ]
37079     });
37080     Roo.id(this.bselect);
37081     var f = Roo.form;
37082     this.editors = {
37083         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37084         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37085         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37086         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37087         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37088     };
37089     this.renderCellDelegate = this.renderCell.createDelegate(this);
37090     this.renderPropDelegate = this.renderProp.createDelegate(this);
37091 };
37092
37093 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37094     
37095     
37096     nameText : 'Name',
37097     valueText : 'Value',
37098     
37099     dateFormat : 'm/j/Y',
37100     
37101     
37102     renderDate : function(dateVal){
37103         return dateVal.dateFormat(this.dateFormat);
37104     },
37105
37106     renderBool : function(bVal){
37107         return bVal ? 'true' : 'false';
37108     },
37109
37110     isCellEditable : function(colIndex, rowIndex){
37111         return colIndex == 1;
37112     },
37113
37114     getRenderer : function(col){
37115         return col == 1 ?
37116             this.renderCellDelegate : this.renderPropDelegate;
37117     },
37118
37119     renderProp : function(v){
37120         return this.getPropertyName(v);
37121     },
37122
37123     renderCell : function(val){
37124         var rv = val;
37125         if(val instanceof Date){
37126             rv = this.renderDate(val);
37127         }else if(typeof val == 'boolean'){
37128             rv = this.renderBool(val);
37129         }
37130         return Roo.util.Format.htmlEncode(rv);
37131     },
37132
37133     getPropertyName : function(name){
37134         var pn = this.grid.propertyNames;
37135         return pn && pn[name] ? pn[name] : name;
37136     },
37137
37138     getCellEditor : function(colIndex, rowIndex){
37139         var p = this.store.getProperty(rowIndex);
37140         var n = p.data['name'], val = p.data['value'];
37141         
37142         if(typeof(this.grid.customEditors[n]) == 'string'){
37143             return this.editors[this.grid.customEditors[n]];
37144         }
37145         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37146             return this.grid.customEditors[n];
37147         }
37148         if(val instanceof Date){
37149             return this.editors['date'];
37150         }else if(typeof val == 'number'){
37151             return this.editors['number'];
37152         }else if(typeof val == 'boolean'){
37153             return this.editors['boolean'];
37154         }else{
37155             return this.editors['string'];
37156         }
37157     }
37158 });
37159
37160 /**
37161  * @class Roo.grid.PropertyGrid
37162  * @extends Roo.grid.EditorGrid
37163  * This class represents the  interface of a component based property grid control.
37164  * <br><br>Usage:<pre><code>
37165  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37166       
37167  });
37168  // set any options
37169  grid.render();
37170  * </code></pre>
37171   
37172  * @constructor
37173  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37174  * The container MUST have some type of size defined for the grid to fill. The container will be
37175  * automatically set to position relative if it isn't already.
37176  * @param {Object} config A config object that sets properties on this grid.
37177  */
37178 Roo.grid.PropertyGrid = function(container, config){
37179     config = config || {};
37180     var store = new Roo.grid.PropertyStore(this);
37181     this.store = store;
37182     var cm = new Roo.grid.PropertyColumnModel(this, store);
37183     store.store.sort('name', 'ASC');
37184     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37185         ds: store.store,
37186         cm: cm,
37187         enableColLock:false,
37188         enableColumnMove:false,
37189         stripeRows:false,
37190         trackMouseOver: false,
37191         clicksToEdit:1
37192     }, config));
37193     this.getGridEl().addClass('x-props-grid');
37194     this.lastEditRow = null;
37195     this.on('columnresize', this.onColumnResize, this);
37196     this.addEvents({
37197          /**
37198              * @event beforepropertychange
37199              * Fires before a property changes (return false to stop?)
37200              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37201              * @param {String} id Record Id
37202              * @param {String} newval New Value
37203          * @param {String} oldval Old Value
37204              */
37205         "beforepropertychange": true,
37206         /**
37207              * @event propertychange
37208              * Fires after a property changes
37209              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37210              * @param {String} id Record Id
37211              * @param {String} newval New Value
37212          * @param {String} oldval Old Value
37213              */
37214         "propertychange": true
37215     });
37216     this.customEditors = this.customEditors || {};
37217 };
37218 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37219     
37220      /**
37221      * @cfg {Object} customEditors map of colnames=> custom editors.
37222      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37223      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37224      * false disables editing of the field.
37225          */
37226     
37227       /**
37228      * @cfg {Object} propertyNames map of property Names to their displayed value
37229          */
37230     
37231     render : function(){
37232         Roo.grid.PropertyGrid.superclass.render.call(this);
37233         this.autoSize.defer(100, this);
37234     },
37235
37236     autoSize : function(){
37237         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37238         if(this.view){
37239             this.view.fitColumns();
37240         }
37241     },
37242
37243     onColumnResize : function(){
37244         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37245         this.autoSize();
37246     },
37247     /**
37248      * Sets the data for the Grid
37249      * accepts a Key => Value object of all the elements avaiable.
37250      * @param {Object} data  to appear in grid.
37251      */
37252     setSource : function(source){
37253         this.store.setSource(source);
37254         //this.autoSize();
37255     },
37256     /**
37257      * Gets all the data from the grid.
37258      * @return {Object} data  data stored in grid
37259      */
37260     getSource : function(){
37261         return this.store.getSource();
37262     }
37263 });/*
37264   
37265  * Licence LGPL
37266  
37267  */
37268  
37269 /**
37270  * @class Roo.grid.Calendar
37271  * @extends Roo.util.Grid
37272  * This class extends the Grid to provide a calendar widget
37273  * <br><br>Usage:<pre><code>
37274  var grid = new Roo.grid.Calendar("my-container-id", {
37275      ds: myDataStore,
37276      cm: myColModel,
37277      selModel: mySelectionModel,
37278      autoSizeColumns: true,
37279      monitorWindowResize: false,
37280      trackMouseOver: true
37281      eventstore : real data store..
37282  });
37283  // set any options
37284  grid.render();
37285   
37286   * @constructor
37287  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37288  * The container MUST have some type of size defined for the grid to fill. The container will be
37289  * automatically set to position relative if it isn't already.
37290  * @param {Object} config A config object that sets properties on this grid.
37291  */
37292 Roo.grid.Calendar = function(container, config){
37293         // initialize the container
37294         this.container = Roo.get(container);
37295         this.container.update("");
37296         this.container.setStyle("overflow", "hidden");
37297     this.container.addClass('x-grid-container');
37298
37299     this.id = this.container.id;
37300
37301     Roo.apply(this, config);
37302     // check and correct shorthanded configs
37303     
37304     var rows = [];
37305     var d =1;
37306     for (var r = 0;r < 6;r++) {
37307         
37308         rows[r]=[];
37309         for (var c =0;c < 7;c++) {
37310             rows[r][c]= '';
37311         }
37312     }
37313     if (this.eventStore) {
37314         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37315         this.eventStore.on('load',this.onLoad, this);
37316         this.eventStore.on('beforeload',this.clearEvents, this);
37317          
37318     }
37319     
37320     this.dataSource = new Roo.data.Store({
37321             proxy: new Roo.data.MemoryProxy(rows),
37322             reader: new Roo.data.ArrayReader({}, [
37323                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37324     });
37325
37326     this.dataSource.load();
37327     this.ds = this.dataSource;
37328     this.ds.xmodule = this.xmodule || false;
37329     
37330     
37331     var cellRender = function(v,x,r)
37332     {
37333         return String.format(
37334             '<div class="fc-day  fc-widget-content"><div>' +
37335                 '<div class="fc-event-container"></div>' +
37336                 '<div class="fc-day-number">{0}</div>'+
37337                 
37338                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37339             '</div></div>', v);
37340     
37341     }
37342     
37343     
37344     this.colModel = new Roo.grid.ColumnModel( [
37345         {
37346             xtype: 'ColumnModel',
37347             xns: Roo.grid,
37348             dataIndex : 'weekday0',
37349             header : 'Sunday',
37350             renderer : cellRender
37351         },
37352         {
37353             xtype: 'ColumnModel',
37354             xns: Roo.grid,
37355             dataIndex : 'weekday1',
37356             header : 'Monday',
37357             renderer : cellRender
37358         },
37359         {
37360             xtype: 'ColumnModel',
37361             xns: Roo.grid,
37362             dataIndex : 'weekday2',
37363             header : 'Tuesday',
37364             renderer : cellRender
37365         },
37366         {
37367             xtype: 'ColumnModel',
37368             xns: Roo.grid,
37369             dataIndex : 'weekday3',
37370             header : 'Wednesday',
37371             renderer : cellRender
37372         },
37373         {
37374             xtype: 'ColumnModel',
37375             xns: Roo.grid,
37376             dataIndex : 'weekday4',
37377             header : 'Thursday',
37378             renderer : cellRender
37379         },
37380         {
37381             xtype: 'ColumnModel',
37382             xns: Roo.grid,
37383             dataIndex : 'weekday5',
37384             header : 'Friday',
37385             renderer : cellRender
37386         },
37387         {
37388             xtype: 'ColumnModel',
37389             xns: Roo.grid,
37390             dataIndex : 'weekday6',
37391             header : 'Saturday',
37392             renderer : cellRender
37393         }
37394     ]);
37395     this.cm = this.colModel;
37396     this.cm.xmodule = this.xmodule || false;
37397  
37398         
37399           
37400     //this.selModel = new Roo.grid.CellSelectionModel();
37401     //this.sm = this.selModel;
37402     //this.selModel.init(this);
37403     
37404     
37405     if(this.width){
37406         this.container.setWidth(this.width);
37407     }
37408
37409     if(this.height){
37410         this.container.setHeight(this.height);
37411     }
37412     /** @private */
37413         this.addEvents({
37414         // raw events
37415         /**
37416          * @event click
37417          * The raw click event for the entire grid.
37418          * @param {Roo.EventObject} e
37419          */
37420         "click" : true,
37421         /**
37422          * @event dblclick
37423          * The raw dblclick event for the entire grid.
37424          * @param {Roo.EventObject} e
37425          */
37426         "dblclick" : true,
37427         /**
37428          * @event contextmenu
37429          * The raw contextmenu event for the entire grid.
37430          * @param {Roo.EventObject} e
37431          */
37432         "contextmenu" : true,
37433         /**
37434          * @event mousedown
37435          * The raw mousedown event for the entire grid.
37436          * @param {Roo.EventObject} e
37437          */
37438         "mousedown" : true,
37439         /**
37440          * @event mouseup
37441          * The raw mouseup event for the entire grid.
37442          * @param {Roo.EventObject} e
37443          */
37444         "mouseup" : true,
37445         /**
37446          * @event mouseover
37447          * The raw mouseover event for the entire grid.
37448          * @param {Roo.EventObject} e
37449          */
37450         "mouseover" : true,
37451         /**
37452          * @event mouseout
37453          * The raw mouseout event for the entire grid.
37454          * @param {Roo.EventObject} e
37455          */
37456         "mouseout" : true,
37457         /**
37458          * @event keypress
37459          * The raw keypress event for the entire grid.
37460          * @param {Roo.EventObject} e
37461          */
37462         "keypress" : true,
37463         /**
37464          * @event keydown
37465          * The raw keydown event for the entire grid.
37466          * @param {Roo.EventObject} e
37467          */
37468         "keydown" : true,
37469
37470         // custom events
37471
37472         /**
37473          * @event cellclick
37474          * Fires when a cell is clicked
37475          * @param {Grid} this
37476          * @param {Number} rowIndex
37477          * @param {Number} columnIndex
37478          * @param {Roo.EventObject} e
37479          */
37480         "cellclick" : true,
37481         /**
37482          * @event celldblclick
37483          * Fires when a cell is double clicked
37484          * @param {Grid} this
37485          * @param {Number} rowIndex
37486          * @param {Number} columnIndex
37487          * @param {Roo.EventObject} e
37488          */
37489         "celldblclick" : true,
37490         /**
37491          * @event rowclick
37492          * Fires when a row is clicked
37493          * @param {Grid} this
37494          * @param {Number} rowIndex
37495          * @param {Roo.EventObject} e
37496          */
37497         "rowclick" : true,
37498         /**
37499          * @event rowdblclick
37500          * Fires when a row is double clicked
37501          * @param {Grid} this
37502          * @param {Number} rowIndex
37503          * @param {Roo.EventObject} e
37504          */
37505         "rowdblclick" : true,
37506         /**
37507          * @event headerclick
37508          * Fires when a header is clicked
37509          * @param {Grid} this
37510          * @param {Number} columnIndex
37511          * @param {Roo.EventObject} e
37512          */
37513         "headerclick" : true,
37514         /**
37515          * @event headerdblclick
37516          * Fires when a header cell is double clicked
37517          * @param {Grid} this
37518          * @param {Number} columnIndex
37519          * @param {Roo.EventObject} e
37520          */
37521         "headerdblclick" : true,
37522         /**
37523          * @event rowcontextmenu
37524          * Fires when a row is right clicked
37525          * @param {Grid} this
37526          * @param {Number} rowIndex
37527          * @param {Roo.EventObject} e
37528          */
37529         "rowcontextmenu" : true,
37530         /**
37531          * @event cellcontextmenu
37532          * Fires when a cell is right clicked
37533          * @param {Grid} this
37534          * @param {Number} rowIndex
37535          * @param {Number} cellIndex
37536          * @param {Roo.EventObject} e
37537          */
37538          "cellcontextmenu" : true,
37539         /**
37540          * @event headercontextmenu
37541          * Fires when a header is right clicked
37542          * @param {Grid} this
37543          * @param {Number} columnIndex
37544          * @param {Roo.EventObject} e
37545          */
37546         "headercontextmenu" : true,
37547         /**
37548          * @event bodyscroll
37549          * Fires when the body element is scrolled
37550          * @param {Number} scrollLeft
37551          * @param {Number} scrollTop
37552          */
37553         "bodyscroll" : true,
37554         /**
37555          * @event columnresize
37556          * Fires when the user resizes a column
37557          * @param {Number} columnIndex
37558          * @param {Number} newSize
37559          */
37560         "columnresize" : true,
37561         /**
37562          * @event columnmove
37563          * Fires when the user moves a column
37564          * @param {Number} oldIndex
37565          * @param {Number} newIndex
37566          */
37567         "columnmove" : true,
37568         /**
37569          * @event startdrag
37570          * Fires when row(s) start being dragged
37571          * @param {Grid} this
37572          * @param {Roo.GridDD} dd The drag drop object
37573          * @param {event} e The raw browser event
37574          */
37575         "startdrag" : true,
37576         /**
37577          * @event enddrag
37578          * Fires when a drag operation is complete
37579          * @param {Grid} this
37580          * @param {Roo.GridDD} dd The drag drop object
37581          * @param {event} e The raw browser event
37582          */
37583         "enddrag" : true,
37584         /**
37585          * @event dragdrop
37586          * Fires when dragged row(s) are dropped on a valid DD target
37587          * @param {Grid} this
37588          * @param {Roo.GridDD} dd The drag drop object
37589          * @param {String} targetId The target drag drop object
37590          * @param {event} e The raw browser event
37591          */
37592         "dragdrop" : true,
37593         /**
37594          * @event dragover
37595          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37596          * @param {Grid} this
37597          * @param {Roo.GridDD} dd The drag drop object
37598          * @param {String} targetId The target drag drop object
37599          * @param {event} e The raw browser event
37600          */
37601         "dragover" : true,
37602         /**
37603          * @event dragenter
37604          *  Fires when the dragged row(s) first cross another DD target while being dragged
37605          * @param {Grid} this
37606          * @param {Roo.GridDD} dd The drag drop object
37607          * @param {String} targetId The target drag drop object
37608          * @param {event} e The raw browser event
37609          */
37610         "dragenter" : true,
37611         /**
37612          * @event dragout
37613          * Fires when the dragged row(s) leave another DD target while being dragged
37614          * @param {Grid} this
37615          * @param {Roo.GridDD} dd The drag drop object
37616          * @param {String} targetId The target drag drop object
37617          * @param {event} e The raw browser event
37618          */
37619         "dragout" : true,
37620         /**
37621          * @event rowclass
37622          * Fires when a row is rendered, so you can change add a style to it.
37623          * @param {GridView} gridview   The grid view
37624          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37625          */
37626         'rowclass' : true,
37627
37628         /**
37629          * @event render
37630          * Fires when the grid is rendered
37631          * @param {Grid} grid
37632          */
37633         'render' : true,
37634             /**
37635              * @event select
37636              * Fires when a date is selected
37637              * @param {DatePicker} this
37638              * @param {Date} date The selected date
37639              */
37640         'select': true,
37641         /**
37642              * @event monthchange
37643              * Fires when the displayed month changes 
37644              * @param {DatePicker} this
37645              * @param {Date} date The selected month
37646              */
37647         'monthchange': true,
37648         /**
37649              * @event evententer
37650              * Fires when mouse over an event
37651              * @param {Calendar} this
37652              * @param {event} Event
37653              */
37654         'evententer': true,
37655         /**
37656              * @event eventleave
37657              * Fires when the mouse leaves an
37658              * @param {Calendar} this
37659              * @param {event}
37660              */
37661         'eventleave': true,
37662         /**
37663              * @event eventclick
37664              * Fires when the mouse click an
37665              * @param {Calendar} this
37666              * @param {event}
37667              */
37668         'eventclick': true,
37669         /**
37670              * @event eventrender
37671              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37672              * @param {Calendar} this
37673              * @param {data} data to be modified
37674              */
37675         'eventrender': true
37676         
37677     });
37678
37679     Roo.grid.Grid.superclass.constructor.call(this);
37680     this.on('render', function() {
37681         this.view.el.addClass('x-grid-cal'); 
37682         
37683         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37684
37685     },this);
37686     
37687     if (!Roo.grid.Calendar.style) {
37688         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37689             
37690             
37691             '.x-grid-cal .x-grid-col' :  {
37692                 height: 'auto !important',
37693                 'vertical-align': 'top'
37694             },
37695             '.x-grid-cal  .fc-event-hori' : {
37696                 height: '14px'
37697             }
37698              
37699             
37700         }, Roo.id());
37701     }
37702
37703     
37704     
37705 };
37706 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37707     /**
37708      * @cfg {Store} eventStore The store that loads events.
37709      */
37710     eventStore : 25,
37711
37712      
37713     activeDate : false,
37714     startDay : 0,
37715     autoWidth : true,
37716     monitorWindowResize : false,
37717
37718     
37719     resizeColumns : function() {
37720         var col = (this.view.el.getWidth() / 7) - 3;
37721         // loop through cols, and setWidth
37722         for(var i =0 ; i < 7 ; i++){
37723             this.cm.setColumnWidth(i, col);
37724         }
37725     },
37726      setDate :function(date) {
37727         
37728         Roo.log('setDate?');
37729         
37730         this.resizeColumns();
37731         var vd = this.activeDate;
37732         this.activeDate = date;
37733 //        if(vd && this.el){
37734 //            var t = date.getTime();
37735 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37736 //                Roo.log('using add remove');
37737 //                
37738 //                this.fireEvent('monthchange', this, date);
37739 //                
37740 //                this.cells.removeClass("fc-state-highlight");
37741 //                this.cells.each(function(c){
37742 //                   if(c.dateValue == t){
37743 //                       c.addClass("fc-state-highlight");
37744 //                       setTimeout(function(){
37745 //                            try{c.dom.firstChild.focus();}catch(e){}
37746 //                       }, 50);
37747 //                       return false;
37748 //                   }
37749 //                   return true;
37750 //                });
37751 //                return;
37752 //            }
37753 //        }
37754         
37755         var days = date.getDaysInMonth();
37756         
37757         var firstOfMonth = date.getFirstDateOfMonth();
37758         var startingPos = firstOfMonth.getDay()-this.startDay;
37759         
37760         if(startingPos < this.startDay){
37761             startingPos += 7;
37762         }
37763         
37764         var pm = date.add(Date.MONTH, -1);
37765         var prevStart = pm.getDaysInMonth()-startingPos;
37766 //        
37767         
37768         
37769         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37770         
37771         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37772         //this.cells.addClassOnOver('fc-state-hover');
37773         
37774         var cells = this.cells.elements;
37775         var textEls = this.textNodes;
37776         
37777         //Roo.each(cells, function(cell){
37778         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37779         //});
37780         
37781         days += startingPos;
37782
37783         // convert everything to numbers so it's fast
37784         var day = 86400000;
37785         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37786         //Roo.log(d);
37787         //Roo.log(pm);
37788         //Roo.log(prevStart);
37789         
37790         var today = new Date().clearTime().getTime();
37791         var sel = date.clearTime().getTime();
37792         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37793         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37794         var ddMatch = this.disabledDatesRE;
37795         var ddText = this.disabledDatesText;
37796         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37797         var ddaysText = this.disabledDaysText;
37798         var format = this.format;
37799         
37800         var setCellClass = function(cal, cell){
37801             
37802             //Roo.log('set Cell Class');
37803             cell.title = "";
37804             var t = d.getTime();
37805             
37806             //Roo.log(d);
37807             
37808             
37809             cell.dateValue = t;
37810             if(t == today){
37811                 cell.className += " fc-today";
37812                 cell.className += " fc-state-highlight";
37813                 cell.title = cal.todayText;
37814             }
37815             if(t == sel){
37816                 // disable highlight in other month..
37817                 cell.className += " fc-state-highlight";
37818                 
37819             }
37820             // disabling
37821             if(t < min) {
37822                 //cell.className = " fc-state-disabled";
37823                 cell.title = cal.minText;
37824                 return;
37825             }
37826             if(t > max) {
37827                 //cell.className = " fc-state-disabled";
37828                 cell.title = cal.maxText;
37829                 return;
37830             }
37831             if(ddays){
37832                 if(ddays.indexOf(d.getDay()) != -1){
37833                     // cell.title = ddaysText;
37834                    // cell.className = " fc-state-disabled";
37835                 }
37836             }
37837             if(ddMatch && format){
37838                 var fvalue = d.dateFormat(format);
37839                 if(ddMatch.test(fvalue)){
37840                     cell.title = ddText.replace("%0", fvalue);
37841                    cell.className = " fc-state-disabled";
37842                 }
37843             }
37844             
37845             if (!cell.initialClassName) {
37846                 cell.initialClassName = cell.dom.className;
37847             }
37848             
37849             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37850         };
37851
37852         var i = 0;
37853         
37854         for(; i < startingPos; i++) {
37855             cells[i].dayName =  (++prevStart);
37856             Roo.log(textEls[i]);
37857             d.setDate(d.getDate()+1);
37858             
37859             //cells[i].className = "fc-past fc-other-month";
37860             setCellClass(this, cells[i]);
37861         }
37862         
37863         var intDay = 0;
37864         
37865         for(; i < days; i++){
37866             intDay = i - startingPos + 1;
37867             cells[i].dayName =  (intDay);
37868             d.setDate(d.getDate()+1);
37869             
37870             cells[i].className = ''; // "x-date-active";
37871             setCellClass(this, cells[i]);
37872         }
37873         var extraDays = 0;
37874         
37875         for(; i < 42; i++) {
37876             //textEls[i].innerHTML = (++extraDays);
37877             
37878             d.setDate(d.getDate()+1);
37879             cells[i].dayName = (++extraDays);
37880             cells[i].className = "fc-future fc-other-month";
37881             setCellClass(this, cells[i]);
37882         }
37883         
37884         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37885         
37886         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37887         
37888         // this will cause all the cells to mis
37889         var rows= [];
37890         var i =0;
37891         for (var r = 0;r < 6;r++) {
37892             for (var c =0;c < 7;c++) {
37893                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37894             }    
37895         }
37896         
37897         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37898         for(i=0;i<cells.length;i++) {
37899             
37900             this.cells.elements[i].dayName = cells[i].dayName ;
37901             this.cells.elements[i].className = cells[i].className;
37902             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37903             this.cells.elements[i].title = cells[i].title ;
37904             this.cells.elements[i].dateValue = cells[i].dateValue ;
37905         }
37906         
37907         
37908         
37909         
37910         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37911         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37912         
37913         ////if(totalRows != 6){
37914             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37915            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37916        // }
37917         
37918         this.fireEvent('monthchange', this, date);
37919         
37920         
37921     },
37922  /**
37923      * Returns the grid's SelectionModel.
37924      * @return {SelectionModel}
37925      */
37926     getSelectionModel : function(){
37927         if(!this.selModel){
37928             this.selModel = new Roo.grid.CellSelectionModel();
37929         }
37930         return this.selModel;
37931     },
37932
37933     load: function() {
37934         this.eventStore.load()
37935         
37936         
37937         
37938     },
37939     
37940     findCell : function(dt) {
37941         dt = dt.clearTime().getTime();
37942         var ret = false;
37943         this.cells.each(function(c){
37944             //Roo.log("check " +c.dateValue + '?=' + dt);
37945             if(c.dateValue == dt){
37946                 ret = c;
37947                 return false;
37948             }
37949             return true;
37950         });
37951         
37952         return ret;
37953     },
37954     
37955     findCells : function(rec) {
37956         var s = rec.data.start_dt.clone().clearTime().getTime();
37957        // Roo.log(s);
37958         var e= rec.data.end_dt.clone().clearTime().getTime();
37959        // Roo.log(e);
37960         var ret = [];
37961         this.cells.each(function(c){
37962              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37963             
37964             if(c.dateValue > e){
37965                 return ;
37966             }
37967             if(c.dateValue < s){
37968                 return ;
37969             }
37970             ret.push(c);
37971         });
37972         
37973         return ret;    
37974     },
37975     
37976     findBestRow: function(cells)
37977     {
37978         var ret = 0;
37979         
37980         for (var i =0 ; i < cells.length;i++) {
37981             ret  = Math.max(cells[i].rows || 0,ret);
37982         }
37983         return ret;
37984         
37985     },
37986     
37987     
37988     addItem : function(rec)
37989     {
37990         // look for vertical location slot in
37991         var cells = this.findCells(rec);
37992         
37993         rec.row = this.findBestRow(cells);
37994         
37995         // work out the location.
37996         
37997         var crow = false;
37998         var rows = [];
37999         for(var i =0; i < cells.length; i++) {
38000             if (!crow) {
38001                 crow = {
38002                     start : cells[i],
38003                     end :  cells[i]
38004                 };
38005                 continue;
38006             }
38007             if (crow.start.getY() == cells[i].getY()) {
38008                 // on same row.
38009                 crow.end = cells[i];
38010                 continue;
38011             }
38012             // different row.
38013             rows.push(crow);
38014             crow = {
38015                 start: cells[i],
38016                 end : cells[i]
38017             };
38018             
38019         }
38020         
38021         rows.push(crow);
38022         rec.els = [];
38023         rec.rows = rows;
38024         rec.cells = cells;
38025         for (var i = 0; i < cells.length;i++) {
38026             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38027             
38028         }
38029         
38030         
38031     },
38032     
38033     clearEvents: function() {
38034         
38035         if (!this.eventStore.getCount()) {
38036             return;
38037         }
38038         // reset number of rows in cells.
38039         Roo.each(this.cells.elements, function(c){
38040             c.rows = 0;
38041         });
38042         
38043         this.eventStore.each(function(e) {
38044             this.clearEvent(e);
38045         },this);
38046         
38047     },
38048     
38049     clearEvent : function(ev)
38050     {
38051         if (ev.els) {
38052             Roo.each(ev.els, function(el) {
38053                 el.un('mouseenter' ,this.onEventEnter, this);
38054                 el.un('mouseleave' ,this.onEventLeave, this);
38055                 el.remove();
38056             },this);
38057             ev.els = [];
38058         }
38059     },
38060     
38061     
38062     renderEvent : function(ev,ctr) {
38063         if (!ctr) {
38064              ctr = this.view.el.select('.fc-event-container',true).first();
38065         }
38066         
38067          
38068         this.clearEvent(ev);
38069             //code
38070        
38071         
38072         
38073         ev.els = [];
38074         var cells = ev.cells;
38075         var rows = ev.rows;
38076         this.fireEvent('eventrender', this, ev);
38077         
38078         for(var i =0; i < rows.length; i++) {
38079             
38080             cls = '';
38081             if (i == 0) {
38082                 cls += ' fc-event-start';
38083             }
38084             if ((i+1) == rows.length) {
38085                 cls += ' fc-event-end';
38086             }
38087             
38088             //Roo.log(ev.data);
38089             // how many rows should it span..
38090             var cg = this.eventTmpl.append(ctr,Roo.apply({
38091                 fccls : cls
38092                 
38093             }, ev.data) , true);
38094             
38095             
38096             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38097             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38098             cg.on('click', this.onEventClick, this, ev);
38099             
38100             ev.els.push(cg);
38101             
38102             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38103             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38104             //Roo.log(cg);
38105              
38106             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38107             cg.setWidth(ebox.right - sbox.x -2);
38108         }
38109     },
38110     
38111     renderEvents: function()
38112     {   
38113         // first make sure there is enough space..
38114         
38115         if (!this.eventTmpl) {
38116             this.eventTmpl = new Roo.Template(
38117                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38118                     '<div class="fc-event-inner">' +
38119                         '<span class="fc-event-time">{time}</span>' +
38120                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38121                     '</div>' +
38122                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38123                 '</div>'
38124             );
38125                 
38126         }
38127                
38128         
38129         
38130         this.cells.each(function(c) {
38131             //Roo.log(c.select('.fc-day-content div',true).first());
38132             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38133         });
38134         
38135         var ctr = this.view.el.select('.fc-event-container',true).first();
38136         
38137         var cls;
38138         this.eventStore.each(function(ev){
38139             
38140             this.renderEvent(ev);
38141              
38142              
38143         }, this);
38144         this.view.layout();
38145         
38146     },
38147     
38148     onEventEnter: function (e, el,event,d) {
38149         this.fireEvent('evententer', this, el, event);
38150     },
38151     
38152     onEventLeave: function (e, el,event,d) {
38153         this.fireEvent('eventleave', this, el, event);
38154     },
38155     
38156     onEventClick: function (e, el,event,d) {
38157         this.fireEvent('eventclick', this, el, event);
38158     },
38159     
38160     onMonthChange: function () {
38161         this.store.load();
38162     },
38163     
38164     onLoad: function () {
38165         
38166         //Roo.log('calendar onload');
38167 //         
38168         if(this.eventStore.getCount() > 0){
38169             
38170            
38171             
38172             this.eventStore.each(function(d){
38173                 
38174                 
38175                 // FIXME..
38176                 var add =   d.data;
38177                 if (typeof(add.end_dt) == 'undefined')  {
38178                     Roo.log("Missing End time in calendar data: ");
38179                     Roo.log(d);
38180                     return;
38181                 }
38182                 if (typeof(add.start_dt) == 'undefined')  {
38183                     Roo.log("Missing Start time in calendar data: ");
38184                     Roo.log(d);
38185                     return;
38186                 }
38187                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38188                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38189                 add.id = add.id || d.id;
38190                 add.title = add.title || '??';
38191                 
38192                 this.addItem(d);
38193                 
38194              
38195             },this);
38196         }
38197         
38198         this.renderEvents();
38199     }
38200     
38201
38202 });
38203 /*
38204  grid : {
38205                 xtype: 'Grid',
38206                 xns: Roo.grid,
38207                 listeners : {
38208                     render : function ()
38209                     {
38210                         _this.grid = this;
38211                         
38212                         if (!this.view.el.hasClass('course-timesheet')) {
38213                             this.view.el.addClass('course-timesheet');
38214                         }
38215                         if (this.tsStyle) {
38216                             this.ds.load({});
38217                             return; 
38218                         }
38219                         Roo.log('width');
38220                         Roo.log(_this.grid.view.el.getWidth());
38221                         
38222                         
38223                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38224                             '.course-timesheet .x-grid-row' : {
38225                                 height: '80px'
38226                             },
38227                             '.x-grid-row td' : {
38228                                 'vertical-align' : 0
38229                             },
38230                             '.course-edit-link' : {
38231                                 'color' : 'blue',
38232                                 'text-overflow' : 'ellipsis',
38233                                 'overflow' : 'hidden',
38234                                 'white-space' : 'nowrap',
38235                                 'cursor' : 'pointer'
38236                             },
38237                             '.sub-link' : {
38238                                 'color' : 'green'
38239                             },
38240                             '.de-act-sup-link' : {
38241                                 'color' : 'purple',
38242                                 'text-decoration' : 'line-through'
38243                             },
38244                             '.de-act-link' : {
38245                                 'color' : 'red',
38246                                 'text-decoration' : 'line-through'
38247                             },
38248                             '.course-timesheet .course-highlight' : {
38249                                 'border-top-style': 'dashed !important',
38250                                 'border-bottom-bottom': 'dashed !important'
38251                             },
38252                             '.course-timesheet .course-item' : {
38253                                 'font-family'   : 'tahoma, arial, helvetica',
38254                                 'font-size'     : '11px',
38255                                 'overflow'      : 'hidden',
38256                                 'padding-left'  : '10px',
38257                                 'padding-right' : '10px',
38258                                 'padding-top' : '10px' 
38259                             }
38260                             
38261                         }, Roo.id());
38262                                 this.ds.load({});
38263                     }
38264                 },
38265                 autoWidth : true,
38266                 monitorWindowResize : false,
38267                 cellrenderer : function(v,x,r)
38268                 {
38269                     return v;
38270                 },
38271                 sm : {
38272                     xtype: 'CellSelectionModel',
38273                     xns: Roo.grid
38274                 },
38275                 dataSource : {
38276                     xtype: 'Store',
38277                     xns: Roo.data,
38278                     listeners : {
38279                         beforeload : function (_self, options)
38280                         {
38281                             options.params = options.params || {};
38282                             options.params._month = _this.monthField.getValue();
38283                             options.params.limit = 9999;
38284                             options.params['sort'] = 'when_dt';    
38285                             options.params['dir'] = 'ASC';    
38286                             this.proxy.loadResponse = this.loadResponse;
38287                             Roo.log("load?");
38288                             //this.addColumns();
38289                         },
38290                         load : function (_self, records, options)
38291                         {
38292                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38293                                 // if you click on the translation.. you can edit it...
38294                                 var el = Roo.get(this);
38295                                 var id = el.dom.getAttribute('data-id');
38296                                 var d = el.dom.getAttribute('data-date');
38297                                 var t = el.dom.getAttribute('data-time');
38298                                 //var id = this.child('span').dom.textContent;
38299                                 
38300                                 //Roo.log(this);
38301                                 Pman.Dialog.CourseCalendar.show({
38302                                     id : id,
38303                                     when_d : d,
38304                                     when_t : t,
38305                                     productitem_active : id ? 1 : 0
38306                                 }, function() {
38307                                     _this.grid.ds.load({});
38308                                 });
38309                            
38310                            });
38311                            
38312                            _this.panel.fireEvent('resize', [ '', '' ]);
38313                         }
38314                     },
38315                     loadResponse : function(o, success, response){
38316                             // this is overridden on before load..
38317                             
38318                             Roo.log("our code?");       
38319                             //Roo.log(success);
38320                             //Roo.log(response)
38321                             delete this.activeRequest;
38322                             if(!success){
38323                                 this.fireEvent("loadexception", this, o, response);
38324                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38325                                 return;
38326                             }
38327                             var result;
38328                             try {
38329                                 result = o.reader.read(response);
38330                             }catch(e){
38331                                 Roo.log("load exception?");
38332                                 this.fireEvent("loadexception", this, o, response, e);
38333                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38334                                 return;
38335                             }
38336                             Roo.log("ready...");        
38337                             // loop through result.records;
38338                             // and set this.tdate[date] = [] << array of records..
38339                             _this.tdata  = {};
38340                             Roo.each(result.records, function(r){
38341                                 //Roo.log(r.data);
38342                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38343                                     _this.tdata[r.data.when_dt.format('j')] = [];
38344                                 }
38345                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38346                             });
38347                             
38348                             //Roo.log(_this.tdata);
38349                             
38350                             result.records = [];
38351                             result.totalRecords = 6;
38352                     
38353                             // let's generate some duumy records for the rows.
38354                             //var st = _this.dateField.getValue();
38355                             
38356                             // work out monday..
38357                             //st = st.add(Date.DAY, -1 * st.format('w'));
38358                             
38359                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38360                             
38361                             var firstOfMonth = date.getFirstDayOfMonth();
38362                             var days = date.getDaysInMonth();
38363                             var d = 1;
38364                             var firstAdded = false;
38365                             for (var i = 0; i < result.totalRecords ; i++) {
38366                                 //var d= st.add(Date.DAY, i);
38367                                 var row = {};
38368                                 var added = 0;
38369                                 for(var w = 0 ; w < 7 ; w++){
38370                                     if(!firstAdded && firstOfMonth != w){
38371                                         continue;
38372                                     }
38373                                     if(d > days){
38374                                         continue;
38375                                     }
38376                                     firstAdded = true;
38377                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38378                                     row['weekday'+w] = String.format(
38379                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38380                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38381                                                     d,
38382                                                     date.format('Y-m-')+dd
38383                                                 );
38384                                     added++;
38385                                     if(typeof(_this.tdata[d]) != 'undefined'){
38386                                         Roo.each(_this.tdata[d], function(r){
38387                                             var is_sub = '';
38388                                             var deactive = '';
38389                                             var id = r.id;
38390                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38391                                             if(r.parent_id*1>0){
38392                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38393                                                 id = r.parent_id;
38394                                             }
38395                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38396                                                 deactive = 'de-act-link';
38397                                             }
38398                                             
38399                                             row['weekday'+w] += String.format(
38400                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38401                                                     id, //0
38402                                                     r.product_id_name, //1
38403                                                     r.when_dt.format('h:ia'), //2
38404                                                     is_sub, //3
38405                                                     deactive, //4
38406                                                     desc // 5
38407                                             );
38408                                         });
38409                                     }
38410                                     d++;
38411                                 }
38412                                 
38413                                 // only do this if something added..
38414                                 if(added > 0){ 
38415                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38416                                 }
38417                                 
38418                                 
38419                                 // push it twice. (second one with an hour..
38420                                 
38421                             }
38422                             //Roo.log(result);
38423                             this.fireEvent("load", this, o, o.request.arg);
38424                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38425                         },
38426                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38427                     proxy : {
38428                         xtype: 'HttpProxy',
38429                         xns: Roo.data,
38430                         method : 'GET',
38431                         url : baseURL + '/Roo/Shop_course.php'
38432                     },
38433                     reader : {
38434                         xtype: 'JsonReader',
38435                         xns: Roo.data,
38436                         id : 'id',
38437                         fields : [
38438                             {
38439                                 'name': 'id',
38440                                 'type': 'int'
38441                             },
38442                             {
38443                                 'name': 'when_dt',
38444                                 'type': 'string'
38445                             },
38446                             {
38447                                 'name': 'end_dt',
38448                                 'type': 'string'
38449                             },
38450                             {
38451                                 'name': 'parent_id',
38452                                 'type': 'int'
38453                             },
38454                             {
38455                                 'name': 'product_id',
38456                                 'type': 'int'
38457                             },
38458                             {
38459                                 'name': 'productitem_id',
38460                                 'type': 'int'
38461                             },
38462                             {
38463                                 'name': 'guid',
38464                                 'type': 'int'
38465                             }
38466                         ]
38467                     }
38468                 },
38469                 toolbar : {
38470                     xtype: 'Toolbar',
38471                     xns: Roo,
38472                     items : [
38473                         {
38474                             xtype: 'Button',
38475                             xns: Roo.Toolbar,
38476                             listeners : {
38477                                 click : function (_self, e)
38478                                 {
38479                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38480                                     sd.setMonth(sd.getMonth()-1);
38481                                     _this.monthField.setValue(sd.format('Y-m-d'));
38482                                     _this.grid.ds.load({});
38483                                 }
38484                             },
38485                             text : "Back"
38486                         },
38487                         {
38488                             xtype: 'Separator',
38489                             xns: Roo.Toolbar
38490                         },
38491                         {
38492                             xtype: 'MonthField',
38493                             xns: Roo.form,
38494                             listeners : {
38495                                 render : function (_self)
38496                                 {
38497                                     _this.monthField = _self;
38498                                    // _this.monthField.set  today
38499                                 },
38500                                 select : function (combo, date)
38501                                 {
38502                                     _this.grid.ds.load({});
38503                                 }
38504                             },
38505                             value : (function() { return new Date(); })()
38506                         },
38507                         {
38508                             xtype: 'Separator',
38509                             xns: Roo.Toolbar
38510                         },
38511                         {
38512                             xtype: 'TextItem',
38513                             xns: Roo.Toolbar,
38514                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38515                         },
38516                         {
38517                             xtype: 'Fill',
38518                             xns: Roo.Toolbar
38519                         },
38520                         {
38521                             xtype: 'Button',
38522                             xns: Roo.Toolbar,
38523                             listeners : {
38524                                 click : function (_self, e)
38525                                 {
38526                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38527                                     sd.setMonth(sd.getMonth()+1);
38528                                     _this.monthField.setValue(sd.format('Y-m-d'));
38529                                     _this.grid.ds.load({});
38530                                 }
38531                             },
38532                             text : "Next"
38533                         }
38534                     ]
38535                 },
38536                  
38537             }
38538         };
38539         
38540         *//*
38541  * Based on:
38542  * Ext JS Library 1.1.1
38543  * Copyright(c) 2006-2007, Ext JS, LLC.
38544  *
38545  * Originally Released Under LGPL - original licence link has changed is not relivant.
38546  *
38547  * Fork - LGPL
38548  * <script type="text/javascript">
38549  */
38550  
38551 /**
38552  * @class Roo.LoadMask
38553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38556  * element's UpdateManager load indicator and will be destroyed after the initial load.
38557  * @constructor
38558  * Create a new LoadMask
38559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38560  * @param {Object} config The config object
38561  */
38562 Roo.LoadMask = function(el, config){
38563     this.el = Roo.get(el);
38564     Roo.apply(this, config);
38565     if(this.store){
38566         this.store.on('beforeload', this.onBeforeLoad, this);
38567         this.store.on('load', this.onLoad, this);
38568         this.store.on('loadexception', this.onLoadException, this);
38569         this.removeMask = false;
38570     }else{
38571         var um = this.el.getUpdateManager();
38572         um.showLoadIndicator = false; // disable the default indicator
38573         um.on('beforeupdate', this.onBeforeLoad, this);
38574         um.on('update', this.onLoad, this);
38575         um.on('failure', this.onLoad, this);
38576         this.removeMask = true;
38577     }
38578 };
38579
38580 Roo.LoadMask.prototype = {
38581     /**
38582      * @cfg {Boolean} removeMask
38583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38585      */
38586     /**
38587      * @cfg {String} msg
38588      * The text to display in a centered loading message box (defaults to 'Loading...')
38589      */
38590     msg : 'Loading...',
38591     /**
38592      * @cfg {String} msgCls
38593      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38594      */
38595     msgCls : 'x-mask-loading',
38596
38597     /**
38598      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38599      * @type Boolean
38600      */
38601     disabled: false,
38602
38603     /**
38604      * Disables the mask to prevent it from being displayed
38605      */
38606     disable : function(){
38607        this.disabled = true;
38608     },
38609
38610     /**
38611      * Enables the mask so that it can be displayed
38612      */
38613     enable : function(){
38614         this.disabled = false;
38615     },
38616     
38617     onLoadException : function()
38618     {
38619         Roo.log(arguments);
38620         
38621         if (typeof(arguments[3]) != 'undefined') {
38622             Roo.MessageBox.alert("Error loading",arguments[3]);
38623         } 
38624         /*
38625         try {
38626             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38627                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38628             }   
38629         } catch(e) {
38630             
38631         }
38632         */
38633     
38634         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38635     },
38636     // private
38637     onLoad : function()
38638     {
38639         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38640     },
38641
38642     // private
38643     onBeforeLoad : function(){
38644         if(!this.disabled){
38645             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38646         }
38647     },
38648
38649     // private
38650     destroy : function(){
38651         if(this.store){
38652             this.store.un('beforeload', this.onBeforeLoad, this);
38653             this.store.un('load', this.onLoad, this);
38654             this.store.un('loadexception', this.onLoadException, this);
38655         }else{
38656             var um = this.el.getUpdateManager();
38657             um.un('beforeupdate', this.onBeforeLoad, this);
38658             um.un('update', this.onLoad, this);
38659             um.un('failure', this.onLoad, this);
38660         }
38661     }
38662 };/*
38663  * Based on:
38664  * Ext JS Library 1.1.1
38665  * Copyright(c) 2006-2007, Ext JS, LLC.
38666  *
38667  * Originally Released Under LGPL - original licence link has changed is not relivant.
38668  *
38669  * Fork - LGPL
38670  * <script type="text/javascript">
38671  */
38672
38673
38674 /**
38675  * @class Roo.XTemplate
38676  * @extends Roo.Template
38677  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38678 <pre><code>
38679 var t = new Roo.XTemplate(
38680         '&lt;select name="{name}"&gt;',
38681                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38682         '&lt;/select&gt;'
38683 );
38684  
38685 // then append, applying the master template values
38686  </code></pre>
38687  *
38688  * Supported features:
38689  *
38690  *  Tags:
38691
38692 <pre><code>
38693       {a_variable} - output encoded.
38694       {a_variable.format:("Y-m-d")} - call a method on the variable
38695       {a_variable:raw} - unencoded output
38696       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38697       {a_variable:this.method_on_template(...)} - call a method on the template object.
38698  
38699 </code></pre>
38700  *  The tpl tag:
38701 <pre><code>
38702         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38703         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38704         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38705         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38706   
38707         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38708         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38709 </code></pre>
38710  *      
38711  */
38712 Roo.XTemplate = function()
38713 {
38714     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38715     if (this.html) {
38716         this.compile();
38717     }
38718 };
38719
38720
38721 Roo.extend(Roo.XTemplate, Roo.Template, {
38722
38723     /**
38724      * The various sub templates
38725      */
38726     tpls : false,
38727     /**
38728      *
38729      * basic tag replacing syntax
38730      * WORD:WORD()
38731      *
38732      * // you can fake an object call by doing this
38733      *  x.t:(test,tesT) 
38734      * 
38735      */
38736     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38737
38738     /**
38739      * compile the template
38740      *
38741      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38742      *
38743      */
38744     compile: function()
38745     {
38746         var s = this.html;
38747      
38748         s = ['<tpl>', s, '</tpl>'].join('');
38749     
38750         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38751             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38752             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38753             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38754             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38755             m,
38756             id     = 0,
38757             tpls   = [];
38758     
38759         while(true == !!(m = s.match(re))){
38760             var forMatch   = m[0].match(nameRe),
38761                 ifMatch   = m[0].match(ifRe),
38762                 execMatch   = m[0].match(execRe),
38763                 namedMatch   = m[0].match(namedRe),
38764                 
38765                 exp  = null, 
38766                 fn   = null,
38767                 exec = null,
38768                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38769                 
38770             if (ifMatch) {
38771                 // if - puts fn into test..
38772                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38773                 if(exp){
38774                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38775                 }
38776             }
38777             
38778             if (execMatch) {
38779                 // exec - calls a function... returns empty if true is  returned.
38780                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38781                 if(exp){
38782                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38783                 }
38784             }
38785             
38786             
38787             if (name) {
38788                 // for = 
38789                 switch(name){
38790                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38791                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38792                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38793                 }
38794             }
38795             var uid = namedMatch ? namedMatch[1] : id;
38796             
38797             
38798             tpls.push({
38799                 id:     namedMatch ? namedMatch[1] : id,
38800                 target: name,
38801                 exec:   exec,
38802                 test:   fn,
38803                 body:   m[1] || ''
38804             });
38805             if (namedMatch) {
38806                 s = s.replace(m[0], '');
38807             } else { 
38808                 s = s.replace(m[0], '{xtpl'+ id + '}');
38809             }
38810             ++id;
38811         }
38812         this.tpls = [];
38813         for(var i = tpls.length-1; i >= 0; --i){
38814             this.compileTpl(tpls[i]);
38815             this.tpls[tpls[i].id] = tpls[i];
38816         }
38817         this.master = tpls[tpls.length-1];
38818         return this;
38819     },
38820     /**
38821      * same as applyTemplate, except it's done to one of the subTemplates
38822      * when using named templates, you can do:
38823      *
38824      * var str = pl.applySubTemplate('your-name', values);
38825      *
38826      * 
38827      * @param {Number} id of the template
38828      * @param {Object} values to apply to template
38829      * @param {Object} parent (normaly the instance of this object)
38830      */
38831     applySubTemplate : function(id, values, parent)
38832     {
38833         
38834         
38835         var t = this.tpls[id];
38836         
38837         
38838         try { 
38839             if(t.test && !t.test.call(this, values, parent)){
38840                 return '';
38841             }
38842         } catch(e) {
38843             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38844             Roo.log(e.toString());
38845             Roo.log(t.test);
38846             return ''
38847         }
38848         try { 
38849             
38850             if(t.exec && t.exec.call(this, values, parent)){
38851                 return '';
38852             }
38853         } catch(e) {
38854             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38855             Roo.log(e.toString());
38856             Roo.log(t.exec);
38857             return ''
38858         }
38859         try {
38860             var vs = t.target ? t.target.call(this, values, parent) : values;
38861             parent = t.target ? values : parent;
38862             if(t.target && vs instanceof Array){
38863                 var buf = [];
38864                 for(var i = 0, len = vs.length; i < len; i++){
38865                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38866                 }
38867                 return buf.join('');
38868             }
38869             return t.compiled.call(this, vs, parent);
38870         } catch (e) {
38871             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38872             Roo.log(e.toString());
38873             Roo.log(t.compiled);
38874             return '';
38875         }
38876     },
38877
38878     compileTpl : function(tpl)
38879     {
38880         var fm = Roo.util.Format;
38881         var useF = this.disableFormats !== true;
38882         var sep = Roo.isGecko ? "+" : ",";
38883         var undef = function(str) {
38884             Roo.log("Property not found :"  + str);
38885             return '';
38886         };
38887         
38888         var fn = function(m, name, format, args)
38889         {
38890             //Roo.log(arguments);
38891             args = args ? args.replace(/\\'/g,"'") : args;
38892             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38893             if (typeof(format) == 'undefined') {
38894                 format= 'htmlEncode';
38895             }
38896             if (format == 'raw' ) {
38897                 format = false;
38898             }
38899             
38900             if(name.substr(0, 4) == 'xtpl'){
38901                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38902             }
38903             
38904             // build an array of options to determine if value is undefined..
38905             
38906             // basically get 'xxxx.yyyy' then do
38907             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38908             //    (function () { Roo.log("Property not found"); return ''; })() :
38909             //    ......
38910             
38911             var udef_ar = [];
38912             var lookfor = '';
38913             Roo.each(name.split('.'), function(st) {
38914                 lookfor += (lookfor.length ? '.': '') + st;
38915                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38916             });
38917             
38918             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38919             
38920             
38921             if(format && useF){
38922                 
38923                 args = args ? ',' + args : "";
38924                  
38925                 if(format.substr(0, 5) != "this."){
38926                     format = "fm." + format + '(';
38927                 }else{
38928                     format = 'this.call("'+ format.substr(5) + '", ';
38929                     args = ", values";
38930                 }
38931                 
38932                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38933             }
38934              
38935             if (args.length) {
38936                 // called with xxyx.yuu:(test,test)
38937                 // change to ()
38938                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38939             }
38940             // raw.. - :raw modifier..
38941             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38942             
38943         };
38944         var body;
38945         // branched to use + in gecko and [].join() in others
38946         if(Roo.isGecko){
38947             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38948                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38949                     "';};};";
38950         }else{
38951             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38952             body.push(tpl.body.replace(/(\r\n|\n)/g,
38953                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38954             body.push("'].join('');};};");
38955             body = body.join('');
38956         }
38957         
38958         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38959        
38960         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38961         eval(body);
38962         
38963         return this;
38964     },
38965
38966     applyTemplate : function(values){
38967         return this.master.compiled.call(this, values, {});
38968         //var s = this.subs;
38969     },
38970
38971     apply : function(){
38972         return this.applyTemplate.apply(this, arguments);
38973     }
38974
38975  });
38976
38977 Roo.XTemplate.from = function(el){
38978     el = Roo.getDom(el);
38979     return new Roo.XTemplate(el.value || el.innerHTML);
38980 };