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  * @static
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 [required] 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.DataReader} reader [required]  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>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1398  * to the Reader when its load method is called.
1399  * @constructor
1400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1401  */
1402 Roo.data.MemoryProxy = function(data){
1403     if (data.data) {
1404         data = data.data;
1405     }
1406     Roo.data.MemoryProxy.superclass.constructor.call(this);
1407     this.data = data;
1408 };
1409
1410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1411     
1412     /**
1413      * Load data from the requested source (in this case an in-memory
1414      * data object passed to the constructor), read the data object into
1415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1416      * process that block using the passed callback.
1417      * @param {Object} params This parameter is not used by the MemoryProxy class.
1418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1419      * object into a block of Roo.data.Records.
1420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1421      * The function must be passed <ul>
1422      * <li>The Record block object</li>
1423      * <li>The "arg" argument from the load function</li>
1424      * <li>A boolean success indicator</li>
1425      * </ul>
1426      * @param {Object} scope The scope in which to call the callback
1427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1428      */
1429     load : function(params, reader, callback, scope, arg){
1430         params = params || {};
1431         var result;
1432         try {
1433             result = reader.readRecords(params.data ? params.data :this.data);
1434         }catch(e){
1435             this.fireEvent("loadexception", this, arg, null, e);
1436             callback.call(scope, null, arg, false);
1437             return;
1438         }
1439         callback.call(scope, result, arg, true);
1440     },
1441     
1442     // private
1443     update : function(params, records){
1444         
1445     }
1446 });/*
1447  * Based on:
1448  * Ext JS Library 1.1.1
1449  * Copyright(c) 2006-2007, Ext JS, LLC.
1450  *
1451  * Originally Released Under LGPL - original licence link has changed is not relivant.
1452  *
1453  * Fork - LGPL
1454  * <script type="text/javascript">
1455  */
1456 /**
1457  * @class Roo.data.HttpProxy
1458  * @extends Roo.data.DataProxy
1459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1460  * configured to reference a certain URL.<br><br>
1461  * <p>
1462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1463  * from which the running page was served.<br><br>
1464  * <p>
1465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1466  * <p>
1467  * Be aware that to enable the browser to parse an XML document, the server must set
1468  * the Content-Type header in the HTTP response to "text/xml".
1469  * @constructor
1470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1472  * will be used to make the request.
1473  */
1474 Roo.data.HttpProxy = function(conn){
1475     Roo.data.HttpProxy.superclass.constructor.call(this);
1476     // is conn a conn config or a real conn?
1477     this.conn = conn;
1478     this.useAjax = !conn || !conn.events;
1479   
1480 };
1481
1482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1483     // thse are take from connection...
1484     
1485     /**
1486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1487      */
1488     /**
1489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1490      * extra parameters to each request made by this object. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1494      *  to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @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)
1498      */
1499     /**
1500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1501      */
1502      /**
1503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1504      * @type Boolean
1505      */
1506   
1507
1508     /**
1509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1510      * @type Boolean
1511      */
1512     /**
1513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1515      * a finer-grained basis than the DataProxy events.
1516      */
1517     getConnection : function(){
1518         return this.useAjax ? Roo.Ajax : this.conn;
1519     },
1520
1521     /**
1522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1524      * process that block using the passed callback.
1525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1526      * for the request to the remote server.
1527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1528      * object into a block of Roo.data.Records.
1529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1530      * The function must be passed <ul>
1531      * <li>The Record block object</li>
1532      * <li>The "arg" argument from the load function</li>
1533      * <li>A boolean success indicator</li>
1534      * </ul>
1535      * @param {Object} scope The scope in which to call the callback
1536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1537      */
1538     load : function(params, reader, callback, scope, arg){
1539         if(this.fireEvent("beforeload", this, params) !== false){
1540             var  o = {
1541                 params : params || {},
1542                 request: {
1543                     callback : callback,
1544                     scope : scope,
1545                     arg : arg
1546                 },
1547                 reader: reader,
1548                 callback : this.loadResponse,
1549                 scope: this
1550             };
1551             if(this.useAjax){
1552                 Roo.applyIf(o, this.conn);
1553                 if(this.activeRequest){
1554                     Roo.Ajax.abort(this.activeRequest);
1555                 }
1556                 this.activeRequest = Roo.Ajax.request(o);
1557             }else{
1558                 this.conn.request(o);
1559             }
1560         }else{
1561             callback.call(scope||this, null, arg, false);
1562         }
1563     },
1564
1565     // private
1566     loadResponse : function(o, success, response){
1567         delete this.activeRequest;
1568         if(!success){
1569             this.fireEvent("loadexception", this, o, response);
1570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571             return;
1572         }
1573         var result;
1574         try {
1575             result = o.reader.read(response);
1576         }catch(e){
1577             o.success = false;
1578             o.raw = { errorMsg : response.responseText };
1579             this.fireEvent("loadexception", this, o, response, e);
1580             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1581             return;
1582         }
1583         
1584         this.fireEvent("load", this, o, o.request.arg);
1585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1586     },
1587
1588     // private
1589     update : function(dataSet){
1590
1591     },
1592
1593     // private
1594     updateResponse : function(dataSet){
1595
1596     }
1597 });/*
1598  * Based on:
1599  * Ext JS Library 1.1.1
1600  * Copyright(c) 2006-2007, Ext JS, LLC.
1601  *
1602  * Originally Released Under LGPL - original licence link has changed is not relivant.
1603  *
1604  * Fork - LGPL
1605  * <script type="text/javascript">
1606  */
1607
1608 /**
1609  * @class Roo.data.ScriptTagProxy
1610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1611  * other than the originating domain of the running page.<br><br>
1612  * <p>
1613  * <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
1614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1615  * <p>
1616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1617  * source code that is used as the source inside a &lt;script> tag.<br><br>
1618  * <p>
1619  * In order for the browser to process the returned data, the server must wrap the data object
1620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1622  * depending on whether the callback name was passed:
1623  * <p>
1624  * <pre><code>
1625 boolean scriptTag = false;
1626 String cb = request.getParameter("callback");
1627 if (cb != null) {
1628     scriptTag = true;
1629     response.setContentType("text/javascript");
1630 } else {
1631     response.setContentType("application/x-json");
1632 }
1633 Writer out = response.getWriter();
1634 if (scriptTag) {
1635     out.write(cb + "(");
1636 }
1637 out.print(dataBlock.toJsonString());
1638 if (scriptTag) {
1639     out.write(");");
1640 }
1641 </pre></code>
1642  *
1643  * @constructor
1644  * @param {Object} config A configuration object.
1645  */
1646 Roo.data.ScriptTagProxy = function(config){
1647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1648     Roo.apply(this, config);
1649     this.head = document.getElementsByTagName("head")[0];
1650 };
1651
1652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1653
1654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1655     /**
1656      * @cfg {String} url The URL from which to request the data object.
1657      */
1658     /**
1659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1660      */
1661     timeout : 30000,
1662     /**
1663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1664      * the server the name of the callback function set up by the load call to process the returned data object.
1665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1666      * javascript output which calls this named function passing the data object as its only parameter.
1667      */
1668     callbackParam : "callback",
1669     /**
1670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1671      * name to the request.
1672      */
1673     nocache : true,
1674
1675     /**
1676      * Load data from the configured URL, read the data object into
1677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1678      * process that block using the passed callback.
1679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1680      * for the request to the remote server.
1681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1682      * object into a block of Roo.data.Records.
1683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1684      * The function must be passed <ul>
1685      * <li>The Record block object</li>
1686      * <li>The "arg" argument from the load function</li>
1687      * <li>A boolean success indicator</li>
1688      * </ul>
1689      * @param {Object} scope The scope in which to call the callback
1690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1691      */
1692     load : function(params, reader, callback, scope, arg){
1693         if(this.fireEvent("beforeload", this, params) !== false){
1694
1695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1696
1697             var url = this.url;
1698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1699             if(this.nocache){
1700                 url += "&_dc=" + (new Date().getTime());
1701             }
1702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1703             var trans = {
1704                 id : transId,
1705                 cb : "stcCallback"+transId,
1706                 scriptId : "stcScript"+transId,
1707                 params : params,
1708                 arg : arg,
1709                 url : url,
1710                 callback : callback,
1711                 scope : scope,
1712                 reader : reader
1713             };
1714             var conn = this;
1715
1716             window[trans.cb] = function(o){
1717                 conn.handleResponse(o, trans);
1718             };
1719
1720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1721
1722             if(this.autoAbort !== false){
1723                 this.abort();
1724             }
1725
1726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1727
1728             var script = document.createElement("script");
1729             script.setAttribute("src", url);
1730             script.setAttribute("type", "text/javascript");
1731             script.setAttribute("id", trans.scriptId);
1732             this.head.appendChild(script);
1733
1734             this.trans = trans;
1735         }else{
1736             callback.call(scope||this, null, arg, false);
1737         }
1738     },
1739
1740     // private
1741     isLoading : function(){
1742         return this.trans ? true : false;
1743     },
1744
1745     /**
1746      * Abort the current server request.
1747      */
1748     abort : function(){
1749         if(this.isLoading()){
1750             this.destroyTrans(this.trans);
1751         }
1752     },
1753
1754     // private
1755     destroyTrans : function(trans, isLoaded){
1756         this.head.removeChild(document.getElementById(trans.scriptId));
1757         clearTimeout(trans.timeoutId);
1758         if(isLoaded){
1759             window[trans.cb] = undefined;
1760             try{
1761                 delete window[trans.cb];
1762             }catch(e){}
1763         }else{
1764             // if hasn't been loaded, wait for load to remove it to prevent script error
1765             window[trans.cb] = function(){
1766                 window[trans.cb] = undefined;
1767                 try{
1768                     delete window[trans.cb];
1769                 }catch(e){}
1770             };
1771         }
1772     },
1773
1774     // private
1775     handleResponse : function(o, trans){
1776         this.trans = false;
1777         this.destroyTrans(trans, true);
1778         var result;
1779         try {
1780             result = trans.reader.readRecords(o);
1781         }catch(e){
1782             this.fireEvent("loadexception", this, o, trans.arg, e);
1783             trans.callback.call(trans.scope||window, null, trans.arg, false);
1784             return;
1785         }
1786         this.fireEvent("load", this, o, trans.arg);
1787         trans.callback.call(trans.scope||window, result, trans.arg, true);
1788     },
1789
1790     // private
1791     handleFailure : function(trans){
1792         this.trans = false;
1793         this.destroyTrans(trans, false);
1794         this.fireEvent("loadexception", this, null, trans.arg);
1795         trans.callback.call(trans.scope||window, null, trans.arg, false);
1796     }
1797 });/*
1798  * Based on:
1799  * Ext JS Library 1.1.1
1800  * Copyright(c) 2006-2007, Ext JS, LLC.
1801  *
1802  * Originally Released Under LGPL - original licence link has changed is not relivant.
1803  *
1804  * Fork - LGPL
1805  * <script type="text/javascript">
1806  */
1807
1808 /**
1809  * @class Roo.data.JsonReader
1810  * @extends Roo.data.DataReader
1811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1812  * based on mappings in a provided Roo.data.Record constructor.
1813  * 
1814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1815  * in the reply previously. 
1816  * 
1817  * <p>
1818  * Example code:
1819  * <pre><code>
1820 var RecordDef = Roo.data.Record.create([
1821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1823 ]);
1824 var myReader = new Roo.data.JsonReader({
1825     totalProperty: "results",    // The property which contains the total dataset size (optional)
1826     root: "rows",                // The property which contains an Array of row objects
1827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1828 }, RecordDef);
1829 </code></pre>
1830  * <p>
1831  * This would consume a JSON file like this:
1832  * <pre><code>
1833 { 'results': 2, 'rows': [
1834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1836 }
1837 </code></pre>
1838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1840  * paged from the remote server.
1841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1842  * @cfg {String} root name of the property which contains the Array of row objects.
1843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1844  * @cfg {Array} fields Array of field definition objects
1845  * @constructor
1846  * Create a new JsonReader
1847  * @param {Object} meta Metadata configuration options
1848  * @param {Object} recordType Either an Array of field definition objects,
1849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1850  */
1851 Roo.data.JsonReader = function(meta, recordType){
1852     
1853     meta = meta || {};
1854     // set some defaults:
1855     Roo.applyIf(meta, {
1856         totalProperty: 'total',
1857         successProperty : 'success',
1858         root : 'data',
1859         id : 'id'
1860     });
1861     
1862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1863 };
1864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1865     
1866     readerType : 'Json',
1867     
1868     /**
1869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1870      * Used by Store query builder to append _requestMeta to params.
1871      * 
1872      */
1873     metaFromRemote : false,
1874     /**
1875      * This method is only used by a DataProxy which has retrieved data from a remote server.
1876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1877      * @return {Object} data A data block which is used by an Roo.data.Store object as
1878      * a cache of Roo.data.Records.
1879      */
1880     read : function(response){
1881         var json = response.responseText;
1882        
1883         var o = /* eval:var:o */ eval("("+json+")");
1884         if(!o) {
1885             throw {message: "JsonReader.read: Json object not found"};
1886         }
1887         
1888         if(o.metaData){
1889             
1890             delete this.ef;
1891             this.metaFromRemote = true;
1892             this.meta = o.metaData;
1893             this.recordType = Roo.data.Record.create(o.metaData.fields);
1894             this.onMetaChange(this.meta, this.recordType, o);
1895         }
1896         return this.readRecords(o);
1897     },
1898
1899     // private function a store will implement
1900     onMetaChange : function(meta, recordType, o){
1901
1902     },
1903
1904     /**
1905          * @ignore
1906          */
1907     simpleAccess: function(obj, subsc) {
1908         return obj[subsc];
1909     },
1910
1911         /**
1912          * @ignore
1913          */
1914     getJsonAccessor: function(){
1915         var re = /[\[\.]/;
1916         return function(expr) {
1917             try {
1918                 return(re.test(expr))
1919                     ? new Function("obj", "return obj." + expr)
1920                     : function(obj){
1921                         return obj[expr];
1922                     };
1923             } catch(e){}
1924             return Roo.emptyFn;
1925         };
1926     }(),
1927
1928     /**
1929      * Create a data block containing Roo.data.Records from an XML document.
1930      * @param {Object} o An object which contains an Array of row objects in the property specified
1931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1932      * which contains the total size of the dataset.
1933      * @return {Object} data A data block which is used by an Roo.data.Store object as
1934      * a cache of Roo.data.Records.
1935      */
1936     readRecords : function(o){
1937         /**
1938          * After any data loads, the raw JSON data is available for further custom processing.
1939          * @type Object
1940          */
1941         this.o = o;
1942         var s = this.meta, Record = this.recordType,
1943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1944
1945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1946         if (!this.ef) {
1947             if(s.totalProperty) {
1948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1949                 }
1950                 if(s.successProperty) {
1951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1952                 }
1953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1954                 if (s.id) {
1955                         var g = this.getJsonAccessor(s.id);
1956                         this.getId = function(rec) {
1957                                 var r = g(rec);  
1958                                 return (r === undefined || r === "") ? null : r;
1959                         };
1960                 } else {
1961                         this.getId = function(){return null;};
1962                 }
1963             this.ef = [];
1964             for(var jj = 0; jj < fl; jj++){
1965                 f = fi[jj];
1966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1967                 this.ef[jj] = this.getJsonAccessor(map);
1968             }
1969         }
1970
1971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1972         if(s.totalProperty){
1973             var vt = parseInt(this.getTotal(o), 10);
1974             if(!isNaN(vt)){
1975                 totalRecords = vt;
1976             }
1977         }
1978         if(s.successProperty){
1979             var vs = this.getSuccess(o);
1980             if(vs === false || vs === 'false'){
1981                 success = false;
1982             }
1983         }
1984         var records = [];
1985         for(var i = 0; i < c; i++){
1986             var n = root[i];
1987             var values = {};
1988             var id = this.getId(n);
1989             for(var j = 0; j < fl; j++){
1990                 f = fi[j];
1991                                 var v = this.ef[j](n);
1992                                 if (!f.convert) {
1993                                         Roo.log('missing convert for ' + f.name);
1994                                         Roo.log(f);
1995                                         continue;
1996                                 }
1997                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1998             }
1999                         if (!Record) {
2000                                 return {
2001                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2002                                         success : false,
2003                                         records : [],
2004                                         totalRecords : 0
2005                                 };
2006                         }
2007             var record = new Record(values, id);
2008             record.json = n;
2009             records[i] = record;
2010         }
2011         return {
2012             raw : o,
2013             success : success,
2014             records : records,
2015             totalRecords : totalRecords
2016         };
2017     },
2018     // used when loading children.. @see loadDataFromChildren
2019     toLoadData: function(rec)
2020     {
2021         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2022         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2023         return { data : data, total : data.length };
2024         
2025     }
2026 });/*
2027  * Based on:
2028  * Ext JS Library 1.1.1
2029  * Copyright(c) 2006-2007, Ext JS, LLC.
2030  *
2031  * Originally Released Under LGPL - original licence link has changed is not relivant.
2032  *
2033  * Fork - LGPL
2034  * <script type="text/javascript">
2035  */
2036
2037 /**
2038  * @class Roo.data.XmlReader
2039  * @extends Roo.data.DataReader
2040  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2041  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2042  * <p>
2043  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2044  * header in the HTTP response must be set to "text/xml".</em>
2045  * <p>
2046  * Example code:
2047  * <pre><code>
2048 var RecordDef = Roo.data.Record.create([
2049    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2050    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2051 ]);
2052 var myReader = new Roo.data.XmlReader({
2053    totalRecords: "results", // The element which contains the total dataset size (optional)
2054    record: "row",           // The repeated element which contains row information
2055    id: "id"                 // The element within the row that provides an ID for the record (optional)
2056 }, RecordDef);
2057 </code></pre>
2058  * <p>
2059  * This would consume an XML file like this:
2060  * <pre><code>
2061 &lt;?xml?>
2062 &lt;dataset>
2063  &lt;results>2&lt;/results>
2064  &lt;row>
2065    &lt;id>1&lt;/id>
2066    &lt;name>Bill&lt;/name>
2067    &lt;occupation>Gardener&lt;/occupation>
2068  &lt;/row>
2069  &lt;row>
2070    &lt;id>2&lt;/id>
2071    &lt;name>Ben&lt;/name>
2072    &lt;occupation>Horticulturalist&lt;/occupation>
2073  &lt;/row>
2074 &lt;/dataset>
2075 </code></pre>
2076  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2078  * paged from the remote server.
2079  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2080  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2081  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2082  * a record identifier value.
2083  * @constructor
2084  * Create a new XmlReader
2085  * @param {Object} meta Metadata configuration options
2086  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2087  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2088  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2089  */
2090 Roo.data.XmlReader = function(meta, recordType){
2091     meta = meta || {};
2092     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2093 };
2094 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2095     
2096     readerType : 'Xml',
2097     
2098     /**
2099      * This method is only used by a DataProxy which has retrieved data from a remote server.
2100          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2101          * to contain a method called 'responseXML' that returns an XML document object.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     read : function(response){
2106         var doc = response.responseXML;
2107         if(!doc) {
2108             throw {message: "XmlReader.read: XML Document not available"};
2109         }
2110         return this.readRecords(doc);
2111     },
2112
2113     /**
2114      * Create a data block containing Roo.data.Records from an XML document.
2115          * @param {Object} doc A parsed XML document.
2116      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2117      * a cache of Roo.data.Records.
2118      */
2119     readRecords : function(doc){
2120         /**
2121          * After any data loads/reads, the raw XML Document is available for further custom processing.
2122          * @type XMLDocument
2123          */
2124         this.xmlData = doc;
2125         var root = doc.documentElement || doc;
2126         var q = Roo.DomQuery;
2127         var recordType = this.recordType, fields = recordType.prototype.fields;
2128         var sid = this.meta.id;
2129         var totalRecords = 0, success = true;
2130         if(this.meta.totalRecords){
2131             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2132         }
2133         
2134         if(this.meta.success){
2135             var sv = q.selectValue(this.meta.success, root, true);
2136             success = sv !== false && sv !== 'false';
2137         }
2138         var records = [];
2139         var ns = q.select(this.meta.record, root);
2140         for(var i = 0, len = ns.length; i < len; i++) {
2141                 var n = ns[i];
2142                 var values = {};
2143                 var id = sid ? q.selectValue(sid, n) : undefined;
2144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2145                     var f = fields.items[j];
2146                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2147                     v = f.convert(v);
2148                     values[f.name] = v;
2149                 }
2150                 var record = new recordType(values, id);
2151                 record.node = n;
2152                 records[records.length] = record;
2153             }
2154
2155             return {
2156                 success : success,
2157                 records : records,
2158                 totalRecords : totalRecords || records.length
2159             };
2160     }
2161 });/*
2162  * Based on:
2163  * Ext JS Library 1.1.1
2164  * Copyright(c) 2006-2007, Ext JS, LLC.
2165  *
2166  * Originally Released Under LGPL - original licence link has changed is not relivant.
2167  *
2168  * Fork - LGPL
2169  * <script type="text/javascript">
2170  */
2171
2172 /**
2173  * @class Roo.data.ArrayReader
2174  * @extends Roo.data.DataReader
2175  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2176  * Each element of that Array represents a row of data fields. The
2177  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2178  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2179  * <p>
2180  * Example code:.
2181  * <pre><code>
2182 var RecordDef = Roo.data.Record.create([
2183     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2184     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2185 ]);
2186 var myReader = new Roo.data.ArrayReader({
2187     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2188 }, RecordDef);
2189 </code></pre>
2190  * <p>
2191  * This would consume an Array like this:
2192  * <pre><code>
2193 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2194   </code></pre>
2195  
2196  * @constructor
2197  * Create a new JsonReader
2198  * @param {Object} meta Metadata configuration options.
2199  * @param {Object|Array} recordType Either an Array of field definition objects
2200  * 
2201  * @cfg {Array} fields Array of field definition objects
2202  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2203  * as specified to {@link Roo.data.Record#create},
2204  * or an {@link Roo.data.Record} object
2205  *
2206  * 
2207  * created using {@link Roo.data.Record#create}.
2208  */
2209 Roo.data.ArrayReader = function(meta, recordType)
2210 {    
2211     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2212 };
2213
2214 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2215     
2216       /**
2217      * Create a data block containing Roo.data.Records from an XML document.
2218      * @param {Object} o An Array of row objects which represents the dataset.
2219      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2220      * a cache of Roo.data.Records.
2221      */
2222     readRecords : function(o)
2223     {
2224         var sid = this.meta ? this.meta.id : null;
2225         var recordType = this.recordType, fields = recordType.prototype.fields;
2226         var records = [];
2227         var root = o;
2228         for(var i = 0; i < root.length; i++){
2229             var n = root[i];
2230             var values = {};
2231             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2232             for(var j = 0, jlen = fields.length; j < jlen; j++){
2233                 var f = fields.items[j];
2234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2236                 v = f.convert(v);
2237                 values[f.name] = v;
2238             }
2239             var record = new recordType(values, id);
2240             record.json = n;
2241             records[records.length] = record;
2242         }
2243         return {
2244             records : records,
2245             totalRecords : records.length
2246         };
2247     },
2248     // used when loading children.. @see loadDataFromChildren
2249     toLoadData: function(rec)
2250     {
2251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2252         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2253         
2254     }
2255     
2256     
2257 });/*
2258  * Based on:
2259  * Ext JS Library 1.1.1
2260  * Copyright(c) 2006-2007, Ext JS, LLC.
2261  *
2262  * Originally Released Under LGPL - original licence link has changed is not relivant.
2263  *
2264  * Fork - LGPL
2265  * <script type="text/javascript">
2266  */
2267
2268
2269 /**
2270  * @class Roo.data.Tree
2271  * @extends Roo.util.Observable
2272  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2273  * in the tree have most standard DOM functionality.
2274  * @constructor
2275  * @param {Node} root (optional) The root node
2276  */
2277 Roo.data.Tree = function(root){
2278    this.nodeHash = {};
2279    /**
2280     * The root node for this tree
2281     * @type Node
2282     */
2283    this.root = null;
2284    if(root){
2285        this.setRootNode(root);
2286    }
2287    this.addEvents({
2288        /**
2289         * @event append
2290         * Fires when a new child node is appended to a node in this tree.
2291         * @param {Tree} tree The owner tree
2292         * @param {Node} parent The parent node
2293         * @param {Node} node The newly appended node
2294         * @param {Number} index The index of the newly appended node
2295         */
2296        "append" : true,
2297        /**
2298         * @event remove
2299         * Fires when a child node is removed from a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node removed
2303         */
2304        "remove" : true,
2305        /**
2306         * @event move
2307         * Fires when a node is moved to a new location in the tree
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} node The node moved
2310         * @param {Node} oldParent The old parent of this node
2311         * @param {Node} newParent The new parent of this node
2312         * @param {Number} index The index it was moved to
2313         */
2314        "move" : true,
2315        /**
2316         * @event insert
2317         * Fires when a new child node is inserted in a node in this tree.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} parent The parent node
2320         * @param {Node} node The child node inserted
2321         * @param {Node} refNode The child node the node was inserted before
2322         */
2323        "insert" : true,
2324        /**
2325         * @event beforeappend
2326         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2327         * @param {Tree} tree The owner tree
2328         * @param {Node} parent The parent node
2329         * @param {Node} node The child node to be appended
2330         */
2331        "beforeappend" : true,
2332        /**
2333         * @event beforeremove
2334         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be removed
2338         */
2339        "beforeremove" : true,
2340        /**
2341         * @event beforemove
2342         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} node The node being moved
2345         * @param {Node} oldParent The parent of the node
2346         * @param {Node} newParent The new parent the node is moving to
2347         * @param {Number} index The index it is being moved to
2348         */
2349        "beforemove" : true,
2350        /**
2351         * @event beforeinsert
2352         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2353         * @param {Tree} tree The owner tree
2354         * @param {Node} parent The parent node
2355         * @param {Node} node The child node to be inserted
2356         * @param {Node} refNode The child node the node is being inserted before
2357         */
2358        "beforeinsert" : true
2359    });
2360
2361     Roo.data.Tree.superclass.constructor.call(this);
2362 };
2363
2364 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2365     pathSeparator: "/",
2366
2367     proxyNodeEvent : function(){
2368         return this.fireEvent.apply(this, arguments);
2369     },
2370
2371     /**
2372      * Returns the root node for this tree.
2373      * @return {Node}
2374      */
2375     getRootNode : function(){
2376         return this.root;
2377     },
2378
2379     /**
2380      * Sets the root node for this tree.
2381      * @param {Node} node
2382      * @return {Node}
2383      */
2384     setRootNode : function(node){
2385         this.root = node;
2386         node.ownerTree = this;
2387         node.isRoot = true;
2388         this.registerNode(node);
2389         return node;
2390     },
2391
2392     /**
2393      * Gets a node in this tree by its id.
2394      * @param {String} id
2395      * @return {Node}
2396      */
2397     getNodeById : function(id){
2398         return this.nodeHash[id];
2399     },
2400
2401     registerNode : function(node){
2402         this.nodeHash[node.id] = node;
2403     },
2404
2405     unregisterNode : function(node){
2406         delete this.nodeHash[node.id];
2407     },
2408
2409     toString : function(){
2410         return "[Tree"+(this.id?" "+this.id:"")+"]";
2411     }
2412 });
2413
2414 /**
2415  * @class Roo.data.Node
2416  * @extends Roo.util.Observable
2417  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2418  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2419  * @constructor
2420  * @param {Object} attributes The attributes/config for the node
2421  */
2422 Roo.data.Node = function(attributes){
2423     /**
2424      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2425      * @type {Object}
2426      */
2427     this.attributes = attributes || {};
2428     this.leaf = this.attributes.leaf;
2429     /**
2430      * The node id. @type String
2431      */
2432     this.id = this.attributes.id;
2433     if(!this.id){
2434         this.id = Roo.id(null, "ynode-");
2435         this.attributes.id = this.id;
2436     }
2437      
2438     
2439     /**
2440      * All child nodes of this node. @type Array
2441      */
2442     this.childNodes = [];
2443     if(!this.childNodes.indexOf){ // indexOf is a must
2444         this.childNodes.indexOf = function(o){
2445             for(var i = 0, len = this.length; i < len; i++){
2446                 if(this[i] == o) {
2447                     return i;
2448                 }
2449             }
2450             return -1;
2451         };
2452     }
2453     /**
2454      * The parent node for this node. @type Node
2455      */
2456     this.parentNode = null;
2457     /**
2458      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2459      */
2460     this.firstChild = null;
2461     /**
2462      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.lastChild = null;
2465     /**
2466      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2467      */
2468     this.previousSibling = null;
2469     /**
2470      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.nextSibling = null;
2473
2474     this.addEvents({
2475        /**
2476         * @event append
2477         * Fires when a new child node is appended
2478         * @param {Tree} tree The owner tree
2479         * @param {Node} this This node
2480         * @param {Node} node The newly appended node
2481         * @param {Number} index The index of the newly appended node
2482         */
2483        "append" : true,
2484        /**
2485         * @event remove
2486         * Fires when a child node is removed
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The removed node
2490         */
2491        "remove" : true,
2492        /**
2493         * @event move
2494         * Fires when this node is moved to a new location in the tree
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} oldParent The old parent of this node
2498         * @param {Node} newParent The new parent of this node
2499         * @param {Number} index The index it was moved to
2500         */
2501        "move" : true,
2502        /**
2503         * @event insert
2504         * Fires when a new child node is inserted.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} node The child node inserted
2508         * @param {Node} refNode The child node the node was inserted before
2509         */
2510        "insert" : true,
2511        /**
2512         * @event beforeappend
2513         * Fires before a new child is appended, return false to cancel the append.
2514         * @param {Tree} tree The owner tree
2515         * @param {Node} this This node
2516         * @param {Node} node The child node to be appended
2517         */
2518        "beforeappend" : true,
2519        /**
2520         * @event beforeremove
2521         * Fires before a child is removed, return false to cancel the remove.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be removed
2525         */
2526        "beforeremove" : true,
2527        /**
2528         * @event beforemove
2529         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} oldParent The parent of this node
2533         * @param {Node} newParent The new parent this node is moving to
2534         * @param {Number} index The index it is being moved to
2535         */
2536        "beforemove" : true,
2537        /**
2538         * @event beforeinsert
2539         * Fires before a new child is inserted, return false to cancel the insert.
2540         * @param {Tree} tree The owner tree
2541         * @param {Node} this This node
2542         * @param {Node} node The child node to be inserted
2543         * @param {Node} refNode The child node the node is being inserted before
2544         */
2545        "beforeinsert" : true
2546    });
2547     this.listeners = this.attributes.listeners;
2548     Roo.data.Node.superclass.constructor.call(this);
2549 };
2550
2551 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2552     fireEvent : function(evtName){
2553         // first do standard event for this node
2554         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2555             return false;
2556         }
2557         // then bubble it up to the tree if the event wasn't cancelled
2558         var ot = this.getOwnerTree();
2559         if(ot){
2560             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2561                 return false;
2562             }
2563         }
2564         return true;
2565     },
2566
2567     /**
2568      * Returns true if this node is a leaf
2569      * @return {Boolean}
2570      */
2571     isLeaf : function(){
2572         return this.leaf === true;
2573     },
2574
2575     // private
2576     setFirstChild : function(node){
2577         this.firstChild = node;
2578     },
2579
2580     //private
2581     setLastChild : function(node){
2582         this.lastChild = node;
2583     },
2584
2585
2586     /**
2587      * Returns true if this node is the last child of its parent
2588      * @return {Boolean}
2589      */
2590     isLast : function(){
2591        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2592     },
2593
2594     /**
2595      * Returns true if this node is the first child of its parent
2596      * @return {Boolean}
2597      */
2598     isFirst : function(){
2599        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2600     },
2601
2602     hasChildNodes : function(){
2603         return !this.isLeaf() && this.childNodes.length > 0;
2604     },
2605
2606     /**
2607      * Insert node(s) as the last child node of this node.
2608      * @param {Node/Array} node The node or Array of nodes to append
2609      * @return {Node} The appended node if single append, or null if an array was passed
2610      */
2611     appendChild : function(node){
2612         var multi = false;
2613         if(node instanceof Array){
2614             multi = node;
2615         }else if(arguments.length > 1){
2616             multi = arguments;
2617         }
2618         
2619         // if passed an array or multiple args do them one by one
2620         if(multi){
2621             for(var i = 0, len = multi.length; i < len; i++) {
2622                 this.appendChild(multi[i]);
2623             }
2624         }else{
2625             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2626                 return false;
2627             }
2628             var index = this.childNodes.length;
2629             var oldParent = node.parentNode;
2630             // it's a move, make sure we move it cleanly
2631             if(oldParent){
2632                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2633                     return false;
2634                 }
2635                 oldParent.removeChild(node);
2636             }
2637             
2638             index = this.childNodes.length;
2639             if(index == 0){
2640                 this.setFirstChild(node);
2641             }
2642             this.childNodes.push(node);
2643             node.parentNode = this;
2644             var ps = this.childNodes[index-1];
2645             if(ps){
2646                 node.previousSibling = ps;
2647                 ps.nextSibling = node;
2648             }else{
2649                 node.previousSibling = null;
2650             }
2651             node.nextSibling = null;
2652             this.setLastChild(node);
2653             node.setOwnerTree(this.getOwnerTree());
2654             this.fireEvent("append", this.ownerTree, this, node, index);
2655             if(this.ownerTree) {
2656                 this.ownerTree.fireEvent("appendnode", this, node, index);
2657             }
2658             if(oldParent){
2659                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2660             }
2661             return node;
2662         }
2663     },
2664
2665     /**
2666      * Removes a child node from this node.
2667      * @param {Node} node The node to remove
2668      * @return {Node} The removed node
2669      */
2670     removeChild : function(node){
2671         var index = this.childNodes.indexOf(node);
2672         if(index == -1){
2673             return false;
2674         }
2675         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2676             return false;
2677         }
2678
2679         // remove it from childNodes collection
2680         this.childNodes.splice(index, 1);
2681
2682         // update siblings
2683         if(node.previousSibling){
2684             node.previousSibling.nextSibling = node.nextSibling;
2685         }
2686         if(node.nextSibling){
2687             node.nextSibling.previousSibling = node.previousSibling;
2688         }
2689
2690         // update child refs
2691         if(this.firstChild == node){
2692             this.setFirstChild(node.nextSibling);
2693         }
2694         if(this.lastChild == node){
2695             this.setLastChild(node.previousSibling);
2696         }
2697
2698         node.setOwnerTree(null);
2699         // clear any references from the node
2700         node.parentNode = null;
2701         node.previousSibling = null;
2702         node.nextSibling = null;
2703         this.fireEvent("remove", this.ownerTree, this, node);
2704         return node;
2705     },
2706
2707     /**
2708      * Inserts the first node before the second node in this nodes childNodes collection.
2709      * @param {Node} node The node to insert
2710      * @param {Node} refNode The node to insert before (if null the node is appended)
2711      * @return {Node} The inserted node
2712      */
2713     insertBefore : function(node, refNode){
2714         if(!refNode){ // like standard Dom, refNode can be null for append
2715             return this.appendChild(node);
2716         }
2717         // nothing to do
2718         if(node == refNode){
2719             return false;
2720         }
2721
2722         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2723             return false;
2724         }
2725         var index = this.childNodes.indexOf(refNode);
2726         var oldParent = node.parentNode;
2727         var refIndex = index;
2728
2729         // when moving internally, indexes will change after remove
2730         if(oldParent == this && this.childNodes.indexOf(node) < index){
2731             refIndex--;
2732         }
2733
2734         // it's a move, make sure we move it cleanly
2735         if(oldParent){
2736             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2737                 return false;
2738             }
2739             oldParent.removeChild(node);
2740         }
2741         if(refIndex == 0){
2742             this.setFirstChild(node);
2743         }
2744         this.childNodes.splice(refIndex, 0, node);
2745         node.parentNode = this;
2746         var ps = this.childNodes[refIndex-1];
2747         if(ps){
2748             node.previousSibling = ps;
2749             ps.nextSibling = node;
2750         }else{
2751             node.previousSibling = null;
2752         }
2753         node.nextSibling = refNode;
2754         refNode.previousSibling = node;
2755         node.setOwnerTree(this.getOwnerTree());
2756         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2757         if(oldParent){
2758             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2759         }
2760         return node;
2761     },
2762
2763     /**
2764      * Returns the child node at the specified index.
2765      * @param {Number} index
2766      * @return {Node}
2767      */
2768     item : function(index){
2769         return this.childNodes[index];
2770     },
2771
2772     /**
2773      * Replaces one child node in this node with another.
2774      * @param {Node} newChild The replacement node
2775      * @param {Node} oldChild The node to replace
2776      * @return {Node} The replaced node
2777      */
2778     replaceChild : function(newChild, oldChild){
2779         this.insertBefore(newChild, oldChild);
2780         this.removeChild(oldChild);
2781         return oldChild;
2782     },
2783
2784     /**
2785      * Returns the index of a child node
2786      * @param {Node} node
2787      * @return {Number} The index of the node or -1 if it was not found
2788      */
2789     indexOf : function(child){
2790         return this.childNodes.indexOf(child);
2791     },
2792
2793     /**
2794      * Returns the tree this node is in.
2795      * @return {Tree}
2796      */
2797     getOwnerTree : function(){
2798         // if it doesn't have one, look for one
2799         if(!this.ownerTree){
2800             var p = this;
2801             while(p){
2802                 if(p.ownerTree){
2803                     this.ownerTree = p.ownerTree;
2804                     break;
2805                 }
2806                 p = p.parentNode;
2807             }
2808         }
2809         return this.ownerTree;
2810     },
2811
2812     /**
2813      * Returns depth of this node (the root node has a depth of 0)
2814      * @return {Number}
2815      */
2816     getDepth : function(){
2817         var depth = 0;
2818         var p = this;
2819         while(p.parentNode){
2820             ++depth;
2821             p = p.parentNode;
2822         }
2823         return depth;
2824     },
2825
2826     // private
2827     setOwnerTree : function(tree){
2828         // if it's move, we need to update everyone
2829         if(tree != this.ownerTree){
2830             if(this.ownerTree){
2831                 this.ownerTree.unregisterNode(this);
2832             }
2833             this.ownerTree = tree;
2834             var cs = this.childNodes;
2835             for(var i = 0, len = cs.length; i < len; i++) {
2836                 cs[i].setOwnerTree(tree);
2837             }
2838             if(tree){
2839                 tree.registerNode(this);
2840             }
2841         }
2842     },
2843
2844     /**
2845      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2846      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2847      * @return {String} The path
2848      */
2849     getPath : function(attr){
2850         attr = attr || "id";
2851         var p = this.parentNode;
2852         var b = [this.attributes[attr]];
2853         while(p){
2854             b.unshift(p.attributes[attr]);
2855             p = p.parentNode;
2856         }
2857         var sep = this.getOwnerTree().pathSeparator;
2858         return sep + b.join(sep);
2859     },
2860
2861     /**
2862      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the bubble is stopped.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     bubble : function(fn, scope, args){
2871         var p = this;
2872         while(p){
2873             if(fn.call(scope || p, args || p) === false){
2874                 break;
2875             }
2876             p = p.parentNode;
2877         }
2878     },
2879
2880     /**
2881      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the cascade is stopped on that branch.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     cascade : function(fn, scope, args){
2890         if(fn.call(scope || this, args || this) !== false){
2891             var cs = this.childNodes;
2892             for(var i = 0, len = cs.length; i < len; i++) {
2893                 cs[i].cascade(fn, scope, args);
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2900      * function call will be the scope provided or the current node. The arguments to the function
2901      * will be the args provided or the current node. If the function returns false at any point,
2902      * the iteration stops.
2903      * @param {Function} fn The function to call
2904      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2905      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2906      */
2907     eachChild : function(fn, scope, args){
2908         var cs = this.childNodes;
2909         for(var i = 0, len = cs.length; i < len; i++) {
2910                 if(fn.call(scope || this, args || cs[i]) === false){
2911                     break;
2912                 }
2913         }
2914     },
2915
2916     /**
2917      * Finds the first child that has the attribute with the specified value.
2918      * @param {String} attribute The attribute name
2919      * @param {Mixed} value The value to search for
2920      * @return {Node} The found child or null if none was found
2921      */
2922     findChild : function(attribute, value){
2923         var cs = this.childNodes;
2924         for(var i = 0, len = cs.length; i < len; i++) {
2925                 if(cs[i].attributes[attribute] == value){
2926                     return cs[i];
2927                 }
2928         }
2929         return null;
2930     },
2931
2932     /**
2933      * Finds the first child by a custom function. The child matches if the function passed
2934      * returns true.
2935      * @param {Function} fn
2936      * @param {Object} scope (optional)
2937      * @return {Node} The found child or null if none was found
2938      */
2939     findChildBy : function(fn, scope){
2940         var cs = this.childNodes;
2941         for(var i = 0, len = cs.length; i < len; i++) {
2942                 if(fn.call(scope||cs[i], cs[i]) === true){
2943                     return cs[i];
2944                 }
2945         }
2946         return null;
2947     },
2948
2949     /**
2950      * Sorts this nodes children using the supplied sort function
2951      * @param {Function} fn
2952      * @param {Object} scope (optional)
2953      */
2954     sort : function(fn, scope){
2955         var cs = this.childNodes;
2956         var len = cs.length;
2957         if(len > 0){
2958             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2959             cs.sort(sortFn);
2960             for(var i = 0; i < len; i++){
2961                 var n = cs[i];
2962                 n.previousSibling = cs[i-1];
2963                 n.nextSibling = cs[i+1];
2964                 if(i == 0){
2965                     this.setFirstChild(n);
2966                 }
2967                 if(i == len-1){
2968                     this.setLastChild(n);
2969                 }
2970             }
2971         }
2972     },
2973
2974     /**
2975      * Returns true if this node is an ancestor (at any point) of the passed node.
2976      * @param {Node} node
2977      * @return {Boolean}
2978      */
2979     contains : function(node){
2980         return node.isAncestor(this);
2981     },
2982
2983     /**
2984      * Returns true if the passed node is an ancestor (at any point) of this node.
2985      * @param {Node} node
2986      * @return {Boolean}
2987      */
2988     isAncestor : function(node){
2989         var p = this.parentNode;
2990         while(p){
2991             if(p == node){
2992                 return true;
2993             }
2994             p = p.parentNode;
2995         }
2996         return false;
2997     },
2998
2999     toString : function(){
3000         return "[Node"+(this.id?" "+this.id:"")+"]";
3001     }
3002 });/*
3003  * Based on:
3004  * Ext JS Library 1.1.1
3005  * Copyright(c) 2006-2007, Ext JS, LLC.
3006  *
3007  * Originally Released Under LGPL - original licence link has changed is not relivant.
3008  *
3009  * Fork - LGPL
3010  * <script type="text/javascript">
3011  */
3012
3013
3014 /**
3015  * @class Roo.Shadow
3016  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3017  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3018  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3019  * @constructor
3020  * Create a new Shadow
3021  * @param {Object} config The config object
3022  */
3023 Roo.Shadow = function(config){
3024     Roo.apply(this, config);
3025     if(typeof this.mode != "string"){
3026         this.mode = this.defaultMode;
3027     }
3028     var o = this.offset, a = {h: 0};
3029     var rad = Math.floor(this.offset/2);
3030     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3031         case "drop":
3032             a.w = 0;
3033             a.l = a.t = o;
3034             a.t -= 1;
3035             if(Roo.isIE){
3036                 a.l -= this.offset + rad;
3037                 a.t -= this.offset + rad;
3038                 a.w -= rad;
3039                 a.h -= rad;
3040                 a.t += 1;
3041             }
3042         break;
3043         case "sides":
3044             a.w = (o*2);
3045             a.l = -o;
3046             a.t = o-1;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= this.offset + rad;
3050                 a.l += 1;
3051                 a.w -= (this.offset - rad)*2;
3052                 a.w -= rad + 1;
3053                 a.h -= 1;
3054             }
3055         break;
3056         case "frame":
3057             a.w = a.h = (o*2);
3058             a.l = a.t = -o;
3059             a.t += 1;
3060             a.h -= 2;
3061             if(Roo.isIE){
3062                 a.l -= (this.offset - rad);
3063                 a.t -= (this.offset - rad);
3064                 a.l += 1;
3065                 a.w -= (this.offset + rad + 1);
3066                 a.h -= (this.offset + rad);
3067                 a.h += 1;
3068             }
3069         break;
3070     };
3071
3072     this.adjusts = a;
3073 };
3074
3075 Roo.Shadow.prototype = {
3076     /**
3077      * @cfg {String} mode
3078      * The shadow display mode.  Supports the following options:<br />
3079      * sides: Shadow displays on both sides and bottom only<br />
3080      * frame: Shadow displays equally on all four sides<br />
3081      * drop: Traditional bottom-right drop shadow (default)
3082      */
3083     mode: false,
3084     /**
3085      * @cfg {String} offset
3086      * The number of pixels to offset the shadow from the element (defaults to 4)
3087      */
3088     offset: 4,
3089
3090     // private
3091     defaultMode: "drop",
3092
3093     /**
3094      * Displays the shadow under the target element
3095      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3096      */
3097     show : function(target){
3098         target = Roo.get(target);
3099         if(!this.el){
3100             this.el = Roo.Shadow.Pool.pull();
3101             if(this.el.dom.nextSibling != target.dom){
3102                 this.el.insertBefore(target);
3103             }
3104         }
3105         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3106         if(Roo.isIE){
3107             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3108         }
3109         this.realign(
3110             target.getLeft(true),
3111             target.getTop(true),
3112             target.getWidth(),
3113             target.getHeight()
3114         );
3115         this.el.dom.style.display = "block";
3116     },
3117
3118     /**
3119      * Returns true if the shadow is visible, else false
3120      */
3121     isVisible : function(){
3122         return this.el ? true : false;  
3123     },
3124
3125     /**
3126      * Direct alignment when values are already available. Show must be called at least once before
3127      * calling this method to ensure it is initialized.
3128      * @param {Number} left The target element left position
3129      * @param {Number} top The target element top position
3130      * @param {Number} width The target element width
3131      * @param {Number} height The target element height
3132      */
3133     realign : function(l, t, w, h){
3134         if(!this.el){
3135             return;
3136         }
3137         var a = this.adjusts, d = this.el.dom, s = d.style;
3138         var iea = 0;
3139         s.left = (l+a.l)+"px";
3140         s.top = (t+a.t)+"px";
3141         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3142  
3143         if(s.width != sws || s.height != shs){
3144             s.width = sws;
3145             s.height = shs;
3146             if(!Roo.isIE){
3147                 var cn = d.childNodes;
3148                 var sww = Math.max(0, (sw-12))+"px";
3149                 cn[0].childNodes[1].style.width = sww;
3150                 cn[1].childNodes[1].style.width = sww;
3151                 cn[2].childNodes[1].style.width = sww;
3152                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3153             }
3154         }
3155     },
3156
3157     /**
3158      * Hides this shadow
3159      */
3160     hide : function(){
3161         if(this.el){
3162             this.el.dom.style.display = "none";
3163             Roo.Shadow.Pool.push(this.el);
3164             delete this.el;
3165         }
3166     },
3167
3168     /**
3169      * Adjust the z-index of this shadow
3170      * @param {Number} zindex The new z-index
3171      */
3172     setZIndex : function(z){
3173         this.zIndex = z;
3174         if(this.el){
3175             this.el.setStyle("z-index", z);
3176         }
3177     }
3178 };
3179
3180 // Private utility class that manages the internal Shadow cache
3181 Roo.Shadow.Pool = function(){
3182     var p = [];
3183     var markup = Roo.isIE ?
3184                  '<div class="x-ie-shadow"></div>' :
3185                  '<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>';
3186     return {
3187         pull : function(){
3188             var sh = p.shift();
3189             if(!sh){
3190                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3191                 sh.autoBoxAdjust = false;
3192             }
3193             return sh;
3194         },
3195
3196         push : function(sh){
3197             p.push(sh);
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210
3211
3212 /**
3213  * @class Roo.SplitBar
3214  * @extends Roo.util.Observable
3215  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3216  * <br><br>
3217  * Usage:
3218  * <pre><code>
3219 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3220                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3221 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3222 split.minSize = 100;
3223 split.maxSize = 600;
3224 split.animate = true;
3225 split.on('moved', splitterMoved);
3226 </code></pre>
3227  * @constructor
3228  * Create a new SplitBar
3229  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3230  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3231  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3232  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3233                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3234                         position of the SplitBar).
3235  */
3236 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3237     
3238     /** @private */
3239     this.el = Roo.get(dragElement, true);
3240     this.el.dom.unselectable = "on";
3241     /** @private */
3242     this.resizingEl = Roo.get(resizingElement, true);
3243
3244     /**
3245      * @private
3246      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3248      * @type Number
3249      */
3250     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3251     
3252     /**
3253      * The minimum size of the resizing element. (Defaults to 0)
3254      * @type Number
3255      */
3256     this.minSize = 0;
3257     
3258     /**
3259      * The maximum size of the resizing element. (Defaults to 2000)
3260      * @type Number
3261      */
3262     this.maxSize = 2000;
3263     
3264     /**
3265      * Whether to animate the transition to the new size
3266      * @type Boolean
3267      */
3268     this.animate = false;
3269     
3270     /**
3271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3272      * @type Boolean
3273      */
3274     this.useShim = false;
3275     
3276     /** @private */
3277     this.shim = null;
3278     
3279     if(!existingProxy){
3280         /** @private */
3281         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3282     }else{
3283         this.proxy = Roo.get(existingProxy).dom;
3284     }
3285     /** @private */
3286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3287     
3288     /** @private */
3289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3290     
3291     /** @private */
3292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3293     
3294     /** @private */
3295     this.dragSpecs = {};
3296     
3297     /**
3298      * @private The adapter to use to positon and resize elements
3299      */
3300     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3301     this.adapter.init(this);
3302     
3303     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3304         /** @private */
3305         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3306         this.el.addClass("x-splitbar-h");
3307     }else{
3308         /** @private */
3309         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3310         this.el.addClass("x-splitbar-v");
3311     }
3312     
3313     this.addEvents({
3314         /**
3315          * @event resize
3316          * Fires when the splitter is moved (alias for {@link #event-moved})
3317          * @param {Roo.SplitBar} this
3318          * @param {Number} newSize the new width or height
3319          */
3320         "resize" : true,
3321         /**
3322          * @event moved
3323          * Fires when the splitter is moved
3324          * @param {Roo.SplitBar} this
3325          * @param {Number} newSize the new width or height
3326          */
3327         "moved" : true,
3328         /**
3329          * @event beforeresize
3330          * Fires before the splitter is dragged
3331          * @param {Roo.SplitBar} this
3332          */
3333         "beforeresize" : true,
3334
3335         "beforeapply" : true
3336     });
3337
3338     Roo.util.Observable.call(this);
3339 };
3340
3341 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3342     onStartProxyDrag : function(x, y){
3343         this.fireEvent("beforeresize", this);
3344         if(!this.overlay){
3345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3346             o.unselectable();
3347             o.enableDisplayMode("block");
3348             // all splitbars share the same overlay
3349             Roo.SplitBar.prototype.overlay = o;
3350         }
3351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3352         this.overlay.show();
3353         Roo.get(this.proxy).setDisplayed("block");
3354         var size = this.adapter.getElementSize(this);
3355         this.activeMinSize = this.getMinimumSize();;
3356         this.activeMaxSize = this.getMaximumSize();;
3357         var c1 = size - this.activeMinSize;
3358         var c2 = Math.max(this.activeMaxSize - size, 0);
3359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3360             this.dd.resetConstraints();
3361             this.dd.setXConstraint(
3362                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3363                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3364             );
3365             this.dd.setYConstraint(0, 0);
3366         }else{
3367             this.dd.resetConstraints();
3368             this.dd.setXConstraint(0, 0);
3369             this.dd.setYConstraint(
3370                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3371                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3372             );
3373          }
3374         this.dragSpecs.startSize = size;
3375         this.dragSpecs.startPoint = [x, y];
3376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3377     },
3378     
3379     /** 
3380      * @private Called after the drag operation by the DDProxy
3381      */
3382     onEndProxyDrag : function(e){
3383         Roo.get(this.proxy).setDisplayed(false);
3384         var endPoint = Roo.lib.Event.getXY(e);
3385         if(this.overlay){
3386             this.overlay.hide();
3387         }
3388         var newSize;
3389         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3390             newSize = this.dragSpecs.startSize + 
3391                 (this.placement == Roo.SplitBar.LEFT ?
3392                     endPoint[0] - this.dragSpecs.startPoint[0] :
3393                     this.dragSpecs.startPoint[0] - endPoint[0]
3394                 );
3395         }else{
3396             newSize = this.dragSpecs.startSize + 
3397                 (this.placement == Roo.SplitBar.TOP ?
3398                     endPoint[1] - this.dragSpecs.startPoint[1] :
3399                     this.dragSpecs.startPoint[1] - endPoint[1]
3400                 );
3401         }
3402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3403         if(newSize != this.dragSpecs.startSize){
3404             if(this.fireEvent('beforeapply', this, newSize) !== false){
3405                 this.adapter.setElementSize(this, newSize);
3406                 this.fireEvent("moved", this, newSize);
3407                 this.fireEvent("resize", this, newSize);
3408             }
3409         }
3410     },
3411     
3412     /**
3413      * Get the adapter this SplitBar uses
3414      * @return The adapter object
3415      */
3416     getAdapter : function(){
3417         return this.adapter;
3418     },
3419     
3420     /**
3421      * Set the adapter this SplitBar uses
3422      * @param {Object} adapter A SplitBar adapter object
3423      */
3424     setAdapter : function(adapter){
3425         this.adapter = adapter;
3426         this.adapter.init(this);
3427     },
3428     
3429     /**
3430      * Gets the minimum size for the resizing element
3431      * @return {Number} The minimum size
3432      */
3433     getMinimumSize : function(){
3434         return this.minSize;
3435     },
3436     
3437     /**
3438      * Sets the minimum size for the resizing element
3439      * @param {Number} minSize The minimum size
3440      */
3441     setMinimumSize : function(minSize){
3442         this.minSize = minSize;
3443     },
3444     
3445     /**
3446      * Gets the maximum size for the resizing element
3447      * @return {Number} The maximum size
3448      */
3449     getMaximumSize : function(){
3450         return this.maxSize;
3451     },
3452     
3453     /**
3454      * Sets the maximum size for the resizing element
3455      * @param {Number} maxSize The maximum size
3456      */
3457     setMaximumSize : function(maxSize){
3458         this.maxSize = maxSize;
3459     },
3460     
3461     /**
3462      * Sets the initialize size for the resizing element
3463      * @param {Number} size The initial size
3464      */
3465     setCurrentSize : function(size){
3466         var oldAnimate = this.animate;
3467         this.animate = false;
3468         this.adapter.setElementSize(this, size);
3469         this.animate = oldAnimate;
3470     },
3471     
3472     /**
3473      * Destroy this splitbar. 
3474      * @param {Boolean} removeEl True to remove the element
3475      */
3476     destroy : function(removeEl){
3477         if(this.shim){
3478             this.shim.remove();
3479         }
3480         this.dd.unreg();
3481         this.proxy.parentNode.removeChild(this.proxy);
3482         if(removeEl){
3483             this.el.remove();
3484         }
3485     }
3486 });
3487
3488 /**
3489  * @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.
3490  */
3491 Roo.SplitBar.createProxy = function(dir){
3492     var proxy = new Roo.Element(document.createElement("div"));
3493     proxy.unselectable();
3494     var cls = 'x-splitbar-proxy';
3495     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3496     document.body.appendChild(proxy.dom);
3497     return proxy.dom;
3498 };
3499
3500 /** 
3501  * @class Roo.SplitBar.BasicLayoutAdapter
3502  * Default Adapter. It assumes the splitter and resizing element are not positioned
3503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3504  */
3505 Roo.SplitBar.BasicLayoutAdapter = function(){
3506 };
3507
3508 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3509     // do nothing for now
3510     init : function(s){
3511     
3512     },
3513     /**
3514      * Called before drag operations to get the current size of the resizing element. 
3515      * @param {Roo.SplitBar} s The SplitBar using this adapter
3516      */
3517      getElementSize : function(s){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             return s.resizingEl.getWidth();
3520         }else{
3521             return s.resizingEl.getHeight();
3522         }
3523     },
3524     
3525     /**
3526      * Called after drag operations to set the size of the resizing element.
3527      * @param {Roo.SplitBar} s The SplitBar using this adapter
3528      * @param {Number} newSize The new size to set
3529      * @param {Function} onComplete A function to be invoked when resizing is complete
3530      */
3531     setElementSize : function(s, newSize, onComplete){
3532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3533             if(!s.animate){
3534                 s.resizingEl.setWidth(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }else{
3542             
3543             if(!s.animate){
3544                 s.resizingEl.setHeight(newSize);
3545                 if(onComplete){
3546                     onComplete(s, newSize);
3547                 }
3548             }else{
3549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3550             }
3551         }
3552     }
3553 };
3554
3555 /** 
3556  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3557  * @extends Roo.SplitBar.BasicLayoutAdapter
3558  * Adapter that  moves the splitter element to align with the resized sizing element. 
3559  * Used with an absolute positioned SplitBar.
3560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3561  * document.body, make sure you assign an id to the body element.
3562  */
3563 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3564     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3565     this.container = Roo.get(container);
3566 };
3567
3568 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3569     init : function(s){
3570         this.basic.init(s);
3571     },
3572     
3573     getElementSize : function(s){
3574         return this.basic.getElementSize(s);
3575     },
3576     
3577     setElementSize : function(s, newSize, onComplete){
3578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3579     },
3580     
3581     moveSplitter : function(s){
3582         var yes = Roo.SplitBar;
3583         switch(s.placement){
3584             case yes.LEFT:
3585                 s.el.setX(s.resizingEl.getRight());
3586                 break;
3587             case yes.RIGHT:
3588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3589                 break;
3590             case yes.TOP:
3591                 s.el.setY(s.resizingEl.getBottom());
3592                 break;
3593             case yes.BOTTOM:
3594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3595                 break;
3596         }
3597     }
3598 };
3599
3600 /**
3601  * Orientation constant - Create a vertical SplitBar
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.VERTICAL = 1;
3606
3607 /**
3608  * Orientation constant - Create a horizontal SplitBar
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.HORIZONTAL = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is to the left of the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.LEFT = 1;
3620
3621 /**
3622  * Placement constant - The resizing element is to the right of the splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.RIGHT = 2;
3627
3628 /**
3629  * Placement constant - The resizing element is positioned above the splitter element
3630  * @static
3631  * @type Number
3632  */
3633 Roo.SplitBar.TOP = 3;
3634
3635 /**
3636  * Placement constant - The resizing element is positioned under splitter element
3637  * @static
3638  * @type Number
3639  */
3640 Roo.SplitBar.BOTTOM = 4;
3641 /*
3642  * Based on:
3643  * Ext JS Library 1.1.1
3644  * Copyright(c) 2006-2007, Ext JS, LLC.
3645  *
3646  * Originally Released Under LGPL - original licence link has changed is not relivant.
3647  *
3648  * Fork - LGPL
3649  * <script type="text/javascript">
3650  */
3651
3652 /**
3653  * @class Roo.View
3654  * @extends Roo.util.Observable
3655  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3656  * This class also supports single and multi selection modes. <br>
3657  * Create a data model bound view:
3658  <pre><code>
3659  var store = new Roo.data.Store(...);
3660
3661  var view = new Roo.View({
3662     el : "my-element",
3663     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3664  
3665     singleSelect: true,
3666     selectedClass: "ydataview-selected",
3667     store: store
3668  });
3669
3670  // listen for node click?
3671  view.on("click", function(vw, index, node, e){
3672  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3673  });
3674
3675  // load XML data
3676  dataModel.load("foobar.xml");
3677  </code></pre>
3678  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3679  * <br><br>
3680  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3681  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3682  * 
3683  * Note: old style constructor is still suported (container, template, config)
3684  * 
3685  * @constructor
3686  * Create a new View
3687  * @param {Object} config The config object
3688  * 
3689  */
3690 Roo.View = function(config, depreciated_tpl, depreciated_config){
3691     
3692     this.parent = false;
3693     
3694     if (typeof(depreciated_tpl) == 'undefined') {
3695         // new way.. - universal constructor.
3696         Roo.apply(this, config);
3697         this.el  = Roo.get(this.el);
3698     } else {
3699         // old format..
3700         this.el  = Roo.get(config);
3701         this.tpl = depreciated_tpl;
3702         Roo.apply(this, depreciated_config);
3703     }
3704     this.wrapEl  = this.el.wrap().wrap();
3705     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3706     
3707     
3708     if(typeof(this.tpl) == "string"){
3709         this.tpl = new Roo.Template(this.tpl);
3710     } else {
3711         // support xtype ctors..
3712         this.tpl = new Roo.factory(this.tpl, Roo);
3713     }
3714     
3715     
3716     this.tpl.compile();
3717     
3718     /** @private */
3719     this.addEvents({
3720         /**
3721          * @event beforeclick
3722          * Fires before a click is processed. Returns false to cancel the default action.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "beforeclick" : true,
3729         /**
3730          * @event click
3731          * Fires when a template node is clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "click" : true,
3738         /**
3739          * @event dblclick
3740          * Fires when a template node is double clicked.
3741          * @param {Roo.View} this
3742          * @param {Number} index The index of the target node
3743          * @param {HTMLElement} node The target node
3744          * @param {Roo.EventObject} e The raw event object
3745          */
3746             "dblclick" : true,
3747         /**
3748          * @event contextmenu
3749          * Fires when a template node is right clicked.
3750          * @param {Roo.View} this
3751          * @param {Number} index The index of the target node
3752          * @param {HTMLElement} node The target node
3753          * @param {Roo.EventObject} e The raw event object
3754          */
3755             "contextmenu" : true,
3756         /**
3757          * @event selectionchange
3758          * Fires when the selected nodes change.
3759          * @param {Roo.View} this
3760          * @param {Array} selections Array of the selected nodes
3761          */
3762             "selectionchange" : true,
3763     
3764         /**
3765          * @event beforeselect
3766          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3767          * @param {Roo.View} this
3768          * @param {HTMLElement} node The node to be selected
3769          * @param {Array} selections Array of currently selected nodes
3770          */
3771             "beforeselect" : true,
3772         /**
3773          * @event preparedata
3774          * Fires on every row to render, to allow you to change the data.
3775          * @param {Roo.View} this
3776          * @param {Object} data to be rendered (change this)
3777          */
3778           "preparedata" : true
3779           
3780           
3781         });
3782
3783
3784
3785     this.el.on({
3786         "click": this.onClick,
3787         "dblclick": this.onDblClick,
3788         "contextmenu": this.onContextMenu,
3789         scope:this
3790     });
3791
3792     this.selections = [];
3793     this.nodes = [];
3794     this.cmp = new Roo.CompositeElementLite([]);
3795     if(this.store){
3796         this.store = Roo.factory(this.store, Roo.data);
3797         this.setStore(this.store, true);
3798     }
3799     
3800     if ( this.footer && this.footer.xtype) {
3801            
3802          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3803         
3804         this.footer.dataSource = this.store;
3805         this.footer.container = fctr;
3806         this.footer = Roo.factory(this.footer, Roo);
3807         fctr.insertFirst(this.el);
3808         
3809         // this is a bit insane - as the paging toolbar seems to detach the el..
3810 //        dom.parentNode.parentNode.parentNode
3811          // they get detached?
3812     }
3813     
3814     
3815     Roo.View.superclass.constructor.call(this);
3816     
3817     
3818 };
3819
3820 Roo.extend(Roo.View, Roo.util.Observable, {
3821     
3822      /**
3823      * @cfg {Roo.data.Store} store Data store to load data from.
3824      */
3825     store : false,
3826     
3827     /**
3828      * @cfg {String|Roo.Element} el The container element.
3829      */
3830     el : '',
3831     
3832     /**
3833      * @cfg {String|Roo.Template} tpl The template used by this View 
3834      */
3835     tpl : false,
3836     /**
3837      * @cfg {String} dataName the named area of the template to use as the data area
3838      *                          Works with domtemplates roo-name="name"
3839      */
3840     dataName: false,
3841     /**
3842      * @cfg {String} selectedClass The css class to add to selected nodes
3843      */
3844     selectedClass : "x-view-selected",
3845      /**
3846      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3847      */
3848     emptyText : "",
3849     
3850     /**
3851      * @cfg {String} text to display on mask (default Loading)
3852      */
3853     mask : false,
3854     /**
3855      * @cfg {Boolean} multiSelect Allow multiple selection
3856      */
3857     multiSelect : false,
3858     /**
3859      * @cfg {Boolean} singleSelect Allow single selection
3860      */
3861     singleSelect:  false,
3862     
3863     /**
3864      * @cfg {Boolean} toggleSelect - selecting 
3865      */
3866     toggleSelect : false,
3867     
3868     /**
3869      * @cfg {Boolean} tickable - selecting 
3870      */
3871     tickable : false,
3872     
3873     /**
3874      * Returns the element this view is bound to.
3875      * @return {Roo.Element}
3876      */
3877     getEl : function(){
3878         return this.wrapEl;
3879     },
3880     
3881     
3882
3883     /**
3884      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3885      */
3886     refresh : function(){
3887         //Roo.log('refresh');
3888         var t = this.tpl;
3889         
3890         // if we are using something like 'domtemplate', then
3891         // the what gets used is:
3892         // t.applySubtemplate(NAME, data, wrapping data..)
3893         // the outer template then get' applied with
3894         //     the store 'extra data'
3895         // and the body get's added to the
3896         //      roo-name="data" node?
3897         //      <span class='roo-tpl-{name}'></span> ?????
3898         
3899         
3900         
3901         this.clearSelections();
3902         this.el.update("");
3903         var html = [];
3904         var records = this.store.getRange();
3905         if(records.length < 1) {
3906             
3907             // is this valid??  = should it render a template??
3908             
3909             this.el.update(this.emptyText);
3910             return;
3911         }
3912         var el = this.el;
3913         if (this.dataName) {
3914             this.el.update(t.apply(this.store.meta)); //????
3915             el = this.el.child('.roo-tpl-' + this.dataName);
3916         }
3917         
3918         for(var i = 0, len = records.length; i < len; i++){
3919             var data = this.prepareData(records[i].data, i, records[i]);
3920             this.fireEvent("preparedata", this, data, i, records[i]);
3921             
3922             var d = Roo.apply({}, data);
3923             
3924             if(this.tickable){
3925                 Roo.apply(d, {'roo-id' : Roo.id()});
3926                 
3927                 var _this = this;
3928             
3929                 Roo.each(this.parent.item, function(item){
3930                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3931                         return;
3932                     }
3933                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3934                 });
3935             }
3936             
3937             html[html.length] = Roo.util.Format.trim(
3938                 this.dataName ?
3939                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3940                     t.apply(d)
3941             );
3942         }
3943         
3944         
3945         
3946         el.update(html.join(""));
3947         this.nodes = el.dom.childNodes;
3948         this.updateIndexes(0);
3949     },
3950     
3951
3952     /**
3953      * Function to override to reformat the data that is sent to
3954      * the template for each node.
3955      * DEPRICATED - use the preparedata event handler.
3956      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3957      * a JSON object for an UpdateManager bound view).
3958      */
3959     prepareData : function(data, index, record)
3960     {
3961         this.fireEvent("preparedata", this, data, index, record);
3962         return data;
3963     },
3964
3965     onUpdate : function(ds, record){
3966         // Roo.log('on update');   
3967         this.clearSelections();
3968         var index = this.store.indexOf(record);
3969         var n = this.nodes[index];
3970         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3971         n.parentNode.removeChild(n);
3972         this.updateIndexes(index, index);
3973     },
3974
3975     
3976     
3977 // --------- FIXME     
3978     onAdd : function(ds, records, index)
3979     {
3980         //Roo.log(['on Add', ds, records, index] );        
3981         this.clearSelections();
3982         if(this.nodes.length == 0){
3983             this.refresh();
3984             return;
3985         }
3986         var n = this.nodes[index];
3987         for(var i = 0, len = records.length; i < len; i++){
3988             var d = this.prepareData(records[i].data, i, records[i]);
3989             if(n){
3990                 this.tpl.insertBefore(n, d);
3991             }else{
3992                 
3993                 this.tpl.append(this.el, d);
3994             }
3995         }
3996         this.updateIndexes(index);
3997     },
3998
3999     onRemove : function(ds, record, index){
4000        // Roo.log('onRemove');
4001         this.clearSelections();
4002         var el = this.dataName  ?
4003             this.el.child('.roo-tpl-' + this.dataName) :
4004             this.el; 
4005         
4006         el.dom.removeChild(this.nodes[index]);
4007         this.updateIndexes(index);
4008     },
4009
4010     /**
4011      * Refresh an individual node.
4012      * @param {Number} index
4013      */
4014     refreshNode : function(index){
4015         this.onUpdate(this.store, this.store.getAt(index));
4016     },
4017
4018     updateIndexes : function(startIndex, endIndex){
4019         var ns = this.nodes;
4020         startIndex = startIndex || 0;
4021         endIndex = endIndex || ns.length - 1;
4022         for(var i = startIndex; i <= endIndex; i++){
4023             ns[i].nodeIndex = i;
4024         }
4025     },
4026
4027     /**
4028      * Changes the data store this view uses and refresh the view.
4029      * @param {Store} store
4030      */
4031     setStore : function(store, initial){
4032         if(!initial && this.store){
4033             this.store.un("datachanged", this.refresh);
4034             this.store.un("add", this.onAdd);
4035             this.store.un("remove", this.onRemove);
4036             this.store.un("update", this.onUpdate);
4037             this.store.un("clear", this.refresh);
4038             this.store.un("beforeload", this.onBeforeLoad);
4039             this.store.un("load", this.onLoad);
4040             this.store.un("loadexception", this.onLoad);
4041         }
4042         if(store){
4043           
4044             store.on("datachanged", this.refresh, this);
4045             store.on("add", this.onAdd, this);
4046             store.on("remove", this.onRemove, this);
4047             store.on("update", this.onUpdate, this);
4048             store.on("clear", this.refresh, this);
4049             store.on("beforeload", this.onBeforeLoad, this);
4050             store.on("load", this.onLoad, this);
4051             store.on("loadexception", this.onLoad, this);
4052         }
4053         
4054         if(store){
4055             this.refresh();
4056         }
4057     },
4058     /**
4059      * onbeforeLoad - masks the loading area.
4060      *
4061      */
4062     onBeforeLoad : function(store,opts)
4063     {
4064          //Roo.log('onBeforeLoad');   
4065         if (!opts.add) {
4066             this.el.update("");
4067         }
4068         this.el.mask(this.mask ? this.mask : "Loading" ); 
4069     },
4070     onLoad : function ()
4071     {
4072         this.el.unmask();
4073     },
4074     
4075
4076     /**
4077      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4078      * @param {HTMLElement} node
4079      * @return {HTMLElement} The template node
4080      */
4081     findItemFromChild : function(node){
4082         var el = this.dataName  ?
4083             this.el.child('.roo-tpl-' + this.dataName,true) :
4084             this.el.dom; 
4085         
4086         if(!node || node.parentNode == el){
4087                     return node;
4088             }
4089             var p = node.parentNode;
4090             while(p && p != el){
4091             if(p.parentNode == el){
4092                 return p;
4093             }
4094             p = p.parentNode;
4095         }
4096             return null;
4097     },
4098
4099     /** @ignore */
4100     onClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             var index = this.indexOf(item);
4104             if(this.onItemClick(item, index, e) !== false){
4105                 this.fireEvent("click", this, index, item, e);
4106             }
4107         }else{
4108             this.clearSelections();
4109         }
4110     },
4111
4112     /** @ignore */
4113     onContextMenu : function(e){
4114         var item = this.findItemFromChild(e.getTarget());
4115         if(item){
4116             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4117         }
4118     },
4119
4120     /** @ignore */
4121     onDblClick : function(e){
4122         var item = this.findItemFromChild(e.getTarget());
4123         if(item){
4124             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4125         }
4126     },
4127
4128     onItemClick : function(item, index, e)
4129     {
4130         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4131             return false;
4132         }
4133         if (this.toggleSelect) {
4134             var m = this.isSelected(item) ? 'unselect' : 'select';
4135             //Roo.log(m);
4136             var _t = this;
4137             _t[m](item, true, false);
4138             return true;
4139         }
4140         if(this.multiSelect || this.singleSelect){
4141             if(this.multiSelect && e.shiftKey && this.lastSelection){
4142                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4143             }else{
4144                 this.select(item, this.multiSelect && e.ctrlKey);
4145                 this.lastSelection = item;
4146             }
4147             
4148             if(!this.tickable){
4149                 e.preventDefault();
4150             }
4151             
4152         }
4153         return true;
4154     },
4155
4156     /**
4157      * Get the number of selected nodes.
4158      * @return {Number}
4159      */
4160     getSelectionCount : function(){
4161         return this.selections.length;
4162     },
4163
4164     /**
4165      * Get the currently selected nodes.
4166      * @return {Array} An array of HTMLElements
4167      */
4168     getSelectedNodes : function(){
4169         return this.selections;
4170     },
4171
4172     /**
4173      * Get the indexes of the selected nodes.
4174      * @return {Array}
4175      */
4176     getSelectedIndexes : function(){
4177         var indexes = [], s = this.selections;
4178         for(var i = 0, len = s.length; i < len; i++){
4179             indexes.push(s[i].nodeIndex);
4180         }
4181         return indexes;
4182     },
4183
4184     /**
4185      * Clear all selections
4186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4187      */
4188     clearSelections : function(suppressEvent){
4189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4190             this.cmp.elements = this.selections;
4191             this.cmp.removeClass(this.selectedClass);
4192             this.selections = [];
4193             if(!suppressEvent){
4194                 this.fireEvent("selectionchange", this, this.selections);
4195             }
4196         }
4197     },
4198
4199     /**
4200      * Returns true if the passed node is selected
4201      * @param {HTMLElement/Number} node The node or node index
4202      * @return {Boolean}
4203      */
4204     isSelected : function(node){
4205         var s = this.selections;
4206         if(s.length < 1){
4207             return false;
4208         }
4209         node = this.getNode(node);
4210         return s.indexOf(node) !== -1;
4211     },
4212
4213     /**
4214      * Selects nodes.
4215      * @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
4216      * @param {Boolean} keepExisting (optional) true to keep existing selections
4217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4218      */
4219     select : function(nodeInfo, keepExisting, suppressEvent){
4220         if(nodeInfo instanceof Array){
4221             if(!keepExisting){
4222                 this.clearSelections(true);
4223             }
4224             for(var i = 0, len = nodeInfo.length; i < len; i++){
4225                 this.select(nodeInfo[i], true, true);
4226             }
4227             return;
4228         } 
4229         var node = this.getNode(nodeInfo);
4230         if(!node || this.isSelected(node)){
4231             return; // already selected.
4232         }
4233         if(!keepExisting){
4234             this.clearSelections(true);
4235         }
4236         
4237         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4238             Roo.fly(node).addClass(this.selectedClass);
4239             this.selections.push(node);
4240             if(!suppressEvent){
4241                 this.fireEvent("selectionchange", this, this.selections);
4242             }
4243         }
4244         
4245         
4246     },
4247       /**
4248      * Unselects nodes.
4249      * @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
4250      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4251      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4252      */
4253     unselect : function(nodeInfo, keepExisting, suppressEvent)
4254     {
4255         if(nodeInfo instanceof Array){
4256             Roo.each(this.selections, function(s) {
4257                 this.unselect(s, nodeInfo);
4258             }, this);
4259             return;
4260         }
4261         var node = this.getNode(nodeInfo);
4262         if(!node || !this.isSelected(node)){
4263             //Roo.log("not selected");
4264             return; // not selected.
4265         }
4266         // fireevent???
4267         var ns = [];
4268         Roo.each(this.selections, function(s) {
4269             if (s == node ) {
4270                 Roo.fly(node).removeClass(this.selectedClass);
4271
4272                 return;
4273             }
4274             ns.push(s);
4275         },this);
4276         
4277         this.selections= ns;
4278         this.fireEvent("selectionchange", this, this.selections);
4279     },
4280
4281     /**
4282      * Gets a template node.
4283      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4284      * @return {HTMLElement} The node or null if it wasn't found
4285      */
4286     getNode : function(nodeInfo){
4287         if(typeof nodeInfo == "string"){
4288             return document.getElementById(nodeInfo);
4289         }else if(typeof nodeInfo == "number"){
4290             return this.nodes[nodeInfo];
4291         }
4292         return nodeInfo;
4293     },
4294
4295     /**
4296      * Gets a range template nodes.
4297      * @param {Number} startIndex
4298      * @param {Number} endIndex
4299      * @return {Array} An array of nodes
4300      */
4301     getNodes : function(start, end){
4302         var ns = this.nodes;
4303         start = start || 0;
4304         end = typeof end == "undefined" ? ns.length - 1 : end;
4305         var nodes = [];
4306         if(start <= end){
4307             for(var i = start; i <= end; i++){
4308                 nodes.push(ns[i]);
4309             }
4310         } else{
4311             for(var i = start; i >= end; i--){
4312                 nodes.push(ns[i]);
4313             }
4314         }
4315         return nodes;
4316     },
4317
4318     /**
4319      * Finds the index of the passed node
4320      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4321      * @return {Number} The index of the node or -1
4322      */
4323     indexOf : function(node){
4324         node = this.getNode(node);
4325         if(typeof node.nodeIndex == "number"){
4326             return node.nodeIndex;
4327         }
4328         var ns = this.nodes;
4329         for(var i = 0, len = ns.length; i < len; i++){
4330             if(ns[i] == node){
4331                 return i;
4332             }
4333         }
4334         return -1;
4335     }
4336 });
4337 /*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348 /**
4349  * @class Roo.JsonView
4350  * @extends Roo.View
4351  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4352 <pre><code>
4353 var view = new Roo.JsonView({
4354     container: "my-element",
4355     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4356     multiSelect: true, 
4357     jsonRoot: "data" 
4358 });
4359
4360 // listen for node click?
4361 view.on("click", function(vw, index, node, e){
4362     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4363 });
4364
4365 // direct load of JSON data
4366 view.load("foobar.php");
4367
4368 // Example from my blog list
4369 var tpl = new Roo.Template(
4370     '&lt;div class="entry"&gt;' +
4371     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4372     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4373     "&lt;/div&gt;&lt;hr /&gt;"
4374 );
4375
4376 var moreView = new Roo.JsonView({
4377     container :  "entry-list", 
4378     template : tpl,
4379     jsonRoot: "posts"
4380 });
4381 moreView.on("beforerender", this.sortEntries, this);
4382 moreView.load({
4383     url: "/blog/get-posts.php",
4384     params: "allposts=true",
4385     text: "Loading Blog Entries..."
4386 });
4387 </code></pre>
4388
4389 * Note: old code is supported with arguments : (container, template, config)
4390
4391
4392  * @constructor
4393  * Create a new JsonView
4394  * 
4395  * @param {Object} config The config object
4396  * 
4397  */
4398 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4399     
4400     
4401     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4402
4403     var um = this.el.getUpdateManager();
4404     um.setRenderer(this);
4405     um.on("update", this.onLoad, this);
4406     um.on("failure", this.onLoadException, this);
4407
4408     /**
4409      * @event beforerender
4410      * Fires before rendering of the downloaded JSON data.
4411      * @param {Roo.JsonView} this
4412      * @param {Object} data The JSON data loaded
4413      */
4414     /**
4415      * @event load
4416      * Fires when data is loaded.
4417      * @param {Roo.JsonView} this
4418      * @param {Object} data The JSON data loaded
4419      * @param {Object} response The raw Connect response object
4420      */
4421     /**
4422      * @event loadexception
4423      * Fires when loading fails.
4424      * @param {Roo.JsonView} this
4425      * @param {Object} response The raw Connect response object
4426      */
4427     this.addEvents({
4428         'beforerender' : true,
4429         'load' : true,
4430         'loadexception' : true
4431     });
4432 };
4433 Roo.extend(Roo.JsonView, Roo.View, {
4434     /**
4435      * @type {String} The root property in the loaded JSON object that contains the data
4436      */
4437     jsonRoot : "",
4438
4439     /**
4440      * Refreshes the view.
4441      */
4442     refresh : function(){
4443         this.clearSelections();
4444         this.el.update("");
4445         var html = [];
4446         var o = this.jsonData;
4447         if(o && o.length > 0){
4448             for(var i = 0, len = o.length; i < len; i++){
4449                 var data = this.prepareData(o[i], i, o);
4450                 html[html.length] = this.tpl.apply(data);
4451             }
4452         }else{
4453             html.push(this.emptyText);
4454         }
4455         this.el.update(html.join(""));
4456         this.nodes = this.el.dom.childNodes;
4457         this.updateIndexes(0);
4458     },
4459
4460     /**
4461      * 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.
4462      * @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:
4463      <pre><code>
4464      view.load({
4465          url: "your-url.php",
4466          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4467          callback: yourFunction,
4468          scope: yourObject, //(optional scope)
4469          discardUrl: false,
4470          nocache: false,
4471          text: "Loading...",
4472          timeout: 30,
4473          scripts: false
4474      });
4475      </code></pre>
4476      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4477      * 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.
4478      * @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}
4479      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4480      * @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.
4481      */
4482     load : function(){
4483         var um = this.el.getUpdateManager();
4484         um.update.apply(um, arguments);
4485     },
4486
4487     // note - render is a standard framework call...
4488     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4489     render : function(el, response){
4490         
4491         this.clearSelections();
4492         this.el.update("");
4493         var o;
4494         try{
4495             if (response != '') {
4496                 o = Roo.util.JSON.decode(response.responseText);
4497                 if(this.jsonRoot){
4498                     
4499                     o = o[this.jsonRoot];
4500                 }
4501             }
4502         } catch(e){
4503         }
4504         /**
4505          * The current JSON data or null
4506          */
4507         this.jsonData = o;
4508         this.beforeRender();
4509         this.refresh();
4510     },
4511
4512 /**
4513  * Get the number of records in the current JSON dataset
4514  * @return {Number}
4515  */
4516     getCount : function(){
4517         return this.jsonData ? this.jsonData.length : 0;
4518     },
4519
4520 /**
4521  * Returns the JSON object for the specified node(s)
4522  * @param {HTMLElement/Array} node The node or an array of nodes
4523  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4524  * you get the JSON object for the node
4525  */
4526     getNodeData : function(node){
4527         if(node instanceof Array){
4528             var data = [];
4529             for(var i = 0, len = node.length; i < len; i++){
4530                 data.push(this.getNodeData(node[i]));
4531             }
4532             return data;
4533         }
4534         return this.jsonData[this.indexOf(node)] || null;
4535     },
4536
4537     beforeRender : function(){
4538         this.snapshot = this.jsonData;
4539         if(this.sortInfo){
4540             this.sort.apply(this, this.sortInfo);
4541         }
4542         this.fireEvent("beforerender", this, this.jsonData);
4543     },
4544
4545     onLoad : function(el, o){
4546         this.fireEvent("load", this, this.jsonData, o);
4547     },
4548
4549     onLoadException : function(el, o){
4550         this.fireEvent("loadexception", this, o);
4551     },
4552
4553 /**
4554  * Filter the data by a specific property.
4555  * @param {String} property A property on your JSON objects
4556  * @param {String/RegExp} value Either string that the property values
4557  * should start with, or a RegExp to test against the property
4558  */
4559     filter : function(property, value){
4560         if(this.jsonData){
4561             var data = [];
4562             var ss = this.snapshot;
4563             if(typeof value == "string"){
4564                 var vlen = value.length;
4565                 if(vlen == 0){
4566                     this.clearFilter();
4567                     return;
4568                 }
4569                 value = value.toLowerCase();
4570                 for(var i = 0, len = ss.length; i < len; i++){
4571                     var o = ss[i];
4572                     if(o[property].substr(0, vlen).toLowerCase() == value){
4573                         data.push(o);
4574                     }
4575                 }
4576             } else if(value.exec){ // regex?
4577                 for(var i = 0, len = ss.length; i < len; i++){
4578                     var o = ss[i];
4579                     if(value.test(o[property])){
4580                         data.push(o);
4581                     }
4582                 }
4583             } else{
4584                 return;
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Filter by a function. The passed function will be called with each
4593  * object in the current dataset. If the function returns true the value is kept,
4594  * otherwise it is filtered.
4595  * @param {Function} fn
4596  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4597  */
4598     filterBy : function(fn, scope){
4599         if(this.jsonData){
4600             var data = [];
4601             var ss = this.snapshot;
4602             for(var i = 0, len = ss.length; i < len; i++){
4603                 var o = ss[i];
4604                 if(fn.call(scope || this, o)){
4605                     data.push(o);
4606                 }
4607             }
4608             this.jsonData = data;
4609             this.refresh();
4610         }
4611     },
4612
4613 /**
4614  * Clears the current filter.
4615  */
4616     clearFilter : function(){
4617         if(this.snapshot && this.jsonData != this.snapshot){
4618             this.jsonData = this.snapshot;
4619             this.refresh();
4620         }
4621     },
4622
4623
4624 /**
4625  * Sorts the data for this view and refreshes it.
4626  * @param {String} property A property on your JSON objects to sort on
4627  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4628  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4629  */
4630     sort : function(property, dir, sortType){
4631         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4632         if(this.jsonData){
4633             var p = property;
4634             var dsc = dir && dir.toLowerCase() == "desc";
4635             var f = function(o1, o2){
4636                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4637                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4638                 ;
4639                 if(v1 < v2){
4640                     return dsc ? +1 : -1;
4641                 } else if(v1 > v2){
4642                     return dsc ? -1 : +1;
4643                 } else{
4644                     return 0;
4645                 }
4646             };
4647             this.jsonData.sort(f);
4648             this.refresh();
4649             if(this.jsonData != this.snapshot){
4650                 this.snapshot.sort(f);
4651             }
4652         }
4653     }
4654 });/*
4655  * Based on:
4656  * Ext JS Library 1.1.1
4657  * Copyright(c) 2006-2007, Ext JS, LLC.
4658  *
4659  * Originally Released Under LGPL - original licence link has changed is not relivant.
4660  *
4661  * Fork - LGPL
4662  * <script type="text/javascript">
4663  */
4664  
4665
4666 /**
4667  * @class Roo.ColorPalette
4668  * @extends Roo.Component
4669  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4670  * Here's an example of typical usage:
4671  * <pre><code>
4672 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4673 cp.render('my-div');
4674
4675 cp.on('select', function(palette, selColor){
4676     // do something with selColor
4677 });
4678 </code></pre>
4679  * @constructor
4680  * Create a new ColorPalette
4681  * @param {Object} config The config object
4682  */
4683 Roo.ColorPalette = function(config){
4684     Roo.ColorPalette.superclass.constructor.call(this, config);
4685     this.addEvents({
4686         /**
4687              * @event select
4688              * Fires when a color is selected
4689              * @param {ColorPalette} this
4690              * @param {String} color The 6-digit color hex code (without the # symbol)
4691              */
4692         select: true
4693     });
4694
4695     if(this.handler){
4696         this.on("select", this.handler, this.scope, true);
4697     }
4698 };
4699 Roo.extend(Roo.ColorPalette, Roo.Component, {
4700     /**
4701      * @cfg {String} itemCls
4702      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4703      */
4704     itemCls : "x-color-palette",
4705     /**
4706      * @cfg {String} value
4707      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4708      * the hex codes are case-sensitive.
4709      */
4710     value : null,
4711     clickEvent:'click',
4712     // private
4713     ctype: "Roo.ColorPalette",
4714
4715     /**
4716      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4717      */
4718     allowReselect : false,
4719
4720     /**
4721      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4722      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4723      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4724      * of colors with the width setting until the box is symmetrical.</p>
4725      * <p>You can override individual colors if needed:</p>
4726      * <pre><code>
4727 var cp = new Roo.ColorPalette();
4728 cp.colors[0] = "FF0000";  // change the first box to red
4729 </code></pre>
4730
4731 Or you can provide a custom array of your own for complete control:
4732 <pre><code>
4733 var cp = new Roo.ColorPalette();
4734 cp.colors = ["000000", "993300", "333300"];
4735 </code></pre>
4736      * @type Array
4737      */
4738     colors : [
4739         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4740         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4741         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4742         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4743         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4744     ],
4745
4746     // private
4747     onRender : function(container, position){
4748         var t = new Roo.MasterTemplate(
4749             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4750         );
4751         var c = this.colors;
4752         for(var i = 0, len = c.length; i < len; i++){
4753             t.add([c[i]]);
4754         }
4755         var el = document.createElement("div");
4756         el.className = this.itemCls;
4757         t.overwrite(el);
4758         container.dom.insertBefore(el, position);
4759         this.el = Roo.get(el);
4760         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4761         if(this.clickEvent != 'click'){
4762             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4763         }
4764     },
4765
4766     // private
4767     afterRender : function(){
4768         Roo.ColorPalette.superclass.afterRender.call(this);
4769         if(this.value){
4770             var s = this.value;
4771             this.value = null;
4772             this.select(s);
4773         }
4774     },
4775
4776     // private
4777     handleClick : function(e, t){
4778         e.preventDefault();
4779         if(!this.disabled){
4780             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4781             this.select(c.toUpperCase());
4782         }
4783     },
4784
4785     /**
4786      * Selects the specified color in the palette (fires the select event)
4787      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4788      */
4789     select : function(color){
4790         color = color.replace("#", "");
4791         if(color != this.value || this.allowReselect){
4792             var el = this.el;
4793             if(this.value){
4794                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4795             }
4796             el.child("a.color-"+color).addClass("x-color-palette-sel");
4797             this.value = color;
4798             this.fireEvent("select", this, color);
4799         }
4800     }
4801 });/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.DatePicker
4814  * @extends Roo.Component
4815  * Simple date picker class.
4816  * @constructor
4817  * Create a new DatePicker
4818  * @param {Object} config The config object
4819  */
4820 Roo.DatePicker = function(config){
4821     Roo.DatePicker.superclass.constructor.call(this, config);
4822
4823     this.value = config && config.value ?
4824                  config.value.clearTime() : new Date().clearTime();
4825
4826     this.addEvents({
4827         /**
4828              * @event select
4829              * Fires when a date is selected
4830              * @param {DatePicker} this
4831              * @param {Date} date The selected date
4832              */
4833         'select': true,
4834         /**
4835              * @event monthchange
4836              * Fires when the displayed month changes 
4837              * @param {DatePicker} this
4838              * @param {Date} date The selected month
4839              */
4840         'monthchange': true
4841     });
4842
4843     if(this.handler){
4844         this.on("select", this.handler,  this.scope || this);
4845     }
4846     // build the disabledDatesRE
4847     if(!this.disabledDatesRE && this.disabledDates){
4848         var dd = this.disabledDates;
4849         var re = "(?:";
4850         for(var i = 0; i < dd.length; i++){
4851             re += dd[i];
4852             if(i != dd.length-1) {
4853                 re += "|";
4854             }
4855         }
4856         this.disabledDatesRE = new RegExp(re + ")");
4857     }
4858 };
4859
4860 Roo.extend(Roo.DatePicker, Roo.Component, {
4861     /**
4862      * @cfg {String} todayText
4863      * The text to display on the button that selects the current date (defaults to "Today")
4864      */
4865     todayText : "Today",
4866     /**
4867      * @cfg {String} okText
4868      * The text to display on the ok button
4869      */
4870     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4871     /**
4872      * @cfg {String} cancelText
4873      * The text to display on the cancel button
4874      */
4875     cancelText : "Cancel",
4876     /**
4877      * @cfg {String} todayTip
4878      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4879      */
4880     todayTip : "{0} (Spacebar)",
4881     /**
4882      * @cfg {Date} minDate
4883      * Minimum allowable date (JavaScript date object, defaults to null)
4884      */
4885     minDate : null,
4886     /**
4887      * @cfg {Date} maxDate
4888      * Maximum allowable date (JavaScript date object, defaults to null)
4889      */
4890     maxDate : null,
4891     /**
4892      * @cfg {String} minText
4893      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4894      */
4895     minText : "This date is before the minimum date",
4896     /**
4897      * @cfg {String} maxText
4898      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4899      */
4900     maxText : "This date is after the maximum date",
4901     /**
4902      * @cfg {String} format
4903      * The default date format string which can be overriden for localization support.  The format must be
4904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4905      */
4906     format : "m/d/y",
4907     /**
4908      * @cfg {Array} disabledDays
4909      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4910      */
4911     disabledDays : null,
4912     /**
4913      * @cfg {String} disabledDaysText
4914      * The tooltip to display when the date falls on a disabled day (defaults to "")
4915      */
4916     disabledDaysText : "",
4917     /**
4918      * @cfg {RegExp} disabledDatesRE
4919      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4920      */
4921     disabledDatesRE : null,
4922     /**
4923      * @cfg {String} disabledDatesText
4924      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4925      */
4926     disabledDatesText : "",
4927     /**
4928      * @cfg {Boolean} constrainToViewport
4929      * True to constrain the date picker to the viewport (defaults to true)
4930      */
4931     constrainToViewport : true,
4932     /**
4933      * @cfg {Array} monthNames
4934      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4935      */
4936     monthNames : Date.monthNames,
4937     /**
4938      * @cfg {Array} dayNames
4939      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4940      */
4941     dayNames : Date.dayNames,
4942     /**
4943      * @cfg {String} nextText
4944      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4945      */
4946     nextText: 'Next Month (Control+Right)',
4947     /**
4948      * @cfg {String} prevText
4949      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4950      */
4951     prevText: 'Previous Month (Control+Left)',
4952     /**
4953      * @cfg {String} monthYearText
4954      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4955      */
4956     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4957     /**
4958      * @cfg {Number} startDay
4959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4960      */
4961     startDay : 0,
4962     /**
4963      * @cfg {Bool} showClear
4964      * Show a clear button (usefull for date form elements that can be blank.)
4965      */
4966     
4967     showClear: false,
4968     
4969     /**
4970      * Sets the value of the date field
4971      * @param {Date} value The date to set
4972      */
4973     setValue : function(value){
4974         var old = this.value;
4975         
4976         if (typeof(value) == 'string') {
4977          
4978             value = Date.parseDate(value, this.format);
4979         }
4980         if (!value) {
4981             value = new Date();
4982         }
4983         
4984         this.value = value.clearTime(true);
4985         if(this.el){
4986             this.update(this.value);
4987         }
4988     },
4989
4990     /**
4991      * Gets the current selected value of the date field
4992      * @return {Date} The selected date
4993      */
4994     getValue : function(){
4995         return this.value;
4996     },
4997
4998     // private
4999     focus : function(){
5000         if(this.el){
5001             this.update(this.activeDate);
5002         }
5003     },
5004
5005     // privateval
5006     onRender : function(container, position){
5007         
5008         var m = [
5009              '<table cellspacing="0">',
5010                 '<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>',
5011                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5012         var dn = this.dayNames;
5013         for(var i = 0; i < 7; i++){
5014             var d = this.startDay+i;
5015             if(d > 6){
5016                 d = d-7;
5017             }
5018             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5019         }
5020         m[m.length] = "</tr></thead><tbody><tr>";
5021         for(var i = 0; i < 42; i++) {
5022             if(i % 7 == 0 && i != 0){
5023                 m[m.length] = "</tr><tr>";
5024             }
5025             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5026         }
5027         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5028             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5029
5030         var el = document.createElement("div");
5031         el.className = "x-date-picker";
5032         el.innerHTML = m.join("");
5033
5034         container.dom.insertBefore(el, position);
5035
5036         this.el = Roo.get(el);
5037         this.eventEl = Roo.get(el.firstChild);
5038
5039         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5040             handler: this.showPrevMonth,
5041             scope: this,
5042             preventDefault:true,
5043             stopDefault:true
5044         });
5045
5046         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5047             handler: this.showNextMonth,
5048             scope: this,
5049             preventDefault:true,
5050             stopDefault:true
5051         });
5052
5053         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5054
5055         this.monthPicker = this.el.down('div.x-date-mp');
5056         this.monthPicker.enableDisplayMode('block');
5057         
5058         var kn = new Roo.KeyNav(this.eventEl, {
5059             "left" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevMonth() :
5062                     this.update(this.activeDate.add("d", -1));
5063             },
5064
5065             "right" : function(e){
5066                 e.ctrlKey ?
5067                     this.showNextMonth() :
5068                     this.update(this.activeDate.add("d", 1));
5069             },
5070
5071             "up" : function(e){
5072                 e.ctrlKey ?
5073                     this.showNextYear() :
5074                     this.update(this.activeDate.add("d", -7));
5075             },
5076
5077             "down" : function(e){
5078                 e.ctrlKey ?
5079                     this.showPrevYear() :
5080                     this.update(this.activeDate.add("d", 7));
5081             },
5082
5083             "pageUp" : function(e){
5084                 this.showNextMonth();
5085             },
5086
5087             "pageDown" : function(e){
5088                 this.showPrevMonth();
5089             },
5090
5091             "enter" : function(e){
5092                 e.stopPropagation();
5093                 return true;
5094             },
5095
5096             scope : this
5097         });
5098
5099         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5100
5101         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5102
5103         this.el.unselectable();
5104         
5105         this.cells = this.el.select("table.x-date-inner tbody td");
5106         this.textNodes = this.el.query("table.x-date-inner tbody span");
5107
5108         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5109             text: "&#160;",
5110             tooltip: this.monthYearText
5111         });
5112
5113         this.mbtn.on('click', this.showMonthPicker, this);
5114         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5115
5116
5117         var today = (new Date()).dateFormat(this.format);
5118         
5119         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5120         if (this.showClear) {
5121             baseTb.add( new Roo.Toolbar.Fill());
5122         }
5123         baseTb.add({
5124             text: String.format(this.todayText, today),
5125             tooltip: String.format(this.todayTip, today),
5126             handler: this.selectToday,
5127             scope: this
5128         });
5129         
5130         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5131             
5132         //});
5133         if (this.showClear) {
5134             
5135             baseTb.add( new Roo.Toolbar.Fill());
5136             baseTb.add({
5137                 text: '&#160;',
5138                 cls: 'x-btn-icon x-btn-clear',
5139                 handler: function() {
5140                     //this.value = '';
5141                     this.fireEvent("select", this, '');
5142                 },
5143                 scope: this
5144             });
5145         }
5146         
5147         
5148         if(Roo.isIE){
5149             this.el.repaint();
5150         }
5151         this.update(this.value);
5152     },
5153
5154     createMonthPicker : function(){
5155         if(!this.monthPicker.dom.firstChild){
5156             var buf = ['<table border="0" cellspacing="0">'];
5157             for(var i = 0; i < 6; i++){
5158                 buf.push(
5159                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5160                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5161                     i == 0 ?
5162                     '<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>' :
5163                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5164                 );
5165             }
5166             buf.push(
5167                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5168                     this.okText,
5169                     '</button><button type="button" class="x-date-mp-cancel">',
5170                     this.cancelText,
5171                     '</button></td></tr>',
5172                 '</table>'
5173             );
5174             this.monthPicker.update(buf.join(''));
5175             this.monthPicker.on('click', this.onMonthClick, this);
5176             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5177
5178             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5179             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5180
5181             this.mpMonths.each(function(m, a, i){
5182                 i += 1;
5183                 if((i%2) == 0){
5184                     m.dom.xmonth = 5 + Math.round(i * .5);
5185                 }else{
5186                     m.dom.xmonth = Math.round((i-1) * .5);
5187                 }
5188             });
5189         }
5190     },
5191
5192     showMonthPicker : function(){
5193         this.createMonthPicker();
5194         var size = this.el.getSize();
5195         this.monthPicker.setSize(size);
5196         this.monthPicker.child('table').setSize(size);
5197
5198         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5199         this.updateMPMonth(this.mpSelMonth);
5200         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5201         this.updateMPYear(this.mpSelYear);
5202
5203         this.monthPicker.slideIn('t', {duration:.2});
5204     },
5205
5206     updateMPYear : function(y){
5207         this.mpyear = y;
5208         var ys = this.mpYears.elements;
5209         for(var i = 1; i <= 10; i++){
5210             var td = ys[i-1], y2;
5211             if((i%2) == 0){
5212                 y2 = y + Math.round(i * .5);
5213                 td.firstChild.innerHTML = y2;
5214                 td.xyear = y2;
5215             }else{
5216                 y2 = y - (5-Math.round(i * .5));
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }
5220             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5221         }
5222     },
5223
5224     updateMPMonth : function(sm){
5225         this.mpMonths.each(function(m, a, i){
5226             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5227         });
5228     },
5229
5230     selectMPMonth: function(m){
5231         
5232     },
5233
5234     onMonthClick : function(e, t){
5235         e.stopEvent();
5236         var el = new Roo.Element(t), pn;
5237         if(el.is('button.x-date-mp-cancel')){
5238             this.hideMonthPicker();
5239         }
5240         else if(el.is('button.x-date-mp-ok')){
5241             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.mpMonths.removeClass('x-date-mp-sel');
5246             pn.addClass('x-date-mp-sel');
5247             this.mpSelMonth = pn.dom.xmonth;
5248         }
5249         else if(pn = el.up('td.x-date-mp-year', 2)){
5250             this.mpYears.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelYear = pn.dom.xyear;
5253         }
5254         else if(el.is('a.x-date-mp-prev')){
5255             this.updateMPYear(this.mpyear-10);
5256         }
5257         else if(el.is('a.x-date-mp-next')){
5258             this.updateMPYear(this.mpyear+10);
5259         }
5260     },
5261
5262     onMonthDblClick : function(e, t){
5263         e.stopEvent();
5264         var el = new Roo.Element(t), pn;
5265         if(pn = el.up('td.x-date-mp-month', 2)){
5266             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5267             this.hideMonthPicker();
5268         }
5269         else if(pn = el.up('td.x-date-mp-year', 2)){
5270             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273     },
5274
5275     hideMonthPicker : function(disableAnim){
5276         if(this.monthPicker){
5277             if(disableAnim === true){
5278                 this.monthPicker.hide();
5279             }else{
5280                 this.monthPicker.slideOut('t', {duration:.2});
5281             }
5282         }
5283     },
5284
5285     // private
5286     showPrevMonth : function(e){
5287         this.update(this.activeDate.add("mo", -1));
5288     },
5289
5290     // private
5291     showNextMonth : function(e){
5292         this.update(this.activeDate.add("mo", 1));
5293     },
5294
5295     // private
5296     showPrevYear : function(){
5297         this.update(this.activeDate.add("y", -1));
5298     },
5299
5300     // private
5301     showNextYear : function(){
5302         this.update(this.activeDate.add("y", 1));
5303     },
5304
5305     // private
5306     handleMouseWheel : function(e){
5307         var delta = e.getWheelDelta();
5308         if(delta > 0){
5309             this.showPrevMonth();
5310             e.stopEvent();
5311         } else if(delta < 0){
5312             this.showNextMonth();
5313             e.stopEvent();
5314         }
5315     },
5316
5317     // private
5318     handleDateClick : function(e, t){
5319         e.stopEvent();
5320         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5321             this.setValue(new Date(t.dateValue));
5322             this.fireEvent("select", this, this.value);
5323         }
5324     },
5325
5326     // private
5327     selectToday : function(){
5328         this.setValue(new Date().clearTime());
5329         this.fireEvent("select", this, this.value);
5330     },
5331
5332     // private
5333     update : function(date)
5334     {
5335         var vd = this.activeDate;
5336         this.activeDate = date;
5337         if(vd && this.el){
5338             var t = date.getTime();
5339             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5340                 this.cells.removeClass("x-date-selected");
5341                 this.cells.each(function(c){
5342                    if(c.dom.firstChild.dateValue == t){
5343                        c.addClass("x-date-selected");
5344                        setTimeout(function(){
5345                             try{c.dom.firstChild.focus();}catch(e){}
5346                        }, 50);
5347                        return false;
5348                    }
5349                 });
5350                 return;
5351             }
5352         }
5353         
5354         var days = date.getDaysInMonth();
5355         var firstOfMonth = date.getFirstDateOfMonth();
5356         var startingPos = firstOfMonth.getDay()-this.startDay;
5357
5358         if(startingPos <= this.startDay){
5359             startingPos += 7;
5360         }
5361
5362         var pm = date.add("mo", -1);
5363         var prevStart = pm.getDaysInMonth()-startingPos;
5364
5365         var cells = this.cells.elements;
5366         var textEls = this.textNodes;
5367         days += startingPos;
5368
5369         // convert everything to numbers so it's fast
5370         var day = 86400000;
5371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5372         var today = new Date().clearTime().getTime();
5373         var sel = date.clearTime().getTime();
5374         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5375         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5376         var ddMatch = this.disabledDatesRE;
5377         var ddText = this.disabledDatesText;
5378         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5379         var ddaysText = this.disabledDaysText;
5380         var format = this.format;
5381
5382         var setCellClass = function(cal, cell){
5383             cell.title = "";
5384             var t = d.getTime();
5385             cell.firstChild.dateValue = t;
5386             if(t == today){
5387                 cell.className += " x-date-today";
5388                 cell.title = cal.todayText;
5389             }
5390             if(t == sel){
5391                 cell.className += " x-date-selected";
5392                 setTimeout(function(){
5393                     try{cell.firstChild.focus();}catch(e){}
5394                 }, 50);
5395             }
5396             // disabling
5397             if(t < min) {
5398                 cell.className = " x-date-disabled";
5399                 cell.title = cal.minText;
5400                 return;
5401             }
5402             if(t > max) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.maxText;
5405                 return;
5406             }
5407             if(ddays){
5408                 if(ddays.indexOf(d.getDay()) != -1){
5409                     cell.title = ddaysText;
5410                     cell.className = " x-date-disabled";
5411                 }
5412             }
5413             if(ddMatch && format){
5414                 var fvalue = d.dateFormat(format);
5415                 if(ddMatch.test(fvalue)){
5416                     cell.title = ddText.replace("%0", fvalue);
5417                     cell.className = " x-date-disabled";
5418                 }
5419             }
5420         };
5421
5422         var i = 0;
5423         for(; i < startingPos; i++) {
5424             textEls[i].innerHTML = (++prevStart);
5425             d.setDate(d.getDate()+1);
5426             cells[i].className = "x-date-prevday";
5427             setCellClass(this, cells[i]);
5428         }
5429         for(; i < days; i++){
5430             intDay = i - startingPos + 1;
5431             textEls[i].innerHTML = (intDay);
5432             d.setDate(d.getDate()+1);
5433             cells[i].className = "x-date-active";
5434             setCellClass(this, cells[i]);
5435         }
5436         var extraDays = 0;
5437         for(; i < 42; i++) {
5438              textEls[i].innerHTML = (++extraDays);
5439              d.setDate(d.getDate()+1);
5440              cells[i].className = "x-date-nextday";
5441              setCellClass(this, cells[i]);
5442         }
5443
5444         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5445         this.fireEvent('monthchange', this, date);
5446         
5447         if(!this.internalRender){
5448             var main = this.el.dom.firstChild;
5449             var w = main.offsetWidth;
5450             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5451             Roo.fly(main).setWidth(w);
5452             this.internalRender = true;
5453             // opera does not respect the auto grow header center column
5454             // then, after it gets a width opera refuses to recalculate
5455             // without a second pass
5456             if(Roo.isOpera && !this.secondPass){
5457                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5458                 this.secondPass = true;
5459                 this.update.defer(10, this, [date]);
5460             }
5461         }
5462         
5463         
5464     }
5465 });        /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475 /**
5476  * @class Roo.TabPanel
5477  * @extends Roo.util.Observable
5478  * A lightweight tab container.
5479  * <br><br>
5480  * Usage:
5481  * <pre><code>
5482 // basic tabs 1, built from existing content
5483 var tabs = new Roo.TabPanel("tabs1");
5484 tabs.addTab("script", "View Script");
5485 tabs.addTab("markup", "View Markup");
5486 tabs.activate("script");
5487
5488 // more advanced tabs, built from javascript
5489 var jtabs = new Roo.TabPanel("jtabs");
5490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5491
5492 // set up the UpdateManager
5493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5494 var updater = tab2.getUpdateManager();
5495 updater.setDefaultUrl("ajax1.htm");
5496 tab2.on('activate', updater.refresh, updater, true);
5497
5498 // Use setUrl for Ajax loading
5499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5500 tab3.setUrl("ajax2.htm", null, true);
5501
5502 // Disabled tab
5503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5504 tab4.disable();
5505
5506 jtabs.activate("jtabs-1");
5507  * </code></pre>
5508  * @constructor
5509  * Create a new TabPanel.
5510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5512  */
5513 Roo.TabPanel = function(container, config){
5514     /**
5515     * The container element for this TabPanel.
5516     * @type Roo.Element
5517     */
5518     this.el = Roo.get(container, true);
5519     if(config){
5520         if(typeof config == "boolean"){
5521             this.tabPosition = config ? "bottom" : "top";
5522         }else{
5523             Roo.apply(this, config);
5524         }
5525     }
5526     if(this.tabPosition == "bottom"){
5527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5528         this.el.addClass("x-tabs-bottom");
5529     }
5530     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5531     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5532     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5533     if(Roo.isIE){
5534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5535     }
5536     if(this.tabPosition != "bottom"){
5537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5538          * @type Roo.Element
5539          */
5540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5541         this.el.addClass("x-tabs-top");
5542     }
5543     this.items = [];
5544
5545     this.bodyEl.setStyle("position", "relative");
5546
5547     this.active = null;
5548     this.activateDelegate = this.activate.createDelegate(this);
5549
5550     this.addEvents({
5551         /**
5552          * @event tabchange
5553          * Fires when the active tab changes
5554          * @param {Roo.TabPanel} this
5555          * @param {Roo.TabPanelItem} activePanel The new active tab
5556          */
5557         "tabchange": true,
5558         /**
5559          * @event beforetabchange
5560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5561          * @param {Roo.TabPanel} this
5562          * @param {Object} e Set cancel to true on this object to cancel the tab change
5563          * @param {Roo.TabPanelItem} tab The tab being changed to
5564          */
5565         "beforetabchange" : true
5566     });
5567
5568     Roo.EventManager.onWindowResize(this.onResize, this);
5569     this.cpad = this.el.getPadding("lr");
5570     this.hiddenCount = 0;
5571
5572
5573     // toolbar on the tabbar support...
5574     if (this.toolbar) {
5575         var tcfg = this.toolbar;
5576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5577         this.toolbar = new Roo.Toolbar(tcfg);
5578         if (Roo.isSafari) {
5579             var tbl = tcfg.container.child('table', true);
5580             tbl.setAttribute('width', '100%');
5581         }
5582         
5583     }
5584    
5585
5586
5587     Roo.TabPanel.superclass.constructor.call(this);
5588 };
5589
5590 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5591     /*
5592      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5593      */
5594     tabPosition : "top",
5595     /*
5596      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5597      */
5598     currentTabWidth : 0,
5599     /*
5600      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5601      */
5602     minTabWidth : 40,
5603     /*
5604      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     maxTabWidth : 250,
5607     /*
5608      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     preferredTabWidth : 175,
5611     /*
5612      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5613      */
5614     resizeTabs : false,
5615     /*
5616      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5617      */
5618     monitorResize : true,
5619     /*
5620      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5621      */
5622     toolbar : false,
5623
5624     /**
5625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5626      * @param {String} id The id of the div to use <b>or create</b>
5627      * @param {String} text The text for the tab
5628      * @param {String} content (optional) Content to put in the TabPanelItem body
5629      * @param {Boolean} closable (optional) True to create a close icon on the tab
5630      * @return {Roo.TabPanelItem} The created TabPanelItem
5631      */
5632     addTab : function(id, text, content, closable){
5633         var item = new Roo.TabPanelItem(this, id, text, closable);
5634         this.addTabItem(item);
5635         if(content){
5636             item.setContent(content);
5637         }
5638         return item;
5639     },
5640
5641     /**
5642      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5643      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5644      * @return {Roo.TabPanelItem}
5645      */
5646     getTab : function(id){
5647         return this.items[id];
5648     },
5649
5650     /**
5651      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5652      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5653      */
5654     hideTab : function(id){
5655         var t = this.items[id];
5656         if(!t.isHidden()){
5657            t.setHidden(true);
5658            this.hiddenCount++;
5659            this.autoSizeTabs();
5660         }
5661     },
5662
5663     /**
5664      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5665      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5666      */
5667     unhideTab : function(id){
5668         var t = this.items[id];
5669         if(t.isHidden()){
5670            t.setHidden(false);
5671            this.hiddenCount--;
5672            this.autoSizeTabs();
5673         }
5674     },
5675
5676     /**
5677      * Adds an existing {@link Roo.TabPanelItem}.
5678      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5679      */
5680     addTabItem : function(item){
5681         this.items[item.id] = item;
5682         this.items.push(item);
5683         if(this.resizeTabs){
5684            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5685            this.autoSizeTabs();
5686         }else{
5687             item.autoSize();
5688         }
5689     },
5690
5691     /**
5692      * Removes a {@link Roo.TabPanelItem}.
5693      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5694      */
5695     removeTab : function(id){
5696         var items = this.items;
5697         var tab = items[id];
5698         if(!tab) { return; }
5699         var index = items.indexOf(tab);
5700         if(this.active == tab && items.length > 1){
5701             var newTab = this.getNextAvailable(index);
5702             if(newTab) {
5703                 newTab.activate();
5704             }
5705         }
5706         this.stripEl.dom.removeChild(tab.pnode.dom);
5707         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5708             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5709         }
5710         items.splice(index, 1);
5711         delete this.items[tab.id];
5712         tab.fireEvent("close", tab);
5713         tab.purgeListeners();
5714         this.autoSizeTabs();
5715     },
5716
5717     getNextAvailable : function(start){
5718         var items = this.items;
5719         var index = start;
5720         // look for a next tab that will slide over to
5721         // replace the one being removed
5722         while(index < items.length){
5723             var item = items[++index];
5724             if(item && !item.isHidden()){
5725                 return item;
5726             }
5727         }
5728         // if one isn't found select the previous tab (on the left)
5729         index = start;
5730         while(index >= 0){
5731             var item = items[--index];
5732             if(item && !item.isHidden()){
5733                 return item;
5734             }
5735         }
5736         return null;
5737     },
5738
5739     /**
5740      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5741      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5742      */
5743     disableTab : function(id){
5744         var tab = this.items[id];
5745         if(tab && this.active != tab){
5746             tab.disable();
5747         }
5748     },
5749
5750     /**
5751      * Enables a {@link Roo.TabPanelItem} that is disabled.
5752      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5753      */
5754     enableTab : function(id){
5755         var tab = this.items[id];
5756         tab.enable();
5757     },
5758
5759     /**
5760      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5761      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5762      * @return {Roo.TabPanelItem} The TabPanelItem.
5763      */
5764     activate : function(id){
5765         var tab = this.items[id];
5766         if(!tab){
5767             return null;
5768         }
5769         if(tab == this.active || tab.disabled){
5770             return tab;
5771         }
5772         var e = {};
5773         this.fireEvent("beforetabchange", this, e, tab);
5774         if(e.cancel !== true && !tab.disabled){
5775             if(this.active){
5776                 this.active.hide();
5777             }
5778             this.active = this.items[id];
5779             this.active.show();
5780             this.fireEvent("tabchange", this, this.active);
5781         }
5782         return tab;
5783     },
5784
5785     /**
5786      * Gets the active {@link Roo.TabPanelItem}.
5787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5788      */
5789     getActiveTab : function(){
5790         return this.active;
5791     },
5792
5793     /**
5794      * Updates the tab body element to fit the height of the container element
5795      * for overflow scrolling
5796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5797      */
5798     syncHeight : function(targetHeight){
5799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5800         var bm = this.bodyEl.getMargins();
5801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5802         this.bodyEl.setHeight(newHeight);
5803         return newHeight;
5804     },
5805
5806     onResize : function(){
5807         if(this.monitorResize){
5808             this.autoSizeTabs();
5809         }
5810     },
5811
5812     /**
5813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5814      */
5815     beginUpdate : function(){
5816         this.updating = true;
5817     },
5818
5819     /**
5820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5821      */
5822     endUpdate : function(){
5823         this.updating = false;
5824         this.autoSizeTabs();
5825     },
5826
5827     /**
5828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5829      */
5830     autoSizeTabs : function(){
5831         var count = this.items.length;
5832         var vcount = count - this.hiddenCount;
5833         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5834             return;
5835         }
5836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5837         var availWidth = Math.floor(w / vcount);
5838         var b = this.stripBody;
5839         if(b.getWidth() > w){
5840             var tabs = this.items;
5841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5842             if(availWidth < this.minTabWidth){
5843                 /*if(!this.sleft){    // incomplete scrolling code
5844                     this.createScrollButtons();
5845                 }
5846                 this.showScroll();
5847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5848             }
5849         }else{
5850             if(this.currentTabWidth < this.preferredTabWidth){
5851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Returns the number of tabs in this TabPanel.
5858      * @return {Number}
5859      */
5860      getCount : function(){
5861          return this.items.length;
5862      },
5863
5864     /**
5865      * Resizes all the tabs to the passed width
5866      * @param {Number} The new width
5867      */
5868     setTabWidth : function(width){
5869         this.currentTabWidth = width;
5870         for(var i = 0, len = this.items.length; i < len; i++) {
5871                 if(!this.items[i].isHidden()) {
5872                 this.items[i].setWidth(width);
5873             }
5874         }
5875     },
5876
5877     /**
5878      * Destroys this TabPanel
5879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5880      */
5881     destroy : function(removeEl){
5882         Roo.EventManager.removeResizeListener(this.onResize, this);
5883         for(var i = 0, len = this.items.length; i < len; i++){
5884             this.items[i].purgeListeners();
5885         }
5886         if(removeEl === true){
5887             this.el.update("");
5888             this.el.remove();
5889         }
5890     }
5891 });
5892
5893 /**
5894  * @class Roo.TabPanelItem
5895  * @extends Roo.util.Observable
5896  * Represents an individual item (tab plus body) in a TabPanel.
5897  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5898  * @param {String} id The id of this TabPanelItem
5899  * @param {String} text The text for the tab of this TabPanelItem
5900  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5901  */
5902 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5903     /**
5904      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5905      * @type Roo.TabPanel
5906      */
5907     this.tabPanel = tabPanel;
5908     /**
5909      * The id for this TabPanelItem
5910      * @type String
5911      */
5912     this.id = id;
5913     /** @private */
5914     this.disabled = false;
5915     /** @private */
5916     this.text = text;
5917     /** @private */
5918     this.loaded = false;
5919     this.closable = closable;
5920
5921     /**
5922      * The body element for this TabPanelItem.
5923      * @type Roo.Element
5924      */
5925     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5926     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5927     this.bodyEl.setStyle("display", "block");
5928     this.bodyEl.setStyle("zoom", "1");
5929     this.hideAction();
5930
5931     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5932     /** @private */
5933     this.el = Roo.get(els.el, true);
5934     this.inner = Roo.get(els.inner, true);
5935     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5936     this.pnode = Roo.get(els.el.parentNode, true);
5937     this.el.on("mousedown", this.onTabMouseDown, this);
5938     this.el.on("click", this.onTabClick, this);
5939     /** @private */
5940     if(closable){
5941         var c = Roo.get(els.close, true);
5942         c.dom.title = this.closeText;
5943         c.addClassOnOver("close-over");
5944         c.on("click", this.closeClick, this);
5945      }
5946
5947     this.addEvents({
5948          /**
5949          * @event activate
5950          * Fires when this tab becomes the active tab.
5951          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5952          * @param {Roo.TabPanelItem} this
5953          */
5954         "activate": true,
5955         /**
5956          * @event beforeclose
5957          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5958          * @param {Roo.TabPanelItem} this
5959          * @param {Object} e Set cancel to true on this object to cancel the close.
5960          */
5961         "beforeclose": true,
5962         /**
5963          * @event close
5964          * Fires when this tab is closed.
5965          * @param {Roo.TabPanelItem} this
5966          */
5967          "close": true,
5968         /**
5969          * @event deactivate
5970          * Fires when this tab is no longer the active tab.
5971          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5972          * @param {Roo.TabPanelItem} this
5973          */
5974          "deactivate" : true
5975     });
5976     this.hidden = false;
5977
5978     Roo.TabPanelItem.superclass.constructor.call(this);
5979 };
5980
5981 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5982     purgeListeners : function(){
5983        Roo.util.Observable.prototype.purgeListeners.call(this);
5984        this.el.removeAllListeners();
5985     },
5986     /**
5987      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5988      */
5989     show : function(){
5990         this.pnode.addClass("on");
5991         this.showAction();
5992         if(Roo.isOpera){
5993             this.tabPanel.stripWrap.repaint();
5994         }
5995         this.fireEvent("activate", this.tabPanel, this);
5996     },
5997
5998     /**
5999      * Returns true if this tab is the active tab.
6000      * @return {Boolean}
6001      */
6002     isActive : function(){
6003         return this.tabPanel.getActiveTab() == this;
6004     },
6005
6006     /**
6007      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6008      */
6009     hide : function(){
6010         this.pnode.removeClass("on");
6011         this.hideAction();
6012         this.fireEvent("deactivate", this.tabPanel, this);
6013     },
6014
6015     hideAction : function(){
6016         this.bodyEl.hide();
6017         this.bodyEl.setStyle("position", "absolute");
6018         this.bodyEl.setLeft("-20000px");
6019         this.bodyEl.setTop("-20000px");
6020     },
6021
6022     showAction : function(){
6023         this.bodyEl.setStyle("position", "relative");
6024         this.bodyEl.setTop("");
6025         this.bodyEl.setLeft("");
6026         this.bodyEl.show();
6027     },
6028
6029     /**
6030      * Set the tooltip for the tab.
6031      * @param {String} tooltip The tab's tooltip
6032      */
6033     setTooltip : function(text){
6034         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6035             this.textEl.dom.qtip = text;
6036             this.textEl.dom.removeAttribute('title');
6037         }else{
6038             this.textEl.dom.title = text;
6039         }
6040     },
6041
6042     onTabClick : function(e){
6043         e.preventDefault();
6044         this.tabPanel.activate(this.id);
6045     },
6046
6047     onTabMouseDown : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     getWidth : function(){
6053         return this.inner.getWidth();
6054     },
6055
6056     setWidth : function(width){
6057         var iwidth = width - this.pnode.getPadding("lr");
6058         this.inner.setWidth(iwidth);
6059         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6060         this.pnode.setWidth(width);
6061     },
6062
6063     /**
6064      * Show or hide the tab
6065      * @param {Boolean} hidden True to hide or false to show.
6066      */
6067     setHidden : function(hidden){
6068         this.hidden = hidden;
6069         this.pnode.setStyle("display", hidden ? "none" : "");
6070     },
6071
6072     /**
6073      * Returns true if this tab is "hidden"
6074      * @return {Boolean}
6075      */
6076     isHidden : function(){
6077         return this.hidden;
6078     },
6079
6080     /**
6081      * Returns the text for this tab
6082      * @return {String}
6083      */
6084     getText : function(){
6085         return this.text;
6086     },
6087
6088     autoSize : function(){
6089         //this.el.beginMeasure();
6090         this.textEl.setWidth(1);
6091         /*
6092          *  #2804 [new] Tabs in Roojs
6093          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6094          */
6095         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6096         //this.el.endMeasure();
6097     },
6098
6099     /**
6100      * Sets the text for the tab (Note: this also sets the tooltip text)
6101      * @param {String} text The tab's text and tooltip
6102      */
6103     setText : function(text){
6104         this.text = text;
6105         this.textEl.update(text);
6106         this.setTooltip(text);
6107         if(!this.tabPanel.resizeTabs){
6108             this.autoSize();
6109         }
6110     },
6111     /**
6112      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6113      */
6114     activate : function(){
6115         this.tabPanel.activate(this.id);
6116     },
6117
6118     /**
6119      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6120      */
6121     disable : function(){
6122         if(this.tabPanel.active != this){
6123             this.disabled = true;
6124             this.pnode.addClass("disabled");
6125         }
6126     },
6127
6128     /**
6129      * Enables this TabPanelItem if it was previously disabled.
6130      */
6131     enable : function(){
6132         this.disabled = false;
6133         this.pnode.removeClass("disabled");
6134     },
6135
6136     /**
6137      * Sets the content for this TabPanelItem.
6138      * @param {String} content The content
6139      * @param {Boolean} loadScripts true to look for and load scripts
6140      */
6141     setContent : function(content, loadScripts){
6142         this.bodyEl.update(content, loadScripts);
6143     },
6144
6145     /**
6146      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6147      * @return {Roo.UpdateManager} The UpdateManager
6148      */
6149     getUpdateManager : function(){
6150         return this.bodyEl.getUpdateManager();
6151     },
6152
6153     /**
6154      * Set a URL to be used to load the content for this TabPanelItem.
6155      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6156      * @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)
6157      * @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)
6158      * @return {Roo.UpdateManager} The UpdateManager
6159      */
6160     setUrl : function(url, params, loadOnce){
6161         if(this.refreshDelegate){
6162             this.un('activate', this.refreshDelegate);
6163         }
6164         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6165         this.on("activate", this.refreshDelegate);
6166         return this.bodyEl.getUpdateManager();
6167     },
6168
6169     /** @private */
6170     _handleRefresh : function(url, params, loadOnce){
6171         if(!loadOnce || !this.loaded){
6172             var updater = this.bodyEl.getUpdateManager();
6173             updater.update(url, params, this._setLoaded.createDelegate(this));
6174         }
6175     },
6176
6177     /**
6178      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6179      *   Will fail silently if the setUrl method has not been called.
6180      *   This does not activate the panel, just updates its content.
6181      */
6182     refresh : function(){
6183         if(this.refreshDelegate){
6184            this.loaded = false;
6185            this.refreshDelegate();
6186         }
6187     },
6188
6189     /** @private */
6190     _setLoaded : function(){
6191         this.loaded = true;
6192     },
6193
6194     /** @private */
6195     closeClick : function(e){
6196         var o = {};
6197         e.stopEvent();
6198         this.fireEvent("beforeclose", this, o);
6199         if(o.cancel !== true){
6200             this.tabPanel.removeTab(this.id);
6201         }
6202     },
6203     /**
6204      * The text displayed in the tooltip for the close icon.
6205      * @type String
6206      */
6207     closeText : "Close this tab"
6208 });
6209
6210 /** @private */
6211 Roo.TabPanel.prototype.createStrip = function(container){
6212     var strip = document.createElement("div");
6213     strip.className = "x-tabs-wrap";
6214     container.appendChild(strip);
6215     return strip;
6216 };
6217 /** @private */
6218 Roo.TabPanel.prototype.createStripList = function(strip){
6219     // div wrapper for retard IE
6220     // returns the "tr" element.
6221     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6222         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6223         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6224     return strip.firstChild.firstChild.firstChild.firstChild;
6225 };
6226 /** @private */
6227 Roo.TabPanel.prototype.createBody = function(container){
6228     var body = document.createElement("div");
6229     Roo.id(body, "tab-body");
6230     Roo.fly(body).addClass("x-tabs-body");
6231     container.appendChild(body);
6232     return body;
6233 };
6234 /** @private */
6235 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6236     var body = Roo.getDom(id);
6237     if(!body){
6238         body = document.createElement("div");
6239         body.id = id;
6240     }
6241     Roo.fly(body).addClass("x-tabs-item-body");
6242     bodyEl.insertBefore(body, bodyEl.firstChild);
6243     return body;
6244 };
6245 /** @private */
6246 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6247     var td = document.createElement("td");
6248     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6249     //stripEl.appendChild(td);
6250     if(closable){
6251         td.className = "x-tabs-closable";
6252         if(!this.closeTpl){
6253             this.closeTpl = new Roo.Template(
6254                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6255                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6256                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6257             );
6258         }
6259         var el = this.closeTpl.overwrite(td, {"text": text});
6260         var close = el.getElementsByTagName("div")[0];
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "close": close, "inner": inner};
6263     } else {
6264         if(!this.tabTpl){
6265             this.tabTpl = new Roo.Template(
6266                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6267                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6268             );
6269         }
6270         var el = this.tabTpl.overwrite(td, {"text": text});
6271         var inner = el.getElementsByTagName("em")[0];
6272         return {"el": el, "inner": inner};
6273     }
6274 };/*
6275  * Based on:
6276  * Ext JS Library 1.1.1
6277  * Copyright(c) 2006-2007, Ext JS, LLC.
6278  *
6279  * Originally Released Under LGPL - original licence link has changed is not relivant.
6280  *
6281  * Fork - LGPL
6282  * <script type="text/javascript">
6283  */
6284
6285 /**
6286  * @class Roo.Button
6287  * @extends Roo.util.Observable
6288  * Simple Button class
6289  * @cfg {String} text The button text
6290  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6291  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6292  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6293  * @cfg {Object} scope The scope of the handler
6294  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6295  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6296  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6297  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6298  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6299  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6300    applies if enableToggle = true)
6301  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6302  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6303   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6304  * @constructor
6305  * Create a new button
6306  * @param {Object} config The config object
6307  */
6308 Roo.Button = function(renderTo, config)
6309 {
6310     if (!config) {
6311         config = renderTo;
6312         renderTo = config.renderTo || false;
6313     }
6314     
6315     Roo.apply(this, config);
6316     this.addEvents({
6317         /**
6318              * @event click
6319              * Fires when this button is clicked
6320              * @param {Button} this
6321              * @param {EventObject} e The click event
6322              */
6323             "click" : true,
6324         /**
6325              * @event toggle
6326              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6327              * @param {Button} this
6328              * @param {Boolean} pressed
6329              */
6330             "toggle" : true,
6331         /**
6332              * @event mouseover
6333              * Fires when the mouse hovers over the button
6334              * @param {Button} this
6335              * @param {Event} e The event object
6336              */
6337         'mouseover' : true,
6338         /**
6339              * @event mouseout
6340              * Fires when the mouse exits the button
6341              * @param {Button} this
6342              * @param {Event} e The event object
6343              */
6344         'mouseout': true,
6345          /**
6346              * @event render
6347              * Fires when the button is rendered
6348              * @param {Button} this
6349              */
6350         'render': true
6351     });
6352     if(this.menu){
6353         this.menu = Roo.menu.MenuMgr.get(this.menu);
6354     }
6355     // register listeners first!!  - so render can be captured..
6356     Roo.util.Observable.call(this);
6357     if(renderTo){
6358         this.render(renderTo);
6359     }
6360     
6361   
6362 };
6363
6364 Roo.extend(Roo.Button, Roo.util.Observable, {
6365     /**
6366      * 
6367      */
6368     
6369     /**
6370      * Read-only. True if this button is hidden
6371      * @type Boolean
6372      */
6373     hidden : false,
6374     /**
6375      * Read-only. True if this button is disabled
6376      * @type Boolean
6377      */
6378     disabled : false,
6379     /**
6380      * Read-only. True if this button is pressed (only if enableToggle = true)
6381      * @type Boolean
6382      */
6383     pressed : false,
6384
6385     /**
6386      * @cfg {Number} tabIndex 
6387      * The DOM tabIndex for this button (defaults to undefined)
6388      */
6389     tabIndex : undefined,
6390
6391     /**
6392      * @cfg {Boolean} enableToggle
6393      * True to enable pressed/not pressed toggling (defaults to false)
6394      */
6395     enableToggle: false,
6396     /**
6397      * @cfg {Roo.menu.Menu} menu
6398      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6399      */
6400     menu : undefined,
6401     /**
6402      * @cfg {String} menuAlign
6403      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6404      */
6405     menuAlign : "tl-bl?",
6406
6407     /**
6408      * @cfg {String} iconCls
6409      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6410      */
6411     iconCls : undefined,
6412     /**
6413      * @cfg {String} type
6414      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6415      */
6416     type : 'button',
6417
6418     // private
6419     menuClassTarget: 'tr',
6420
6421     /**
6422      * @cfg {String} clickEvent
6423      * The type of event to map to the button's event handler (defaults to 'click')
6424      */
6425     clickEvent : 'click',
6426
6427     /**
6428      * @cfg {Boolean} handleMouseEvents
6429      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6430      */
6431     handleMouseEvents : true,
6432
6433     /**
6434      * @cfg {String} tooltipType
6435      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6436      */
6437     tooltipType : 'qtip',
6438
6439     /**
6440      * @cfg {String} cls
6441      * A CSS class to apply to the button's main element.
6442      */
6443     
6444     /**
6445      * @cfg {Roo.Template} template (Optional)
6446      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6447      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6448      * require code modifications if required elements (e.g. a button) aren't present.
6449      */
6450
6451     // private
6452     render : function(renderTo){
6453         var btn;
6454         if(this.hideParent){
6455             this.parentEl = Roo.get(renderTo);
6456         }
6457         if(!this.dhconfig){
6458             if(!this.template){
6459                 if(!Roo.Button.buttonTemplate){
6460                     // hideous table template
6461                     Roo.Button.buttonTemplate = new Roo.Template(
6462                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6463                         '<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>',
6464                         "</tr></tbody></table>");
6465                 }
6466                 this.template = Roo.Button.buttonTemplate;
6467             }
6468             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6469             var btnEl = btn.child("button:first");
6470             btnEl.on('focus', this.onFocus, this);
6471             btnEl.on('blur', this.onBlur, this);
6472             if(this.cls){
6473                 btn.addClass(this.cls);
6474             }
6475             if(this.icon){
6476                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6477             }
6478             if(this.iconCls){
6479                 btnEl.addClass(this.iconCls);
6480                 if(!this.cls){
6481                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6482                 }
6483             }
6484             if(this.tabIndex !== undefined){
6485                 btnEl.dom.tabIndex = this.tabIndex;
6486             }
6487             if(this.tooltip){
6488                 if(typeof this.tooltip == 'object'){
6489                     Roo.QuickTips.tips(Roo.apply({
6490                           target: btnEl.id
6491                     }, this.tooltip));
6492                 } else {
6493                     btnEl.dom[this.tooltipType] = this.tooltip;
6494                 }
6495             }
6496         }else{
6497             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6498         }
6499         this.el = btn;
6500         if(this.id){
6501             this.el.dom.id = this.el.id = this.id;
6502         }
6503         if(this.menu){
6504             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6505             this.menu.on("show", this.onMenuShow, this);
6506             this.menu.on("hide", this.onMenuHide, this);
6507         }
6508         btn.addClass("x-btn");
6509         if(Roo.isIE && !Roo.isIE7){
6510             this.autoWidth.defer(1, this);
6511         }else{
6512             this.autoWidth();
6513         }
6514         if(this.handleMouseEvents){
6515             btn.on("mouseover", this.onMouseOver, this);
6516             btn.on("mouseout", this.onMouseOut, this);
6517             btn.on("mousedown", this.onMouseDown, this);
6518         }
6519         btn.on(this.clickEvent, this.onClick, this);
6520         //btn.on("mouseup", this.onMouseUp, this);
6521         if(this.hidden){
6522             this.hide();
6523         }
6524         if(this.disabled){
6525             this.disable();
6526         }
6527         Roo.ButtonToggleMgr.register(this);
6528         if(this.pressed){
6529             this.el.addClass("x-btn-pressed");
6530         }
6531         if(this.repeat){
6532             var repeater = new Roo.util.ClickRepeater(btn,
6533                 typeof this.repeat == "object" ? this.repeat : {}
6534             );
6535             repeater.on("click", this.onClick,  this);
6536         }
6537         
6538         this.fireEvent('render', this);
6539         
6540     },
6541     /**
6542      * Returns the button's underlying element
6543      * @return {Roo.Element} The element
6544      */
6545     getEl : function(){
6546         return this.el;  
6547     },
6548     
6549     /**
6550      * Destroys this Button and removes any listeners.
6551      */
6552     destroy : function(){
6553         Roo.ButtonToggleMgr.unregister(this);
6554         this.el.removeAllListeners();
6555         this.purgeListeners();
6556         this.el.remove();
6557     },
6558
6559     // private
6560     autoWidth : function(){
6561         if(this.el){
6562             this.el.setWidth("auto");
6563             if(Roo.isIE7 && Roo.isStrict){
6564                 var ib = this.el.child('button');
6565                 if(ib && ib.getWidth() > 20){
6566                     ib.clip();
6567                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6568                 }
6569             }
6570             if(this.minWidth){
6571                 if(this.hidden){
6572                     this.el.beginMeasure();
6573                 }
6574                 if(this.el.getWidth() < this.minWidth){
6575                     this.el.setWidth(this.minWidth);
6576                 }
6577                 if(this.hidden){
6578                     this.el.endMeasure();
6579                 }
6580             }
6581         }
6582     },
6583
6584     /**
6585      * Assigns this button's click handler
6586      * @param {Function} handler The function to call when the button is clicked
6587      * @param {Object} scope (optional) Scope for the function passed in
6588      */
6589     setHandler : function(handler, scope){
6590         this.handler = handler;
6591         this.scope = scope;  
6592     },
6593     
6594     /**
6595      * Sets this button's text
6596      * @param {String} text The button text
6597      */
6598     setText : function(text){
6599         this.text = text;
6600         if(this.el){
6601             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6602         }
6603         this.autoWidth();
6604     },
6605     
6606     /**
6607      * Gets the text for this button
6608      * @return {String} The button text
6609      */
6610     getText : function(){
6611         return this.text;  
6612     },
6613     
6614     /**
6615      * Show this button
6616      */
6617     show: function(){
6618         this.hidden = false;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6621         }
6622     },
6623     
6624     /**
6625      * Hide this button
6626      */
6627     hide: function(){
6628         this.hidden = true;
6629         if(this.el){
6630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6631         }
6632     },
6633     
6634     /**
6635      * Convenience function for boolean show/hide
6636      * @param {Boolean} visible True to show, false to hide
6637      */
6638     setVisible: function(visible){
6639         if(visible) {
6640             this.show();
6641         }else{
6642             this.hide();
6643         }
6644     },
6645     
6646     /**
6647      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6648      * @param {Boolean} state (optional) Force a particular state
6649      */
6650     toggle : function(state){
6651         state = state === undefined ? !this.pressed : state;
6652         if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656                 this.fireEvent("toggle", this, true);
6657             }else{
6658                 this.el.removeClass("x-btn-pressed");
6659                 this.pressed = false;
6660                 this.fireEvent("toggle", this, false);
6661             }
6662             if(this.toggleHandler){
6663                 this.toggleHandler.call(this.scope || this, this, state);
6664             }
6665         }
6666     },
6667     
6668     /**
6669      * Focus the button
6670      */
6671     focus : function(){
6672         this.el.child('button:first').focus();
6673     },
6674     
6675     /**
6676      * Disable this button
6677      */
6678     disable : function(){
6679         if(this.el){
6680             this.el.addClass("x-btn-disabled");
6681         }
6682         this.disabled = true;
6683     },
6684     
6685     /**
6686      * Enable this button
6687      */
6688     enable : function(){
6689         if(this.el){
6690             this.el.removeClass("x-btn-disabled");
6691         }
6692         this.disabled = false;
6693     },
6694
6695     /**
6696      * Convenience function for boolean enable/disable
6697      * @param {Boolean} enabled True to enable, false to disable
6698      */
6699     setDisabled : function(v){
6700         this[v !== true ? "enable" : "disable"]();
6701     },
6702
6703     // private
6704     onClick : function(e)
6705     {
6706         if(e){
6707             e.preventDefault();
6708         }
6709         if(e.button != 0){
6710             return;
6711         }
6712         if(!this.disabled){
6713             if(this.enableToggle){
6714                 this.toggle();
6715             }
6716             if(this.menu && !this.menu.isVisible()){
6717                 this.menu.show(this.el, this.menuAlign);
6718             }
6719             this.fireEvent("click", this, e);
6720             if(this.handler){
6721                 this.el.removeClass("x-btn-over");
6722                 this.handler.call(this.scope || this, this, e);
6723             }
6724         }
6725     },
6726     // private
6727     onMouseOver : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-over");
6730             this.fireEvent('mouseover', this, e);
6731         }
6732     },
6733     // private
6734     onMouseOut : function(e){
6735         if(!e.within(this.el,  true)){
6736             this.el.removeClass("x-btn-over");
6737             this.fireEvent('mouseout', this, e);
6738         }
6739     },
6740     // private
6741     onFocus : function(e){
6742         if(!this.disabled){
6743             this.el.addClass("x-btn-focus");
6744         }
6745     },
6746     // private
6747     onBlur : function(e){
6748         this.el.removeClass("x-btn-focus");
6749     },
6750     // private
6751     onMouseDown : function(e){
6752         if(!this.disabled && e.button == 0){
6753             this.el.addClass("x-btn-click");
6754             Roo.get(document).on('mouseup', this.onMouseUp, this);
6755         }
6756     },
6757     // private
6758     onMouseUp : function(e){
6759         if(e.button == 0){
6760             this.el.removeClass("x-btn-click");
6761             Roo.get(document).un('mouseup', this.onMouseUp, this);
6762         }
6763     },
6764     // private
6765     onMenuShow : function(e){
6766         this.el.addClass("x-btn-menu-active");
6767     },
6768     // private
6769     onMenuHide : function(e){
6770         this.el.removeClass("x-btn-menu-active");
6771     }   
6772 });
6773
6774 // Private utility class used by Button
6775 Roo.ButtonToggleMgr = function(){
6776    var groups = {};
6777    
6778    function toggleGroup(btn, state){
6779        if(state){
6780            var g = groups[btn.toggleGroup];
6781            for(var i = 0, l = g.length; i < l; i++){
6782                if(g[i] != btn){
6783                    g[i].toggle(false);
6784                }
6785            }
6786        }
6787    }
6788    
6789    return {
6790        register : function(btn){
6791            if(!btn.toggleGroup){
6792                return;
6793            }
6794            var g = groups[btn.toggleGroup];
6795            if(!g){
6796                g = groups[btn.toggleGroup] = [];
6797            }
6798            g.push(btn);
6799            btn.on("toggle", toggleGroup);
6800        },
6801        
6802        unregister : function(btn){
6803            if(!btn.toggleGroup){
6804                return;
6805            }
6806            var g = groups[btn.toggleGroup];
6807            if(g){
6808                g.remove(btn);
6809                btn.un("toggle", toggleGroup);
6810            }
6811        }
6812    };
6813 }();/*
6814  * Based on:
6815  * Ext JS Library 1.1.1
6816  * Copyright(c) 2006-2007, Ext JS, LLC.
6817  *
6818  * Originally Released Under LGPL - original licence link has changed is not relivant.
6819  *
6820  * Fork - LGPL
6821  * <script type="text/javascript">
6822  */
6823  
6824 /**
6825  * @class Roo.SplitButton
6826  * @extends Roo.Button
6827  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6828  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6829  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6830  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6831  * @cfg {String} arrowTooltip The title attribute of the arrow
6832  * @constructor
6833  * Create a new menu button
6834  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6835  * @param {Object} config The config object
6836  */
6837 Roo.SplitButton = function(renderTo, config){
6838     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6839     /**
6840      * @event arrowclick
6841      * Fires when this button's arrow is clicked
6842      * @param {SplitButton} this
6843      * @param {EventObject} e The click event
6844      */
6845     this.addEvents({"arrowclick":true});
6846 };
6847
6848 Roo.extend(Roo.SplitButton, Roo.Button, {
6849     render : function(renderTo){
6850         // this is one sweet looking template!
6851         var tpl = new Roo.Template(
6852             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6853             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6854             '<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>',
6855             "</tbody></table></td><td>",
6856             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6857             '<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>',
6858             "</tbody></table></td></tr></table>"
6859         );
6860         var btn = tpl.append(renderTo, [this.text, this.type], true);
6861         var btnEl = btn.child("button");
6862         if(this.cls){
6863             btn.addClass(this.cls);
6864         }
6865         if(this.icon){
6866             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6867         }
6868         if(this.iconCls){
6869             btnEl.addClass(this.iconCls);
6870             if(!this.cls){
6871                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6872             }
6873         }
6874         this.el = btn;
6875         if(this.handleMouseEvents){
6876             btn.on("mouseover", this.onMouseOver, this);
6877             btn.on("mouseout", this.onMouseOut, this);
6878             btn.on("mousedown", this.onMouseDown, this);
6879             btn.on("mouseup", this.onMouseUp, this);
6880         }
6881         btn.on(this.clickEvent, this.onClick, this);
6882         if(this.tooltip){
6883             if(typeof this.tooltip == 'object'){
6884                 Roo.QuickTips.tips(Roo.apply({
6885                       target: btnEl.id
6886                 }, this.tooltip));
6887             } else {
6888                 btnEl.dom[this.tooltipType] = this.tooltip;
6889             }
6890         }
6891         if(this.arrowTooltip){
6892             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6893         }
6894         if(this.hidden){
6895             this.hide();
6896         }
6897         if(this.disabled){
6898             this.disable();
6899         }
6900         if(this.pressed){
6901             this.el.addClass("x-btn-pressed");
6902         }
6903         if(Roo.isIE && !Roo.isIE7){
6904             this.autoWidth.defer(1, this);
6905         }else{
6906             this.autoWidth();
6907         }
6908         if(this.menu){
6909             this.menu.on("show", this.onMenuShow, this);
6910             this.menu.on("hide", this.onMenuHide, this);
6911         }
6912         this.fireEvent('render', this);
6913     },
6914
6915     // private
6916     autoWidth : function(){
6917         if(this.el){
6918             var tbl = this.el.child("table:first");
6919             var tbl2 = this.el.child("table:last");
6920             this.el.setWidth("auto");
6921             tbl.setWidth("auto");
6922             if(Roo.isIE7 && Roo.isStrict){
6923                 var ib = this.el.child('button:first');
6924                 if(ib && ib.getWidth() > 20){
6925                     ib.clip();
6926                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6927                 }
6928             }
6929             if(this.minWidth){
6930                 if(this.hidden){
6931                     this.el.beginMeasure();
6932                 }
6933                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6934                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6935                 }
6936                 if(this.hidden){
6937                     this.el.endMeasure();
6938                 }
6939             }
6940             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6941         } 
6942     },
6943     /**
6944      * Sets this button's click handler
6945      * @param {Function} handler The function to call when the button is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setHandler : function(handler, scope){
6949         this.handler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Sets this button's arrow click handler
6955      * @param {Function} handler The function to call when the arrow is clicked
6956      * @param {Object} scope (optional) Scope for the function passed above
6957      */
6958     setArrowHandler : function(handler, scope){
6959         this.arrowHandler = handler;
6960         this.scope = scope;  
6961     },
6962     
6963     /**
6964      * Focus the button
6965      */
6966     focus : function(){
6967         if(this.el){
6968             this.el.child("button:first").focus();
6969         }
6970     },
6971
6972     // private
6973     onClick : function(e){
6974         e.preventDefault();
6975         if(!this.disabled){
6976             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6977                 if(this.menu && !this.menu.isVisible()){
6978                     this.menu.show(this.el, this.menuAlign);
6979                 }
6980                 this.fireEvent("arrowclick", this, e);
6981                 if(this.arrowHandler){
6982                     this.arrowHandler.call(this.scope || this, this, e);
6983                 }
6984             }else{
6985                 this.fireEvent("click", this, e);
6986                 if(this.handler){
6987                     this.handler.call(this.scope || this, this, e);
6988                 }
6989             }
6990         }
6991     },
6992     // private
6993     onMouseDown : function(e){
6994         if(!this.disabled){
6995             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6996         }
6997     },
6998     // private
6999     onMouseUp : function(e){
7000         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7001     }   
7002 });
7003
7004
7005 // backwards compat
7006 Roo.MenuButton = Roo.SplitButton;/*
7007  * Based on:
7008  * Ext JS Library 1.1.1
7009  * Copyright(c) 2006-2007, Ext JS, LLC.
7010  *
7011  * Originally Released Under LGPL - original licence link has changed is not relivant.
7012  *
7013  * Fork - LGPL
7014  * <script type="text/javascript">
7015  */
7016
7017 /**
7018  * @class Roo.Toolbar
7019  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7020  * Basic Toolbar class.
7021  * @constructor
7022  * Creates a new Toolbar
7023  * @param {Object} container The config object
7024  */ 
7025 Roo.Toolbar = function(container, buttons, config)
7026 {
7027     /// old consturctor format still supported..
7028     if(container instanceof Array){ // omit the container for later rendering
7029         buttons = container;
7030         config = buttons;
7031         container = null;
7032     }
7033     if (typeof(container) == 'object' && container.xtype) {
7034         config = container;
7035         container = config.container;
7036         buttons = config.buttons || []; // not really - use items!!
7037     }
7038     var xitems = [];
7039     if (config && config.items) {
7040         xitems = config.items;
7041         delete config.items;
7042     }
7043     Roo.apply(this, config);
7044     this.buttons = buttons;
7045     
7046     if(container){
7047         this.render(container);
7048     }
7049     this.xitems = xitems;
7050     Roo.each(xitems, function(b) {
7051         this.add(b);
7052     }, this);
7053     
7054 };
7055
7056 Roo.Toolbar.prototype = {
7057     /**
7058      * @cfg {Array} items
7059      * array of button configs or elements to add (will be converted to a MixedCollection)
7060      */
7061     items: false,
7062     /**
7063      * @cfg {String/HTMLElement/Element} container
7064      * The id or element that will contain the toolbar
7065      */
7066     // private
7067     render : function(ct){
7068         this.el = Roo.get(ct);
7069         if(this.cls){
7070             this.el.addClass(this.cls);
7071         }
7072         // using a table allows for vertical alignment
7073         // 100% width is needed by Safari...
7074         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7075         this.tr = this.el.child("tr", true);
7076         var autoId = 0;
7077         this.items = new Roo.util.MixedCollection(false, function(o){
7078             return o.id || ("item" + (++autoId));
7079         });
7080         if(this.buttons){
7081             this.add.apply(this, this.buttons);
7082             delete this.buttons;
7083         }
7084     },
7085
7086     /**
7087      * Adds element(s) to the toolbar -- this function takes a variable number of 
7088      * arguments of mixed type and adds them to the toolbar.
7089      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7090      * <ul>
7091      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7092      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7093      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7094      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7095      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7096      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7097      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7098      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7099      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7100      * </ul>
7101      * @param {Mixed} arg2
7102      * @param {Mixed} etc.
7103      */
7104     add : function(){
7105         var a = arguments, l = a.length;
7106         for(var i = 0; i < l; i++){
7107             this._add(a[i]);
7108         }
7109     },
7110     // private..
7111     _add : function(el) {
7112         
7113         if (el.xtype) {
7114             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7115         }
7116         
7117         if (el.applyTo){ // some kind of form field
7118             return this.addField(el);
7119         } 
7120         if (el.render){ // some kind of Toolbar.Item
7121             return this.addItem(el);
7122         }
7123         if (typeof el == "string"){ // string
7124             if(el == "separator" || el == "-"){
7125                 return this.addSeparator();
7126             }
7127             if (el == " "){
7128                 return this.addSpacer();
7129             }
7130             if(el == "->"){
7131                 return this.addFill();
7132             }
7133             return this.addText(el);
7134             
7135         }
7136         if(el.tagName){ // element
7137             return this.addElement(el);
7138         }
7139         if(typeof el == "object"){ // must be button config?
7140             return this.addButton(el);
7141         }
7142         // and now what?!?!
7143         return false;
7144         
7145     },
7146     
7147     /**
7148      * Add an Xtype element
7149      * @param {Object} xtype Xtype Object
7150      * @return {Object} created Object
7151      */
7152     addxtype : function(e){
7153         return this.add(e);  
7154     },
7155     
7156     /**
7157      * Returns the Element for this toolbar.
7158      * @return {Roo.Element}
7159      */
7160     getEl : function(){
7161         return this.el;  
7162     },
7163     
7164     /**
7165      * Adds a separator
7166      * @return {Roo.Toolbar.Item} The separator item
7167      */
7168     addSeparator : function(){
7169         return this.addItem(new Roo.Toolbar.Separator());
7170     },
7171
7172     /**
7173      * Adds a spacer element
7174      * @return {Roo.Toolbar.Spacer} The spacer item
7175      */
7176     addSpacer : function(){
7177         return this.addItem(new Roo.Toolbar.Spacer());
7178     },
7179
7180     /**
7181      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7182      * @return {Roo.Toolbar.Fill} The fill item
7183      */
7184     addFill : function(){
7185         return this.addItem(new Roo.Toolbar.Fill());
7186     },
7187
7188     /**
7189      * Adds any standard HTML element to the toolbar
7190      * @param {String/HTMLElement/Element} el The element or id of the element to add
7191      * @return {Roo.Toolbar.Item} The element's item
7192      */
7193     addElement : function(el){
7194         return this.addItem(new Roo.Toolbar.Item(el));
7195     },
7196     /**
7197      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7198      * @type Roo.util.MixedCollection  
7199      */
7200     items : false,
7201      
7202     /**
7203      * Adds any Toolbar.Item or subclass
7204      * @param {Roo.Toolbar.Item} item
7205      * @return {Roo.Toolbar.Item} The item
7206      */
7207     addItem : function(item){
7208         var td = this.nextBlock();
7209         item.render(td);
7210         this.items.add(item);
7211         return item;
7212     },
7213     
7214     /**
7215      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7216      * @param {Object/Array} config A button config or array of configs
7217      * @return {Roo.Toolbar.Button/Array}
7218      */
7219     addButton : function(config){
7220         if(config instanceof Array){
7221             var buttons = [];
7222             for(var i = 0, len = config.length; i < len; i++) {
7223                 buttons.push(this.addButton(config[i]));
7224             }
7225             return buttons;
7226         }
7227         var b = config;
7228         if(!(config instanceof Roo.Toolbar.Button)){
7229             b = config.split ?
7230                 new Roo.Toolbar.SplitButton(config) :
7231                 new Roo.Toolbar.Button(config);
7232         }
7233         var td = this.nextBlock();
7234         b.render(td);
7235         this.items.add(b);
7236         return b;
7237     },
7238     
7239     /**
7240      * Adds text to the toolbar
7241      * @param {String} text The text to add
7242      * @return {Roo.Toolbar.Item} The element's item
7243      */
7244     addText : function(text){
7245         return this.addItem(new Roo.Toolbar.TextItem(text));
7246     },
7247     
7248     /**
7249      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7250      * @param {Number} index The index where the item is to be inserted
7251      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7252      * @return {Roo.Toolbar.Button/Item}
7253      */
7254     insertButton : function(index, item){
7255         if(item instanceof Array){
7256             var buttons = [];
7257             for(var i = 0, len = item.length; i < len; i++) {
7258                buttons.push(this.insertButton(index + i, item[i]));
7259             }
7260             return buttons;
7261         }
7262         if (!(item instanceof Roo.Toolbar.Button)){
7263            item = new Roo.Toolbar.Button(item);
7264         }
7265         var td = document.createElement("td");
7266         this.tr.insertBefore(td, this.tr.childNodes[index]);
7267         item.render(td);
7268         this.items.insert(index, item);
7269         return item;
7270     },
7271     
7272     /**
7273      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7274      * @param {Object} config
7275      * @return {Roo.Toolbar.Item} The element's item
7276      */
7277     addDom : function(config, returnEl){
7278         var td = this.nextBlock();
7279         Roo.DomHelper.overwrite(td, config);
7280         var ti = new Roo.Toolbar.Item(td.firstChild);
7281         ti.render(td);
7282         this.items.add(ti);
7283         return ti;
7284     },
7285
7286     /**
7287      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7288      * @type Roo.util.MixedCollection  
7289      */
7290     fields : false,
7291     
7292     /**
7293      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7294      * Note: the field should not have been rendered yet. For a field that has already been
7295      * rendered, use {@link #addElement}.
7296      * @param {Roo.form.Field} field
7297      * @return {Roo.ToolbarItem}
7298      */
7299      
7300       
7301     addField : function(field) {
7302         if (!this.fields) {
7303             var autoId = 0;
7304             this.fields = new Roo.util.MixedCollection(false, function(o){
7305                 return o.id || ("item" + (++autoId));
7306             });
7307
7308         }
7309         
7310         var td = this.nextBlock();
7311         field.render(td);
7312         var ti = new Roo.Toolbar.Item(td.firstChild);
7313         ti.render(td);
7314         this.items.add(ti);
7315         this.fields.add(field);
7316         return ti;
7317     },
7318     /**
7319      * Hide the toolbar
7320      * @method hide
7321      */
7322      
7323       
7324     hide : function()
7325     {
7326         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7327         this.el.child('div').hide();
7328     },
7329     /**
7330      * Show the toolbar
7331      * @method show
7332      */
7333     show : function()
7334     {
7335         this.el.child('div').show();
7336     },
7337       
7338     // private
7339     nextBlock : function(){
7340         var td = document.createElement("td");
7341         this.tr.appendChild(td);
7342         return td;
7343     },
7344
7345     // private
7346     destroy : function(){
7347         if(this.items){ // rendered?
7348             Roo.destroy.apply(Roo, this.items.items);
7349         }
7350         if(this.fields){ // rendered?
7351             Roo.destroy.apply(Roo, this.fields.items);
7352         }
7353         Roo.Element.uncache(this.el, this.tr);
7354     }
7355 };
7356
7357 /**
7358  * @class Roo.Toolbar.Item
7359  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7360  * @constructor
7361  * Creates a new Item
7362  * @param {HTMLElement} el 
7363  */
7364 Roo.Toolbar.Item = function(el){
7365     var cfg = {};
7366     if (typeof (el.xtype) != 'undefined') {
7367         cfg = el;
7368         el = cfg.el;
7369     }
7370     
7371     this.el = Roo.getDom(el);
7372     this.id = Roo.id(this.el);
7373     this.hidden = false;
7374     
7375     this.addEvents({
7376          /**
7377              * @event render
7378              * Fires when the button is rendered
7379              * @param {Button} this
7380              */
7381         'render': true
7382     });
7383     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7384 };
7385 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7386 //Roo.Toolbar.Item.prototype = {
7387     
7388     /**
7389      * Get this item's HTML Element
7390      * @return {HTMLElement}
7391      */
7392     getEl : function(){
7393        return this.el;  
7394     },
7395
7396     // private
7397     render : function(td){
7398         
7399          this.td = td;
7400         td.appendChild(this.el);
7401         
7402         this.fireEvent('render', this);
7403     },
7404     
7405     /**
7406      * Removes and destroys this item.
7407      */
7408     destroy : function(){
7409         this.td.parentNode.removeChild(this.td);
7410     },
7411     
7412     /**
7413      * Shows this item.
7414      */
7415     show: function(){
7416         this.hidden = false;
7417         this.td.style.display = "";
7418     },
7419     
7420     /**
7421      * Hides this item.
7422      */
7423     hide: function(){
7424         this.hidden = true;
7425         this.td.style.display = "none";
7426     },
7427     
7428     /**
7429      * Convenience function for boolean show/hide.
7430      * @param {Boolean} visible true to show/false to hide
7431      */
7432     setVisible: function(visible){
7433         if(visible) {
7434             this.show();
7435         }else{
7436             this.hide();
7437         }
7438     },
7439     
7440     /**
7441      * Try to focus this item.
7442      */
7443     focus : function(){
7444         Roo.fly(this.el).focus();
7445     },
7446     
7447     /**
7448      * Disables this item.
7449      */
7450     disable : function(){
7451         Roo.fly(this.td).addClass("x-item-disabled");
7452         this.disabled = true;
7453         this.el.disabled = true;
7454     },
7455     
7456     /**
7457      * Enables this item.
7458      */
7459     enable : function(){
7460         Roo.fly(this.td).removeClass("x-item-disabled");
7461         this.disabled = false;
7462         this.el.disabled = false;
7463     }
7464 });
7465
7466
7467 /**
7468  * @class Roo.Toolbar.Separator
7469  * @extends Roo.Toolbar.Item
7470  * A simple toolbar separator class
7471  * @constructor
7472  * Creates a new Separator
7473  */
7474 Roo.Toolbar.Separator = function(cfg){
7475     
7476     var s = document.createElement("span");
7477     s.className = "ytb-sep";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     
7482     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Spacer
7492  * @extends Roo.Toolbar.Item
7493  * A simple element that adds extra horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Spacer = function(cfg){
7498     var s = document.createElement("div");
7499     s.className = "ytb-spacer";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7504 };
7505 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7506     enable:Roo.emptyFn,
7507     disable:Roo.emptyFn,
7508     focus:Roo.emptyFn
7509 });
7510
7511 /**
7512  * @class Roo.Toolbar.Fill
7513  * @extends Roo.Toolbar.Spacer
7514  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7515  * @constructor
7516  * Creates a new Spacer
7517  */
7518 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7519     // private
7520     render : function(td){
7521         td.style.width = '100%';
7522         Roo.Toolbar.Fill.superclass.render.call(this, td);
7523     }
7524 });
7525
7526 /**
7527  * @class Roo.Toolbar.TextItem
7528  * @extends Roo.Toolbar.Item
7529  * A simple class that renders text directly into a toolbar.
7530  * @constructor
7531  * Creates a new TextItem
7532  * @cfg {string} text 
7533  */
7534 Roo.Toolbar.TextItem = function(cfg){
7535     var  text = cfg || "";
7536     if (typeof(cfg) == 'object') {
7537         text = cfg.text || "";
7538     }  else {
7539         cfg = null;
7540     }
7541     var s = document.createElement("span");
7542     s.className = "ytb-text";
7543     s.innerHTML = text;
7544     if (cfg) {
7545         cfg.el  = s;
7546     }
7547     
7548     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7549 };
7550 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7551     
7552      
7553     enable:Roo.emptyFn,
7554     disable:Roo.emptyFn,
7555     focus:Roo.emptyFn,
7556      /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.el.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.el.style.display = "none";
7570     }
7571     
7572 });
7573
7574 /**
7575  * @class Roo.Toolbar.Button
7576  * @extends Roo.Button
7577  * A button that renders into a toolbar.
7578  * @constructor
7579  * Creates a new Button
7580  * @param {Object} config A standard {@link Roo.Button} config object
7581  */
7582 Roo.Toolbar.Button = function(config){
7583     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7584 };
7585 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7586 {
7587     
7588     
7589     render : function(td){
7590         this.td = td;
7591         Roo.Toolbar.Button.superclass.render.call(this, td);
7592     },
7593     
7594     /**
7595      * Removes and destroys this button
7596      */
7597     destroy : function(){
7598         Roo.Toolbar.Button.superclass.destroy.call(this);
7599         this.td.parentNode.removeChild(this.td);
7600     },
7601     
7602     /**
7603      * Shows this button
7604      */
7605     show: function(){
7606         this.hidden = false;
7607         this.td.style.display = "";
7608     },
7609     
7610     /**
7611      * Hides this button
7612      */
7613     hide: function(){
7614         this.hidden = true;
7615         this.td.style.display = "none";
7616     },
7617
7618     /**
7619      * Disables this item
7620      */
7621     disable : function(){
7622         Roo.fly(this.td).addClass("x-item-disabled");
7623         this.disabled = true;
7624     },
7625
7626     /**
7627      * Enables this item
7628      */
7629     enable : function(){
7630         Roo.fly(this.td).removeClass("x-item-disabled");
7631         this.disabled = false;
7632     }
7633 });
7634 // backwards compat
7635 Roo.ToolbarButton = Roo.Toolbar.Button;
7636
7637 /**
7638  * @class Roo.Toolbar.SplitButton
7639  * @extends Roo.SplitButton
7640  * A menu button that renders into a toolbar.
7641  * @constructor
7642  * Creates a new SplitButton
7643  * @param {Object} config A standard {@link Roo.SplitButton} config object
7644  */
7645 Roo.Toolbar.SplitButton = function(config){
7646     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7647 };
7648 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7649     render : function(td){
7650         this.td = td;
7651         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7652     },
7653     
7654     /**
7655      * Removes and destroys this button
7656      */
7657     destroy : function(){
7658         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7659         this.td.parentNode.removeChild(this.td);
7660     },
7661     
7662     /**
7663      * Shows this button
7664      */
7665     show: function(){
7666         this.hidden = false;
7667         this.td.style.display = "";
7668     },
7669     
7670     /**
7671      * Hides this button
7672      */
7673     hide: function(){
7674         this.hidden = true;
7675         this.td.style.display = "none";
7676     }
7677 });
7678
7679 // backwards compat
7680 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  
7691 /**
7692  * @class Roo.PagingToolbar
7693  * @extends Roo.Toolbar
7694  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7695  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7696  * @constructor
7697  * Create a new PagingToolbar
7698  * @param {Object} config The config object
7699  */
7700 Roo.PagingToolbar = function(el, ds, config)
7701 {
7702     // old args format still supported... - xtype is prefered..
7703     if (typeof(el) == 'object' && el.xtype) {
7704         // created from xtype...
7705         config = el;
7706         ds = el.dataSource;
7707         el = config.container;
7708     }
7709     var items = [];
7710     if (config.items) {
7711         items = config.items;
7712         config.items = [];
7713     }
7714     
7715     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7716     this.ds = ds;
7717     this.cursor = 0;
7718     this.renderButtons(this.el);
7719     this.bind(ds);
7720     
7721     // supprot items array.
7722    
7723     Roo.each(items, function(e) {
7724         this.add(Roo.factory(e));
7725     },this);
7726     
7727 };
7728
7729 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7730    
7731     /**
7732      * @cfg {String/HTMLElement/Element} container
7733      * container The id or element that will contain the toolbar
7734      */
7735     /**
7736      * @cfg {Boolean} displayInfo
7737      * True to display the displayMsg (defaults to false)
7738      */
7739     
7740     
7741     /**
7742      * @cfg {Number} pageSize
7743      * The number of records to display per page (defaults to 20)
7744      */
7745     pageSize: 20,
7746     /**
7747      * @cfg {String} displayMsg
7748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7749      */
7750     displayMsg : 'Displaying {0} - {1} of {2}',
7751     /**
7752      * @cfg {String} emptyMsg
7753      * The message to display when no records are found (defaults to "No data to display")
7754      */
7755     emptyMsg : 'No data to display',
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Page")
7758      * @type String
7759      */
7760     beforePageText : "Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "of %0")
7763      * @type String
7764      */
7765     afterPageText : "of {0}",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "First Page")
7768      * @type String
7769      */
7770     firstText : "First Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Previous Page")
7773      * @type String
7774      */
7775     prevText : "Previous Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Next Page")
7778      * @type String
7779      */
7780     nextText : "Next Page",
7781     /**
7782      * Customizable piece of the default paging text (defaults to "Last Page")
7783      * @type String
7784      */
7785     lastText : "Last Page",
7786     /**
7787      * Customizable piece of the default paging text (defaults to "Refresh")
7788      * @type String
7789      */
7790     refreshText : "Refresh",
7791
7792     // private
7793     renderButtons : function(el){
7794         Roo.PagingToolbar.superclass.render.call(this, el);
7795         this.first = this.addButton({
7796             tooltip: this.firstText,
7797             cls: "x-btn-icon x-grid-page-first",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["first"])
7800         });
7801         this.prev = this.addButton({
7802             tooltip: this.prevText,
7803             cls: "x-btn-icon x-grid-page-prev",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["prev"])
7806         });
7807         //this.addSeparator();
7808         this.add(this.beforePageText);
7809         this.field = Roo.get(this.addDom({
7810            tag: "input",
7811            type: "text",
7812            size: "3",
7813            value: "1",
7814            cls: "x-grid-page-number"
7815         }).el);
7816         this.field.on("keydown", this.onPagingKeydown, this);
7817         this.field.on("focus", function(){this.dom.select();});
7818         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7819         this.field.setHeight(18);
7820         //this.addSeparator();
7821         this.next = this.addButton({
7822             tooltip: this.nextText,
7823             cls: "x-btn-icon x-grid-page-next",
7824             disabled: true,
7825             handler: this.onClick.createDelegate(this, ["next"])
7826         });
7827         this.last = this.addButton({
7828             tooltip: this.lastText,
7829             cls: "x-btn-icon x-grid-page-last",
7830             disabled: true,
7831             handler: this.onClick.createDelegate(this, ["last"])
7832         });
7833         //this.addSeparator();
7834         this.loading = this.addButton({
7835             tooltip: this.refreshText,
7836             cls: "x-btn-icon x-grid-loading",
7837             handler: this.onClick.createDelegate(this, ["refresh"])
7838         });
7839
7840         if(this.displayInfo){
7841             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7842         }
7843     },
7844
7845     // private
7846     updateInfo : function(){
7847         if(this.displayEl){
7848             var count = this.ds.getCount();
7849             var msg = count == 0 ?
7850                 this.emptyMsg :
7851                 String.format(
7852                     this.displayMsg,
7853                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7854                 );
7855             this.displayEl.update(msg);
7856         }
7857     },
7858
7859     // private
7860     onLoad : function(ds, r, o){
7861        this.cursor = o.params ? o.params.start : 0;
7862        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7863
7864        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7865        this.field.dom.value = ap;
7866        this.first.setDisabled(ap == 1);
7867        this.prev.setDisabled(ap == 1);
7868        this.next.setDisabled(ap == ps);
7869        this.last.setDisabled(ap == ps);
7870        this.loading.enable();
7871        this.updateInfo();
7872     },
7873
7874     // private
7875     getPageData : function(){
7876         var total = this.ds.getTotalCount();
7877         return {
7878             total : total,
7879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7881         };
7882     },
7883
7884     // private
7885     onLoadError : function(){
7886         this.loading.enable();
7887     },
7888
7889     // private
7890     onPagingKeydown : function(e){
7891         var k = e.getKey();
7892         var d = this.getPageData();
7893         if(k == e.RETURN){
7894             var v = this.field.dom.value, pageNum;
7895             if(!v || isNaN(pageNum = parseInt(v, 10))){
7896                 this.field.dom.value = d.activePage;
7897                 return;
7898             }
7899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7901             e.stopEvent();
7902         }
7903         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))
7904         {
7905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7906           this.field.dom.value = pageNum;
7907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7908           e.stopEvent();
7909         }
7910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7911         {
7912           var v = this.field.dom.value, pageNum; 
7913           var increment = (e.shiftKey) ? 10 : 1;
7914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7915             increment *= -1;
7916           }
7917           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7918             this.field.dom.value = d.activePage;
7919             return;
7920           }
7921           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7922           {
7923             this.field.dom.value = parseInt(v, 10) + increment;
7924             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7925             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7926           }
7927           e.stopEvent();
7928         }
7929     },
7930
7931     // private
7932     beforeLoad : function(){
7933         if(this.loading){
7934             this.loading.disable();
7935         }
7936     },
7937
7938     // private
7939     onClick : function(which){
7940         var ds = this.ds;
7941         switch(which){
7942             case "first":
7943                 ds.load({params:{start: 0, limit: this.pageSize}});
7944             break;
7945             case "prev":
7946                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7947             break;
7948             case "next":
7949                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7950             break;
7951             case "last":
7952                 var total = ds.getTotalCount();
7953                 var extra = total % this.pageSize;
7954                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7955                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7956             break;
7957             case "refresh":
7958                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7959             break;
7960         }
7961     },
7962
7963     /**
7964      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7965      * @param {Roo.data.Store} store The data store to unbind
7966      */
7967     unbind : function(ds){
7968         ds.un("beforeload", this.beforeLoad, this);
7969         ds.un("load", this.onLoad, this);
7970         ds.un("loadexception", this.onLoadError, this);
7971         ds.un("remove", this.updateInfo, this);
7972         ds.un("add", this.updateInfo, this);
7973         this.ds = undefined;
7974     },
7975
7976     /**
7977      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7978      * @param {Roo.data.Store} store The data store to bind
7979      */
7980     bind : function(ds){
7981         ds.on("beforeload", this.beforeLoad, this);
7982         ds.on("load", this.onLoad, this);
7983         ds.on("loadexception", this.onLoadError, this);
7984         ds.on("remove", this.updateInfo, this);
7985         ds.on("add", this.updateInfo, this);
7986         this.ds = ds;
7987     }
7988 });/*
7989  * Based on:
7990  * Ext JS Library 1.1.1
7991  * Copyright(c) 2006-2007, Ext JS, LLC.
7992  *
7993  * Originally Released Under LGPL - original licence link has changed is not relivant.
7994  *
7995  * Fork - LGPL
7996  * <script type="text/javascript">
7997  */
7998
7999 /**
8000  * @class Roo.Resizable
8001  * @extends Roo.util.Observable
8002  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8003  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8004  * 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
8005  * the element will be wrapped for you automatically.</p>
8006  * <p>Here is the list of valid resize handles:</p>
8007  * <pre>
8008 Value   Description
8009 ------  -------------------
8010  'n'     north
8011  's'     south
8012  'e'     east
8013  'w'     west
8014  'nw'    northwest
8015  'sw'    southwest
8016  'se'    southeast
8017  'ne'    northeast
8018  'hd'    horizontal drag
8019  'all'   all
8020 </pre>
8021  * <p>Here's an example showing the creation of a typical Resizable:</p>
8022  * <pre><code>
8023 var resizer = new Roo.Resizable("element-id", {
8024     handles: 'all',
8025     minWidth: 200,
8026     minHeight: 100,
8027     maxWidth: 500,
8028     maxHeight: 400,
8029     pinned: true
8030 });
8031 resizer.on("resize", myHandler);
8032 </code></pre>
8033  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8034  * resizer.east.setDisplayed(false);</p>
8035  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8036  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8037  * resize operation's new size (defaults to [0, 0])
8038  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8039  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8040  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8041  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8042  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8043  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8044  * @cfg {Number} width The width of the element in pixels (defaults to null)
8045  * @cfg {Number} height The height of the element in pixels (defaults to null)
8046  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8047  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8048  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8049  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8050  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8051  * in favor of the handles config option (defaults to false)
8052  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8053  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8054  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8055  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8056  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8057  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8058  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8059  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8060  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8061  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8062  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8063  * @constructor
8064  * Create a new resizable component
8065  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8066  * @param {Object} config configuration options
8067   */
8068 Roo.Resizable = function(el, config)
8069 {
8070     this.el = Roo.get(el);
8071
8072     if(config && config.wrap){
8073         config.resizeChild = this.el;
8074         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8075         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8076         this.el.setStyle("overflow", "hidden");
8077         this.el.setPositioning(config.resizeChild.getPositioning());
8078         config.resizeChild.clearPositioning();
8079         if(!config.width || !config.height){
8080             var csize = config.resizeChild.getSize();
8081             this.el.setSize(csize.width, csize.height);
8082         }
8083         if(config.pinned && !config.adjustments){
8084             config.adjustments = "auto";
8085         }
8086     }
8087
8088     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8089     this.proxy.unselectable();
8090     this.proxy.enableDisplayMode('block');
8091
8092     Roo.apply(this, config);
8093
8094     if(this.pinned){
8095         this.disableTrackOver = true;
8096         this.el.addClass("x-resizable-pinned");
8097     }
8098     // if the element isn't positioned, make it relative
8099     var position = this.el.getStyle("position");
8100     if(position != "absolute" && position != "fixed"){
8101         this.el.setStyle("position", "relative");
8102     }
8103     if(!this.handles){ // no handles passed, must be legacy style
8104         this.handles = 's,e,se';
8105         if(this.multiDirectional){
8106             this.handles += ',n,w';
8107         }
8108     }
8109     if(this.handles == "all"){
8110         this.handles = "n s e w ne nw se sw";
8111     }
8112     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8113     var ps = Roo.Resizable.positions;
8114     for(var i = 0, len = hs.length; i < len; i++){
8115         if(hs[i] && ps[hs[i]]){
8116             var pos = ps[hs[i]];
8117             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8118         }
8119     }
8120     // legacy
8121     this.corner = this.southeast;
8122     
8123     // updateBox = the box can move..
8124     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8125         this.updateBox = true;
8126     }
8127
8128     this.activeHandle = null;
8129
8130     if(this.resizeChild){
8131         if(typeof this.resizeChild == "boolean"){
8132             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8133         }else{
8134             this.resizeChild = Roo.get(this.resizeChild, true);
8135         }
8136     }
8137     
8138     if(this.adjustments == "auto"){
8139         var rc = this.resizeChild;
8140         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8141         if(rc && (hw || hn)){
8142             rc.position("relative");
8143             rc.setLeft(hw ? hw.el.getWidth() : 0);
8144             rc.setTop(hn ? hn.el.getHeight() : 0);
8145         }
8146         this.adjustments = [
8147             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8148             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8149         ];
8150     }
8151
8152     if(this.draggable){
8153         this.dd = this.dynamic ?
8154             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8155         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8156     }
8157
8158     // public events
8159     this.addEvents({
8160         /**
8161          * @event beforeresize
8162          * Fired before resize is allowed. Set enabled to false to cancel resize.
8163          * @param {Roo.Resizable} this
8164          * @param {Roo.EventObject} e The mousedown event
8165          */
8166         "beforeresize" : true,
8167         /**
8168          * @event resizing
8169          * Fired a resizing.
8170          * @param {Roo.Resizable} this
8171          * @param {Number} x The new x position
8172          * @param {Number} y The new y position
8173          * @param {Number} w The new w width
8174          * @param {Number} h The new h hight
8175          * @param {Roo.EventObject} e The mouseup event
8176          */
8177         "resizing" : true,
8178         /**
8179          * @event resize
8180          * Fired after a resize.
8181          * @param {Roo.Resizable} this
8182          * @param {Number} width The new width
8183          * @param {Number} height The new height
8184          * @param {Roo.EventObject} e The mouseup event
8185          */
8186         "resize" : true
8187     });
8188
8189     if(this.width !== null && this.height !== null){
8190         this.resizeTo(this.width, this.height);
8191     }else{
8192         this.updateChildSize();
8193     }
8194     if(Roo.isIE){
8195         this.el.dom.style.zoom = 1;
8196     }
8197     Roo.Resizable.superclass.constructor.call(this);
8198 };
8199
8200 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8201         resizeChild : false,
8202         adjustments : [0, 0],
8203         minWidth : 5,
8204         minHeight : 5,
8205         maxWidth : 10000,
8206         maxHeight : 10000,
8207         enabled : true,
8208         animate : false,
8209         duration : .35,
8210         dynamic : false,
8211         handles : false,
8212         multiDirectional : false,
8213         disableTrackOver : false,
8214         easing : 'easeOutStrong',
8215         widthIncrement : 0,
8216         heightIncrement : 0,
8217         pinned : false,
8218         width : null,
8219         height : null,
8220         preserveRatio : false,
8221         transparent: false,
8222         minX: 0,
8223         minY: 0,
8224         draggable: false,
8225
8226         /**
8227          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8228          */
8229         constrainTo: undefined,
8230         /**
8231          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8232          */
8233         resizeRegion: undefined,
8234
8235
8236     /**
8237      * Perform a manual resize
8238      * @param {Number} width
8239      * @param {Number} height
8240      */
8241     resizeTo : function(width, height){
8242         this.el.setSize(width, height);
8243         this.updateChildSize();
8244         this.fireEvent("resize", this, width, height, null);
8245     },
8246
8247     // private
8248     startSizing : function(e, handle){
8249         this.fireEvent("beforeresize", this, e);
8250         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8251
8252             if(!this.overlay){
8253                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8254                 this.overlay.unselectable();
8255                 this.overlay.enableDisplayMode("block");
8256                 this.overlay.on("mousemove", this.onMouseMove, this);
8257                 this.overlay.on("mouseup", this.onMouseUp, this);
8258             }
8259             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8260
8261             this.resizing = true;
8262             this.startBox = this.el.getBox();
8263             this.startPoint = e.getXY();
8264             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8265                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8266
8267             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8268             this.overlay.show();
8269
8270             if(this.constrainTo) {
8271                 var ct = Roo.get(this.constrainTo);
8272                 this.resizeRegion = ct.getRegion().adjust(
8273                     ct.getFrameWidth('t'),
8274                     ct.getFrameWidth('l'),
8275                     -ct.getFrameWidth('b'),
8276                     -ct.getFrameWidth('r')
8277                 );
8278             }
8279
8280             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8281             this.proxy.show();
8282             this.proxy.setBox(this.startBox);
8283             if(!this.dynamic){
8284                 this.proxy.setStyle('visibility', 'visible');
8285             }
8286         }
8287     },
8288
8289     // private
8290     onMouseDown : function(handle, e){
8291         if(this.enabled){
8292             e.stopEvent();
8293             this.activeHandle = handle;
8294             this.startSizing(e, handle);
8295         }
8296     },
8297
8298     // private
8299     onMouseUp : function(e){
8300         var size = this.resizeElement();
8301         this.resizing = false;
8302         this.handleOut();
8303         this.overlay.hide();
8304         this.proxy.hide();
8305         this.fireEvent("resize", this, size.width, size.height, e);
8306     },
8307
8308     // private
8309     updateChildSize : function(){
8310         
8311         if(this.resizeChild){
8312             var el = this.el;
8313             var child = this.resizeChild;
8314             var adj = this.adjustments;
8315             if(el.dom.offsetWidth){
8316                 var b = el.getSize(true);
8317                 child.setSize(b.width+adj[0], b.height+adj[1]);
8318             }
8319             // Second call here for IE
8320             // The first call enables instant resizing and
8321             // the second call corrects scroll bars if they
8322             // exist
8323             if(Roo.isIE){
8324                 setTimeout(function(){
8325                     if(el.dom.offsetWidth){
8326                         var b = el.getSize(true);
8327                         child.setSize(b.width+adj[0], b.height+adj[1]);
8328                     }
8329                 }, 10);
8330             }
8331         }
8332     },
8333
8334     // private
8335     snap : function(value, inc, min){
8336         if(!inc || !value) {
8337             return value;
8338         }
8339         var newValue = value;
8340         var m = value % inc;
8341         if(m > 0){
8342             if(m > (inc/2)){
8343                 newValue = value + (inc-m);
8344             }else{
8345                 newValue = value - m;
8346             }
8347         }
8348         return Math.max(min, newValue);
8349     },
8350
8351     // private
8352     resizeElement : function(){
8353         var box = this.proxy.getBox();
8354         if(this.updateBox){
8355             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8356         }else{
8357             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8358         }
8359         this.updateChildSize();
8360         if(!this.dynamic){
8361             this.proxy.hide();
8362         }
8363         return box;
8364     },
8365
8366     // private
8367     constrain : function(v, diff, m, mx){
8368         if(v - diff < m){
8369             diff = v - m;
8370         }else if(v - diff > mx){
8371             diff = mx - v;
8372         }
8373         return diff;
8374     },
8375
8376     // private
8377     onMouseMove : function(e){
8378         
8379         if(this.enabled){
8380             try{// try catch so if something goes wrong the user doesn't get hung
8381
8382             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8383                 return;
8384             }
8385
8386             //var curXY = this.startPoint;
8387             var curSize = this.curSize || this.startBox;
8388             var x = this.startBox.x, y = this.startBox.y;
8389             var ox = x, oy = y;
8390             var w = curSize.width, h = curSize.height;
8391             var ow = w, oh = h;
8392             var mw = this.minWidth, mh = this.minHeight;
8393             var mxw = this.maxWidth, mxh = this.maxHeight;
8394             var wi = this.widthIncrement;
8395             var hi = this.heightIncrement;
8396
8397             var eventXY = e.getXY();
8398             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8399             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8400
8401             var pos = this.activeHandle.position;
8402
8403             switch(pos){
8404                 case "east":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     break;
8408              
8409                 case "south":
8410                     h += diffY;
8411                     h = Math.min(Math.max(mh, h), mxh);
8412                     break;
8413                 case "southeast":
8414                     w += diffX;
8415                     h += diffY;
8416                     w = Math.min(Math.max(mw, w), mxw);
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     break;
8419                 case "north":
8420                     diffY = this.constrain(h, diffY, mh, mxh);
8421                     y += diffY;
8422                     h -= diffY;
8423                     break;
8424                 case "hdrag":
8425                     
8426                     if (wi) {
8427                         var adiffX = Math.abs(diffX);
8428                         var sub = (adiffX % wi); // how much 
8429                         if (sub > (wi/2)) { // far enough to snap
8430                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8431                         } else {
8432                             // remove difference.. 
8433                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8434                         }
8435                     }
8436                     x += diffX;
8437                     x = Math.max(this.minX, x);
8438                     break;
8439                 case "west":
8440                     diffX = this.constrain(w, diffX, mw, mxw);
8441                     x += diffX;
8442                     w -= diffX;
8443                     break;
8444                 case "northeast":
8445                     w += diffX;
8446                     w = Math.min(Math.max(mw, w), mxw);
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "northwest":
8452                     diffX = this.constrain(w, diffX, mw, mxw);
8453                     diffY = this.constrain(h, diffY, mh, mxh);
8454                     y += diffY;
8455                     h -= diffY;
8456                     x += diffX;
8457                     w -= diffX;
8458                     break;
8459                case "southwest":
8460                     diffX = this.constrain(w, diffX, mw, mxw);
8461                     h += diffY;
8462                     h = Math.min(Math.max(mh, h), mxh);
8463                     x += diffX;
8464                     w -= diffX;
8465                     break;
8466             }
8467
8468             var sw = this.snap(w, wi, mw);
8469             var sh = this.snap(h, hi, mh);
8470             if(sw != w || sh != h){
8471                 switch(pos){
8472                     case "northeast":
8473                         y -= sh - h;
8474                     break;
8475                     case "north":
8476                         y -= sh - h;
8477                         break;
8478                     case "southwest":
8479                         x -= sw - w;
8480                     break;
8481                     case "west":
8482                         x -= sw - w;
8483                         break;
8484                     case "northwest":
8485                         x -= sw - w;
8486                         y -= sh - h;
8487                     break;
8488                 }
8489                 w = sw;
8490                 h = sh;
8491             }
8492
8493             if(this.preserveRatio){
8494                 switch(pos){
8495                     case "southeast":
8496                     case "east":
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                        break;
8501                     case "south":
8502                         w = ow * (h/oh);
8503                         w = Math.min(Math.max(mw, w), mxw);
8504                         h = oh * (w/ow);
8505                         break;
8506                     case "northeast":
8507                         w = ow * (h/oh);
8508                         w = Math.min(Math.max(mw, w), mxw);
8509                         h = oh * (w/ow);
8510                     break;
8511                     case "north":
8512                         var tw = w;
8513                         w = ow * (h/oh);
8514                         w = Math.min(Math.max(mw, w), mxw);
8515                         h = oh * (w/ow);
8516                         x += (tw - w) / 2;
8517                         break;
8518                     case "southwest":
8519                         h = oh * (w/ow);
8520                         h = Math.min(Math.max(mh, h), mxh);
8521                         var tw = w;
8522                         w = ow * (h/oh);
8523                         x += tw - w;
8524                         break;
8525                     case "west":
8526                         var th = h;
8527                         h = oh * (w/ow);
8528                         h = Math.min(Math.max(mh, h), mxh);
8529                         y += (th - h) / 2;
8530                         var tw = w;
8531                         w = ow * (h/oh);
8532                         x += tw - w;
8533                        break;
8534                     case "northwest":
8535                         var tw = w;
8536                         var th = h;
8537                         h = oh * (w/ow);
8538                         h = Math.min(Math.max(mh, h), mxh);
8539                         w = ow * (h/oh);
8540                         y += th - h;
8541                         x += tw - w;
8542                        break;
8543
8544                 }
8545             }
8546             if (pos == 'hdrag') {
8547                 w = ow;
8548             }
8549             this.proxy.setBounds(x, y, w, h);
8550             if(this.dynamic){
8551                 this.resizeElement();
8552             }
8553             }catch(e){}
8554         }
8555         this.fireEvent("resizing", this, x, y, w, h, e);
8556     },
8557
8558     // private
8559     handleOver : function(){
8560         if(this.enabled){
8561             this.el.addClass("x-resizable-over");
8562         }
8563     },
8564
8565     // private
8566     handleOut : function(){
8567         if(!this.resizing){
8568             this.el.removeClass("x-resizable-over");
8569         }
8570     },
8571
8572     /**
8573      * Returns the element this component is bound to.
8574      * @return {Roo.Element}
8575      */
8576     getEl : function(){
8577         return this.el;
8578     },
8579
8580     /**
8581      * Returns the resizeChild element (or null).
8582      * @return {Roo.Element}
8583      */
8584     getResizeChild : function(){
8585         return this.resizeChild;
8586     },
8587     groupHandler : function()
8588     {
8589         
8590     },
8591     /**
8592      * Destroys this resizable. If the element was wrapped and
8593      * removeEl is not true then the element remains.
8594      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8595      */
8596     destroy : function(removeEl){
8597         this.proxy.remove();
8598         if(this.overlay){
8599             this.overlay.removeAllListeners();
8600             this.overlay.remove();
8601         }
8602         var ps = Roo.Resizable.positions;
8603         for(var k in ps){
8604             if(typeof ps[k] != "function" && this[ps[k]]){
8605                 var h = this[ps[k]];
8606                 h.el.removeAllListeners();
8607                 h.el.remove();
8608             }
8609         }
8610         if(removeEl){
8611             this.el.update("");
8612             this.el.remove();
8613         }
8614     }
8615 });
8616
8617 // private
8618 // hash to map config positions to true positions
8619 Roo.Resizable.positions = {
8620     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8621     hd: "hdrag"
8622 };
8623
8624 // private
8625 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8626     if(!this.tpl){
8627         // only initialize the template if resizable is used
8628         var tpl = Roo.DomHelper.createTemplate(
8629             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8630         );
8631         tpl.compile();
8632         Roo.Resizable.Handle.prototype.tpl = tpl;
8633     }
8634     this.position = pos;
8635     this.rz = rz;
8636     // show north drag fro topdra
8637     var handlepos = pos == 'hdrag' ? 'north' : pos;
8638     
8639     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8640     if (pos == 'hdrag') {
8641         this.el.setStyle('cursor', 'pointer');
8642     }
8643     this.el.unselectable();
8644     if(transparent){
8645         this.el.setOpacity(0);
8646     }
8647     this.el.on("mousedown", this.onMouseDown, this);
8648     if(!disableTrackOver){
8649         this.el.on("mouseover", this.onMouseOver, this);
8650         this.el.on("mouseout", this.onMouseOut, this);
8651     }
8652 };
8653
8654 // private
8655 Roo.Resizable.Handle.prototype = {
8656     afterResize : function(rz){
8657         Roo.log('after?');
8658         // do nothing
8659     },
8660     // private
8661     onMouseDown : function(e){
8662         this.rz.onMouseDown(this, e);
8663     },
8664     // private
8665     onMouseOver : function(e){
8666         this.rz.handleOver(this, e);
8667     },
8668     // private
8669     onMouseOut : function(e){
8670         this.rz.handleOut(this, e);
8671     }
8672 };/*
8673  * Based on:
8674  * Ext JS Library 1.1.1
8675  * Copyright(c) 2006-2007, Ext JS, LLC.
8676  *
8677  * Originally Released Under LGPL - original licence link has changed is not relivant.
8678  *
8679  * Fork - LGPL
8680  * <script type="text/javascript">
8681  */
8682
8683 /**
8684  * @class Roo.Editor
8685  * @extends Roo.Component
8686  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8687  * @constructor
8688  * Create a new Editor
8689  * @param {Roo.form.Field} field The Field object (or descendant)
8690  * @param {Object} config The config object
8691  */
8692 Roo.Editor = function(field, config){
8693     Roo.Editor.superclass.constructor.call(this, config);
8694     this.field = field;
8695     this.addEvents({
8696         /**
8697              * @event beforestartedit
8698              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8699              * false from the handler of this event.
8700              * @param {Editor} this
8701              * @param {Roo.Element} boundEl The underlying element bound to this editor
8702              * @param {Mixed} value The field value being set
8703              */
8704         "beforestartedit" : true,
8705         /**
8706              * @event startedit
8707              * Fires when this editor is displayed
8708              * @param {Roo.Element} boundEl The underlying element bound to this editor
8709              * @param {Mixed} value The starting field value
8710              */
8711         "startedit" : true,
8712         /**
8713              * @event beforecomplete
8714              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8715              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8716              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8717              * event will not fire since no edit actually occurred.
8718              * @param {Editor} this
8719              * @param {Mixed} value The current field value
8720              * @param {Mixed} startValue The original field value
8721              */
8722         "beforecomplete" : true,
8723         /**
8724              * @event complete
8725              * Fires after editing is complete and any changed value has been written to the underlying field.
8726              * @param {Editor} this
8727              * @param {Mixed} value The current field value
8728              * @param {Mixed} startValue The original field value
8729              */
8730         "complete" : true,
8731         /**
8732          * @event specialkey
8733          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8734          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8735          * @param {Roo.form.Field} this
8736          * @param {Roo.EventObject} e The event object
8737          */
8738         "specialkey" : true
8739     });
8740 };
8741
8742 Roo.extend(Roo.Editor, Roo.Component, {
8743     /**
8744      * @cfg {Boolean/String} autosize
8745      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8746      * or "height" to adopt the height only (defaults to false)
8747      */
8748     /**
8749      * @cfg {Boolean} revertInvalid
8750      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8751      * validation fails (defaults to true)
8752      */
8753     /**
8754      * @cfg {Boolean} ignoreNoChange
8755      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8756      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8757      * will never be ignored.
8758      */
8759     /**
8760      * @cfg {Boolean} hideEl
8761      * False to keep the bound element visible while the editor is displayed (defaults to true)
8762      */
8763     /**
8764      * @cfg {Mixed} value
8765      * The data value of the underlying field (defaults to "")
8766      */
8767     value : "",
8768     /**
8769      * @cfg {String} alignment
8770      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8771      */
8772     alignment: "c-c?",
8773     /**
8774      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8775      * for bottom-right shadow (defaults to "frame")
8776      */
8777     shadow : "frame",
8778     /**
8779      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8780      */
8781     constrain : false,
8782     /**
8783      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8784      */
8785     completeOnEnter : false,
8786     /**
8787      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8788      */
8789     cancelOnEsc : false,
8790     /**
8791      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8792      */
8793     updateEl : false,
8794
8795     // private
8796     onRender : function(ct, position){
8797         this.el = new Roo.Layer({
8798             shadow: this.shadow,
8799             cls: "x-editor",
8800             parentEl : ct,
8801             shim : this.shim,
8802             shadowOffset:4,
8803             id: this.id,
8804             constrain: this.constrain
8805         });
8806         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8807         if(this.field.msgTarget != 'title'){
8808             this.field.msgTarget = 'qtip';
8809         }
8810         this.field.render(this.el);
8811         if(Roo.isGecko){
8812             this.field.el.dom.setAttribute('autocomplete', 'off');
8813         }
8814         this.field.on("specialkey", this.onSpecialKey, this);
8815         if(this.swallowKeys){
8816             this.field.el.swallowEvent(['keydown','keypress']);
8817         }
8818         this.field.show();
8819         this.field.on("blur", this.onBlur, this);
8820         if(this.field.grow){
8821             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8822         }
8823     },
8824
8825     onSpecialKey : function(field, e)
8826     {
8827         //Roo.log('editor onSpecialKey');
8828         if(this.completeOnEnter && e.getKey() == e.ENTER){
8829             e.stopEvent();
8830             this.completeEdit();
8831             return;
8832         }
8833         // do not fire special key otherwise it might hide close the editor...
8834         if(e.getKey() == e.ENTER){    
8835             return;
8836         }
8837         if(this.cancelOnEsc && e.getKey() == e.ESC){
8838             this.cancelEdit();
8839             return;
8840         } 
8841         this.fireEvent('specialkey', field, e);
8842     
8843     },
8844
8845     /**
8846      * Starts the editing process and shows the editor.
8847      * @param {String/HTMLElement/Element} el The element to edit
8848      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8849       * to the innerHTML of el.
8850      */
8851     startEdit : function(el, value){
8852         if(this.editing){
8853             this.completeEdit();
8854         }
8855         this.boundEl = Roo.get(el);
8856         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8857         if(!this.rendered){
8858             this.render(this.parentEl || document.body);
8859         }
8860         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8861             return;
8862         }
8863         this.startValue = v;
8864         this.field.setValue(v);
8865         if(this.autoSize){
8866             var sz = this.boundEl.getSize();
8867             switch(this.autoSize){
8868                 case "width":
8869                 this.setSize(sz.width,  "");
8870                 break;
8871                 case "height":
8872                 this.setSize("",  sz.height);
8873                 break;
8874                 default:
8875                 this.setSize(sz.width,  sz.height);
8876             }
8877         }
8878         this.el.alignTo(this.boundEl, this.alignment);
8879         this.editing = true;
8880         if(Roo.QuickTips){
8881             Roo.QuickTips.disable();
8882         }
8883         this.show();
8884     },
8885
8886     /**
8887      * Sets the height and width of this editor.
8888      * @param {Number} width The new width
8889      * @param {Number} height The new height
8890      */
8891     setSize : function(w, h){
8892         this.field.setSize(w, h);
8893         if(this.el){
8894             this.el.sync();
8895         }
8896     },
8897
8898     /**
8899      * Realigns the editor to the bound field based on the current alignment config value.
8900      */
8901     realign : function(){
8902         this.el.alignTo(this.boundEl, this.alignment);
8903     },
8904
8905     /**
8906      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8907      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8908      */
8909     completeEdit : function(remainVisible){
8910         if(!this.editing){
8911             return;
8912         }
8913         var v = this.getValue();
8914         if(this.revertInvalid !== false && !this.field.isValid()){
8915             v = this.startValue;
8916             this.cancelEdit(true);
8917         }
8918         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8919             this.editing = false;
8920             this.hide();
8921             return;
8922         }
8923         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8924             this.editing = false;
8925             if(this.updateEl && this.boundEl){
8926                 this.boundEl.update(v);
8927             }
8928             if(remainVisible !== true){
8929                 this.hide();
8930             }
8931             this.fireEvent("complete", this, v, this.startValue);
8932         }
8933     },
8934
8935     // private
8936     onShow : function(){
8937         this.el.show();
8938         if(this.hideEl !== false){
8939             this.boundEl.hide();
8940         }
8941         this.field.show();
8942         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8943             this.fixIEFocus = true;
8944             this.deferredFocus.defer(50, this);
8945         }else{
8946             this.field.focus();
8947         }
8948         this.fireEvent("startedit", this.boundEl, this.startValue);
8949     },
8950
8951     deferredFocus : function(){
8952         if(this.editing){
8953             this.field.focus();
8954         }
8955     },
8956
8957     /**
8958      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8959      * reverted to the original starting value.
8960      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8961      * cancel (defaults to false)
8962      */
8963     cancelEdit : function(remainVisible){
8964         if(this.editing){
8965             this.setValue(this.startValue);
8966             if(remainVisible !== true){
8967                 this.hide();
8968             }
8969         }
8970     },
8971
8972     // private
8973     onBlur : function(){
8974         if(this.allowBlur !== true && this.editing){
8975             this.completeEdit();
8976         }
8977     },
8978
8979     // private
8980     onHide : function(){
8981         if(this.editing){
8982             this.completeEdit();
8983             return;
8984         }
8985         this.field.blur();
8986         if(this.field.collapse){
8987             this.field.collapse();
8988         }
8989         this.el.hide();
8990         if(this.hideEl !== false){
8991             this.boundEl.show();
8992         }
8993         if(Roo.QuickTips){
8994             Roo.QuickTips.enable();
8995         }
8996     },
8997
8998     /**
8999      * Sets the data value of the editor
9000      * @param {Mixed} value Any valid value supported by the underlying field
9001      */
9002     setValue : function(v){
9003         this.field.setValue(v);
9004     },
9005
9006     /**
9007      * Gets the data value of the editor
9008      * @return {Mixed} The data value
9009      */
9010     getValue : function(){
9011         return this.field.getValue();
9012     }
9013 });/*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023  
9024 /**
9025  * @class Roo.BasicDialog
9026  * @extends Roo.util.Observable
9027  * @parent none builder
9028  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9029  * <pre><code>
9030 var dlg = new Roo.BasicDialog("my-dlg", {
9031     height: 200,
9032     width: 300,
9033     minHeight: 100,
9034     minWidth: 150,
9035     modal: true,
9036     proxyDrag: true,
9037     shadow: true
9038 });
9039 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9040 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9041 dlg.addButton('Cancel', dlg.hide, dlg);
9042 dlg.show();
9043 </code></pre>
9044   <b>A Dialog should always be a direct child of the body element.</b>
9045  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9046  * @cfg {String} title Default text to display in the title bar (defaults to null)
9047  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9048  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9049  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9050  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9051  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9052  * (defaults to null with no animation)
9053  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9054  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9055  * property for valid values (defaults to 'all')
9056  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9057  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9058  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9059  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9060  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9061  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9062  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9063  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9064  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9065  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9066  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9067  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9068  * draggable = true (defaults to false)
9069  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9070  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9071  * shadow (defaults to false)
9072  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9073  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9074  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9075  * @cfg {Array} buttons Array of buttons
9076  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9077  * @constructor
9078  * Create a new BasicDialog.
9079  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9080  * @param {Object} config Configuration options
9081  */
9082 Roo.BasicDialog = function(el, config){
9083     this.el = Roo.get(el);
9084     var dh = Roo.DomHelper;
9085     if(!this.el && config && config.autoCreate){
9086         if(typeof config.autoCreate == "object"){
9087             if(!config.autoCreate.id){
9088                 config.autoCreate.id = el;
9089             }
9090             this.el = dh.append(document.body,
9091                         config.autoCreate, true);
9092         }else{
9093             this.el = dh.append(document.body,
9094                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9095         }
9096     }
9097     el = this.el;
9098     el.setDisplayed(true);
9099     el.hide = this.hideAction;
9100     this.id = el.id;
9101     el.addClass("x-dlg");
9102
9103     Roo.apply(this, config);
9104
9105     this.proxy = el.createProxy("x-dlg-proxy");
9106     this.proxy.hide = this.hideAction;
9107     this.proxy.setOpacity(.5);
9108     this.proxy.hide();
9109
9110     if(config.width){
9111         el.setWidth(config.width);
9112     }
9113     if(config.height){
9114         el.setHeight(config.height);
9115     }
9116     this.size = el.getSize();
9117     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9118         this.xy = [config.x,config.y];
9119     }else{
9120         this.xy = el.getCenterXY(true);
9121     }
9122     /** The header element @type Roo.Element */
9123     this.header = el.child("> .x-dlg-hd");
9124     /** The body element @type Roo.Element */
9125     this.body = el.child("> .x-dlg-bd");
9126     /** The footer element @type Roo.Element */
9127     this.footer = el.child("> .x-dlg-ft");
9128
9129     if(!this.header){
9130         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9131     }
9132     if(!this.body){
9133         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9134     }
9135
9136     this.header.unselectable();
9137     if(this.title){
9138         this.header.update(this.title);
9139     }
9140     // this element allows the dialog to be focused for keyboard event
9141     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9142     this.focusEl.swallowEvent("click", true);
9143
9144     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9145
9146     // wrap the body and footer for special rendering
9147     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9148     if(this.footer){
9149         this.bwrap.dom.appendChild(this.footer.dom);
9150     }
9151
9152     this.bg = this.el.createChild({
9153         tag: "div", cls:"x-dlg-bg",
9154         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9155     });
9156     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9157
9158
9159     if(this.autoScroll !== false && !this.autoTabs){
9160         this.body.setStyle("overflow", "auto");
9161     }
9162
9163     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9164
9165     if(this.closable !== false){
9166         this.el.addClass("x-dlg-closable");
9167         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9168         this.close.on("click", this.closeClick, this);
9169         this.close.addClassOnOver("x-dlg-close-over");
9170     }
9171     if(this.collapsible !== false){
9172         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9173         this.collapseBtn.on("click", this.collapseClick, this);
9174         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9175         this.header.on("dblclick", this.collapseClick, this);
9176     }
9177     if(this.resizable !== false){
9178         this.el.addClass("x-dlg-resizable");
9179         this.resizer = new Roo.Resizable(el, {
9180             minWidth: this.minWidth || 80,
9181             minHeight:this.minHeight || 80,
9182             handles: this.resizeHandles || "all",
9183             pinned: true
9184         });
9185         this.resizer.on("beforeresize", this.beforeResize, this);
9186         this.resizer.on("resize", this.onResize, this);
9187     }
9188     if(this.draggable !== false){
9189         el.addClass("x-dlg-draggable");
9190         if (!this.proxyDrag) {
9191             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9192         }
9193         else {
9194             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9195         }
9196         dd.setHandleElId(this.header.id);
9197         dd.endDrag = this.endMove.createDelegate(this);
9198         dd.startDrag = this.startMove.createDelegate(this);
9199         dd.onDrag = this.onDrag.createDelegate(this);
9200         dd.scroll = false;
9201         this.dd = dd;
9202     }
9203     if(this.modal){
9204         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9205         this.mask.enableDisplayMode("block");
9206         this.mask.hide();
9207         this.el.addClass("x-dlg-modal");
9208     }
9209     if(this.shadow){
9210         this.shadow = new Roo.Shadow({
9211             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9212             offset : this.shadowOffset
9213         });
9214     }else{
9215         this.shadowOffset = 0;
9216     }
9217     if(Roo.useShims && this.shim !== false){
9218         this.shim = this.el.createShim();
9219         this.shim.hide = this.hideAction;
9220         this.shim.hide();
9221     }else{
9222         this.shim = false;
9223     }
9224     if(this.autoTabs){
9225         this.initTabs();
9226     }
9227     if (this.buttons) { 
9228         var bts= this.buttons;
9229         this.buttons = [];
9230         Roo.each(bts, function(b) {
9231             this.addButton(b);
9232         }, this);
9233     }
9234     
9235     
9236     this.addEvents({
9237         /**
9238          * @event keydown
9239          * Fires when a key is pressed
9240          * @param {Roo.BasicDialog} this
9241          * @param {Roo.EventObject} e
9242          */
9243         "keydown" : true,
9244         /**
9245          * @event move
9246          * Fires when this dialog is moved by the user.
9247          * @param {Roo.BasicDialog} this
9248          * @param {Number} x The new page X
9249          * @param {Number} y The new page Y
9250          */
9251         "move" : true,
9252         /**
9253          * @event resize
9254          * Fires when this dialog is resized by the user.
9255          * @param {Roo.BasicDialog} this
9256          * @param {Number} width The new width
9257          * @param {Number} height The new height
9258          */
9259         "resize" : true,
9260         /**
9261          * @event beforehide
9262          * Fires before this dialog is hidden.
9263          * @param {Roo.BasicDialog} this
9264          */
9265         "beforehide" : true,
9266         /**
9267          * @event hide
9268          * Fires when this dialog is hidden.
9269          * @param {Roo.BasicDialog} this
9270          */
9271         "hide" : true,
9272         /**
9273          * @event beforeshow
9274          * Fires before this dialog is shown.
9275          * @param {Roo.BasicDialog} this
9276          */
9277         "beforeshow" : true,
9278         /**
9279          * @event show
9280          * Fires when this dialog is shown.
9281          * @param {Roo.BasicDialog} this
9282          */
9283         "show" : true
9284     });
9285     el.on("keydown", this.onKeyDown, this);
9286     el.on("mousedown", this.toFront, this);
9287     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9288     this.el.hide();
9289     Roo.DialogManager.register(this);
9290     Roo.BasicDialog.superclass.constructor.call(this);
9291 };
9292
9293 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9294     shadowOffset: Roo.isIE ? 6 : 5,
9295     minHeight: 80,
9296     minWidth: 200,
9297     minButtonWidth: 75,
9298     defaultButton: null,
9299     buttonAlign: "right",
9300     tabTag: 'div',
9301     firstShow: true,
9302
9303     /**
9304      * Sets the dialog title text
9305      * @param {String} text The title text to display
9306      * @return {Roo.BasicDialog} this
9307      */
9308     setTitle : function(text){
9309         this.header.update(text);
9310         return this;
9311     },
9312
9313     // private
9314     closeClick : function(){
9315         this.hide();
9316     },
9317
9318     // private
9319     collapseClick : function(){
9320         this[this.collapsed ? "expand" : "collapse"]();
9321     },
9322
9323     /**
9324      * Collapses the dialog to its minimized state (only the title bar is visible).
9325      * Equivalent to the user clicking the collapse dialog button.
9326      */
9327     collapse : function(){
9328         if(!this.collapsed){
9329             this.collapsed = true;
9330             this.el.addClass("x-dlg-collapsed");
9331             this.restoreHeight = this.el.getHeight();
9332             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9333         }
9334     },
9335
9336     /**
9337      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9338      * clicking the expand dialog button.
9339      */
9340     expand : function(){
9341         if(this.collapsed){
9342             this.collapsed = false;
9343             this.el.removeClass("x-dlg-collapsed");
9344             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9345         }
9346     },
9347
9348     /**
9349      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9350      * @return {Roo.TabPanel} The tabs component
9351      */
9352     initTabs : function(){
9353         var tabs = this.getTabs();
9354         while(tabs.getTab(0)){
9355             tabs.removeTab(0);
9356         }
9357         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9358             var dom = el.dom;
9359             tabs.addTab(Roo.id(dom), dom.title);
9360             dom.title = "";
9361         });
9362         tabs.activate(0);
9363         return tabs;
9364     },
9365
9366     // private
9367     beforeResize : function(){
9368         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9369     },
9370
9371     // private
9372     onResize : function(){
9373         this.refreshSize();
9374         this.syncBodyHeight();
9375         this.adjustAssets();
9376         this.focus();
9377         this.fireEvent("resize", this, this.size.width, this.size.height);
9378     },
9379
9380     // private
9381     onKeyDown : function(e){
9382         if(this.isVisible()){
9383             this.fireEvent("keydown", this, e);
9384         }
9385     },
9386
9387     /**
9388      * Resizes the dialog.
9389      * @param {Number} width
9390      * @param {Number} height
9391      * @return {Roo.BasicDialog} this
9392      */
9393     resizeTo : function(width, height){
9394         this.el.setSize(width, height);
9395         this.size = {width: width, height: height};
9396         this.syncBodyHeight();
9397         if(this.fixedcenter){
9398             this.center();
9399         }
9400         if(this.isVisible()){
9401             this.constrainXY();
9402             this.adjustAssets();
9403         }
9404         this.fireEvent("resize", this, width, height);
9405         return this;
9406     },
9407
9408
9409     /**
9410      * Resizes the dialog to fit the specified content size.
9411      * @param {Number} width
9412      * @param {Number} height
9413      * @return {Roo.BasicDialog} this
9414      */
9415     setContentSize : function(w, h){
9416         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9417         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9418         //if(!this.el.isBorderBox()){
9419             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9420             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9421         //}
9422         if(this.tabs){
9423             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9424             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9425         }
9426         this.resizeTo(w, h);
9427         return this;
9428     },
9429
9430     /**
9431      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9432      * executed in response to a particular key being pressed while the dialog is active.
9433      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9434      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9435      * @param {Function} fn The function to call
9436      * @param {Object} scope (optional) The scope of the function
9437      * @return {Roo.BasicDialog} this
9438      */
9439     addKeyListener : function(key, fn, scope){
9440         var keyCode, shift, ctrl, alt;
9441         if(typeof key == "object" && !(key instanceof Array)){
9442             keyCode = key["key"];
9443             shift = key["shift"];
9444             ctrl = key["ctrl"];
9445             alt = key["alt"];
9446         }else{
9447             keyCode = key;
9448         }
9449         var handler = function(dlg, e){
9450             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9451                 var k = e.getKey();
9452                 if(keyCode instanceof Array){
9453                     for(var i = 0, len = keyCode.length; i < len; i++){
9454                         if(keyCode[i] == k){
9455                           fn.call(scope || window, dlg, k, e);
9456                           return;
9457                         }
9458                     }
9459                 }else{
9460                     if(k == keyCode){
9461                         fn.call(scope || window, dlg, k, e);
9462                     }
9463                 }
9464             }
9465         };
9466         this.on("keydown", handler);
9467         return this;
9468     },
9469
9470     /**
9471      * Returns the TabPanel component (creates it if it doesn't exist).
9472      * Note: If you wish to simply check for the existence of tabs without creating them,
9473      * check for a null 'tabs' property.
9474      * @return {Roo.TabPanel} The tabs component
9475      */
9476     getTabs : function(){
9477         if(!this.tabs){
9478             this.el.addClass("x-dlg-auto-tabs");
9479             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9480             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9481         }
9482         return this.tabs;
9483     },
9484
9485     /**
9486      * Adds a button to the footer section of the dialog.
9487      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9488      * object or a valid Roo.DomHelper element config
9489      * @param {Function} handler The function called when the button is clicked
9490      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9491      * @return {Roo.Button} The new button
9492      */
9493     addButton : function(config, handler, scope){
9494         var dh = Roo.DomHelper;
9495         if(!this.footer){
9496             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9497         }
9498         if(!this.btnContainer){
9499             var tb = this.footer.createChild({
9500
9501                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9502                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9503             }, null, true);
9504             this.btnContainer = tb.firstChild.firstChild.firstChild;
9505         }
9506         var bconfig = {
9507             handler: handler,
9508             scope: scope,
9509             minWidth: this.minButtonWidth,
9510             hideParent:true
9511         };
9512         if(typeof config == "string"){
9513             bconfig.text = config;
9514         }else{
9515             if(config.tag){
9516                 bconfig.dhconfig = config;
9517             }else{
9518                 Roo.apply(bconfig, config);
9519             }
9520         }
9521         var fc = false;
9522         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9523             bconfig.position = Math.max(0, bconfig.position);
9524             fc = this.btnContainer.childNodes[bconfig.position];
9525         }
9526          
9527         var btn = new Roo.Button(
9528             fc ? 
9529                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9530                 : this.btnContainer.appendChild(document.createElement("td")),
9531             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9532             bconfig
9533         );
9534         this.syncBodyHeight();
9535         if(!this.buttons){
9536             /**
9537              * Array of all the buttons that have been added to this dialog via addButton
9538              * @type Array
9539              */
9540             this.buttons = [];
9541         }
9542         this.buttons.push(btn);
9543         return btn;
9544     },
9545
9546     /**
9547      * Sets the default button to be focused when the dialog is displayed.
9548      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9549      * @return {Roo.BasicDialog} this
9550      */
9551     setDefaultButton : function(btn){
9552         this.defaultButton = btn;
9553         return this;
9554     },
9555
9556     // private
9557     getHeaderFooterHeight : function(safe){
9558         var height = 0;
9559         if(this.header){
9560            height += this.header.getHeight();
9561         }
9562         if(this.footer){
9563            var fm = this.footer.getMargins();
9564             height += (this.footer.getHeight()+fm.top+fm.bottom);
9565         }
9566         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9567         height += this.centerBg.getPadding("tb");
9568         return height;
9569     },
9570
9571     // private
9572     syncBodyHeight : function()
9573     {
9574         var bd = this.body, // the text
9575             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9576             bw = this.bwrap;
9577         var height = this.size.height - this.getHeaderFooterHeight(false);
9578         bd.setHeight(height-bd.getMargins("tb"));
9579         var hh = this.header.getHeight();
9580         var h = this.size.height-hh;
9581         cb.setHeight(h);
9582         
9583         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9584         bw.setHeight(h-cb.getPadding("tb"));
9585         
9586         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9587         bd.setWidth(bw.getWidth(true));
9588         if(this.tabs){
9589             this.tabs.syncHeight();
9590             if(Roo.isIE){
9591                 this.tabs.el.repaint();
9592             }
9593         }
9594     },
9595
9596     /**
9597      * Restores the previous state of the dialog if Roo.state is configured.
9598      * @return {Roo.BasicDialog} this
9599      */
9600     restoreState : function(){
9601         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9602         if(box && box.width){
9603             this.xy = [box.x, box.y];
9604             this.resizeTo(box.width, box.height);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeShow : function(){
9611         this.expand();
9612         if(this.fixedcenter){
9613             this.xy = this.el.getCenterXY(true);
9614         }
9615         if(this.modal){
9616             Roo.get(document.body).addClass("x-body-masked");
9617             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9618             this.mask.show();
9619         }
9620         this.constrainXY();
9621     },
9622
9623     // private
9624     animShow : function(){
9625         var b = Roo.get(this.animateTarget).getBox();
9626         this.proxy.setSize(b.width, b.height);
9627         this.proxy.setLocation(b.x, b.y);
9628         this.proxy.show();
9629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9630                     true, .35, this.showEl.createDelegate(this));
9631     },
9632
9633     /**
9634      * Shows the dialog.
9635      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9636      * @return {Roo.BasicDialog} this
9637      */
9638     show : function(animateTarget){
9639         if (this.fireEvent("beforeshow", this) === false){
9640             return;
9641         }
9642         if(this.syncHeightBeforeShow){
9643             this.syncBodyHeight();
9644         }else if(this.firstShow){
9645             this.firstShow = false;
9646             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9647         }
9648         this.animateTarget = animateTarget || this.animateTarget;
9649         if(!this.el.isVisible()){
9650             this.beforeShow();
9651             if(this.animateTarget && Roo.get(this.animateTarget)){
9652                 this.animShow();
9653             }else{
9654                 this.showEl();
9655             }
9656         }
9657         return this;
9658     },
9659
9660     // private
9661     showEl : function(){
9662         this.proxy.hide();
9663         this.el.setXY(this.xy);
9664         this.el.show();
9665         this.adjustAssets(true);
9666         this.toFront();
9667         this.focus();
9668         // IE peekaboo bug - fix found by Dave Fenwick
9669         if(Roo.isIE){
9670             this.el.repaint();
9671         }
9672         this.fireEvent("show", this);
9673     },
9674
9675     /**
9676      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9677      * dialog itself will receive focus.
9678      */
9679     focus : function(){
9680         if(this.defaultButton){
9681             this.defaultButton.focus();
9682         }else{
9683             this.focusEl.focus();
9684         }
9685     },
9686
9687     // private
9688     constrainXY : function(){
9689         if(this.constraintoviewport !== false){
9690             if(!this.viewSize){
9691                 if(this.container){
9692                     var s = this.container.getSize();
9693                     this.viewSize = [s.width, s.height];
9694                 }else{
9695                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9696                 }
9697             }
9698             var s = Roo.get(this.container||document).getScroll();
9699
9700             var x = this.xy[0], y = this.xy[1];
9701             var w = this.size.width, h = this.size.height;
9702             var vw = this.viewSize[0], vh = this.viewSize[1];
9703             // only move it if it needs it
9704             var moved = false;
9705             // first validate right/bottom
9706             if(x + w > vw+s.left){
9707                 x = vw - w;
9708                 moved = true;
9709             }
9710             if(y + h > vh+s.top){
9711                 y = vh - h;
9712                 moved = true;
9713             }
9714             // then make sure top/left isn't negative
9715             if(x < s.left){
9716                 x = s.left;
9717                 moved = true;
9718             }
9719             if(y < s.top){
9720                 y = s.top;
9721                 moved = true;
9722             }
9723             if(moved){
9724                 // cache xy
9725                 this.xy = [x, y];
9726                 if(this.isVisible()){
9727                     this.el.setLocation(x, y);
9728                     this.adjustAssets();
9729                 }
9730             }
9731         }
9732     },
9733
9734     // private
9735     onDrag : function(){
9736         if(!this.proxyDrag){
9737             this.xy = this.el.getXY();
9738             this.adjustAssets();
9739         }
9740     },
9741
9742     // private
9743     adjustAssets : function(doShow){
9744         var x = this.xy[0], y = this.xy[1];
9745         var w = this.size.width, h = this.size.height;
9746         if(doShow === true){
9747             if(this.shadow){
9748                 this.shadow.show(this.el);
9749             }
9750             if(this.shim){
9751                 this.shim.show();
9752             }
9753         }
9754         if(this.shadow && this.shadow.isVisible()){
9755             this.shadow.show(this.el);
9756         }
9757         if(this.shim && this.shim.isVisible()){
9758             this.shim.setBounds(x, y, w, h);
9759         }
9760     },
9761
9762     // private
9763     adjustViewport : function(w, h){
9764         if(!w || !h){
9765             w = Roo.lib.Dom.getViewWidth();
9766             h = Roo.lib.Dom.getViewHeight();
9767         }
9768         // cache the size
9769         this.viewSize = [w, h];
9770         if(this.modal && this.mask.isVisible()){
9771             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9772             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9773         }
9774         if(this.isVisible()){
9775             this.constrainXY();
9776         }
9777     },
9778
9779     /**
9780      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9781      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9782      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9783      */
9784     destroy : function(removeEl){
9785         if(this.isVisible()){
9786             this.animateTarget = null;
9787             this.hide();
9788         }
9789         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9790         if(this.tabs){
9791             this.tabs.destroy(removeEl);
9792         }
9793         Roo.destroy(
9794              this.shim,
9795              this.proxy,
9796              this.resizer,
9797              this.close,
9798              this.mask
9799         );
9800         if(this.dd){
9801             this.dd.unreg();
9802         }
9803         if(this.buttons){
9804            for(var i = 0, len = this.buttons.length; i < len; i++){
9805                this.buttons[i].destroy();
9806            }
9807         }
9808         this.el.removeAllListeners();
9809         if(removeEl === true){
9810             this.el.update("");
9811             this.el.remove();
9812         }
9813         Roo.DialogManager.unregister(this);
9814     },
9815
9816     // private
9817     startMove : function(){
9818         if(this.proxyDrag){
9819             this.proxy.show();
9820         }
9821         if(this.constraintoviewport !== false){
9822             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9823         }
9824     },
9825
9826     // private
9827     endMove : function(){
9828         if(!this.proxyDrag){
9829             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9830         }else{
9831             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9832             this.proxy.hide();
9833         }
9834         this.refreshSize();
9835         this.adjustAssets();
9836         this.focus();
9837         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9838     },
9839
9840     /**
9841      * Brings this dialog to the front of any other visible dialogs
9842      * @return {Roo.BasicDialog} this
9843      */
9844     toFront : function(){
9845         Roo.DialogManager.bringToFront(this);
9846         return this;
9847     },
9848
9849     /**
9850      * Sends this dialog to the back (under) of any other visible dialogs
9851      * @return {Roo.BasicDialog} this
9852      */
9853     toBack : function(){
9854         Roo.DialogManager.sendToBack(this);
9855         return this;
9856     },
9857
9858     /**
9859      * Centers this dialog in the viewport
9860      * @return {Roo.BasicDialog} this
9861      */
9862     center : function(){
9863         var xy = this.el.getCenterXY(true);
9864         this.moveTo(xy[0], xy[1]);
9865         return this;
9866     },
9867
9868     /**
9869      * Moves the dialog's top-left corner to the specified point
9870      * @param {Number} x
9871      * @param {Number} y
9872      * @return {Roo.BasicDialog} this
9873      */
9874     moveTo : function(x, y){
9875         this.xy = [x,y];
9876         if(this.isVisible()){
9877             this.el.setXY(this.xy);
9878             this.adjustAssets();
9879         }
9880         return this;
9881     },
9882
9883     /**
9884      * Aligns the dialog to the specified element
9885      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9886      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9887      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9888      * @return {Roo.BasicDialog} this
9889      */
9890     alignTo : function(element, position, offsets){
9891         this.xy = this.el.getAlignToXY(element, position, offsets);
9892         if(this.isVisible()){
9893             this.el.setXY(this.xy);
9894             this.adjustAssets();
9895         }
9896         return this;
9897     },
9898
9899     /**
9900      * Anchors an element to another element and realigns it when the window is resized.
9901      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9902      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9903      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9904      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9905      * is a number, it is used as the buffer delay (defaults to 50ms).
9906      * @return {Roo.BasicDialog} this
9907      */
9908     anchorTo : function(el, alignment, offsets, monitorScroll){
9909         var action = function(){
9910             this.alignTo(el, alignment, offsets);
9911         };
9912         Roo.EventManager.onWindowResize(action, this);
9913         var tm = typeof monitorScroll;
9914         if(tm != 'undefined'){
9915             Roo.EventManager.on(window, 'scroll', action, this,
9916                 {buffer: tm == 'number' ? monitorScroll : 50});
9917         }
9918         action.call(this);
9919         return this;
9920     },
9921
9922     /**
9923      * Returns true if the dialog is visible
9924      * @return {Boolean}
9925      */
9926     isVisible : function(){
9927         return this.el.isVisible();
9928     },
9929
9930     // private
9931     animHide : function(callback){
9932         var b = Roo.get(this.animateTarget).getBox();
9933         this.proxy.show();
9934         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9935         this.el.hide();
9936         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9937                     this.hideEl.createDelegate(this, [callback]));
9938     },
9939
9940     /**
9941      * Hides the dialog.
9942      * @param {Function} callback (optional) Function to call when the dialog is hidden
9943      * @return {Roo.BasicDialog} this
9944      */
9945     hide : function(callback){
9946         if (this.fireEvent("beforehide", this) === false){
9947             return;
9948         }
9949         if(this.shadow){
9950             this.shadow.hide();
9951         }
9952         if(this.shim) {
9953           this.shim.hide();
9954         }
9955         // sometimes animateTarget seems to get set.. causing problems...
9956         // this just double checks..
9957         if(this.animateTarget && Roo.get(this.animateTarget)) {
9958            this.animHide(callback);
9959         }else{
9960             this.el.hide();
9961             this.hideEl(callback);
9962         }
9963         return this;
9964     },
9965
9966     // private
9967     hideEl : function(callback){
9968         this.proxy.hide();
9969         if(this.modal){
9970             this.mask.hide();
9971             Roo.get(document.body).removeClass("x-body-masked");
9972         }
9973         this.fireEvent("hide", this);
9974         if(typeof callback == "function"){
9975             callback();
9976         }
9977     },
9978
9979     // private
9980     hideAction : function(){
9981         this.setLeft("-10000px");
9982         this.setTop("-10000px");
9983         this.setStyle("visibility", "hidden");
9984     },
9985
9986     // private
9987     refreshSize : function(){
9988         this.size = this.el.getSize();
9989         this.xy = this.el.getXY();
9990         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9991     },
9992
9993     // private
9994     // z-index is managed by the DialogManager and may be overwritten at any time
9995     setZIndex : function(index){
9996         if(this.modal){
9997             this.mask.setStyle("z-index", index);
9998         }
9999         if(this.shim){
10000             this.shim.setStyle("z-index", ++index);
10001         }
10002         if(this.shadow){
10003             this.shadow.setZIndex(++index);
10004         }
10005         this.el.setStyle("z-index", ++index);
10006         if(this.proxy){
10007             this.proxy.setStyle("z-index", ++index);
10008         }
10009         if(this.resizer){
10010             this.resizer.proxy.setStyle("z-index", ++index);
10011         }
10012
10013         this.lastZIndex = index;
10014     },
10015
10016     /**
10017      * Returns the element for this dialog
10018      * @return {Roo.Element} The underlying dialog Element
10019      */
10020     getEl : function(){
10021         return this.el;
10022     }
10023 });
10024
10025 /**
10026  * @class Roo.DialogManager
10027  * Provides global access to BasicDialogs that have been created and
10028  * support for z-indexing (layering) multiple open dialogs.
10029  */
10030 Roo.DialogManager = function(){
10031     var list = {};
10032     var accessList = [];
10033     var front = null;
10034
10035     // private
10036     var sortDialogs = function(d1, d2){
10037         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10038     };
10039
10040     // private
10041     var orderDialogs = function(){
10042         accessList.sort(sortDialogs);
10043         var seed = Roo.DialogManager.zseed;
10044         for(var i = 0, len = accessList.length; i < len; i++){
10045             var dlg = accessList[i];
10046             if(dlg){
10047                 dlg.setZIndex(seed + (i*10));
10048             }
10049         }
10050     };
10051
10052     return {
10053         /**
10054          * The starting z-index for BasicDialogs (defaults to 9000)
10055          * @type Number The z-index value
10056          */
10057         zseed : 9000,
10058
10059         // private
10060         register : function(dlg){
10061             list[dlg.id] = dlg;
10062             accessList.push(dlg);
10063         },
10064
10065         // private
10066         unregister : function(dlg){
10067             delete list[dlg.id];
10068             var i=0;
10069             var len=0;
10070             if(!accessList.indexOf){
10071                 for(  i = 0, len = accessList.length; i < len; i++){
10072                     if(accessList[i] == dlg){
10073                         accessList.splice(i, 1);
10074                         return;
10075                     }
10076                 }
10077             }else{
10078                  i = accessList.indexOf(dlg);
10079                 if(i != -1){
10080                     accessList.splice(i, 1);
10081                 }
10082             }
10083         },
10084
10085         /**
10086          * Gets a registered dialog by id
10087          * @param {String/Object} id The id of the dialog or a dialog
10088          * @return {Roo.BasicDialog} this
10089          */
10090         get : function(id){
10091             return typeof id == "object" ? id : list[id];
10092         },
10093
10094         /**
10095          * Brings the specified dialog to the front
10096          * @param {String/Object} dlg The id of the dialog or a dialog
10097          * @return {Roo.BasicDialog} this
10098          */
10099         bringToFront : function(dlg){
10100             dlg = this.get(dlg);
10101             if(dlg != front){
10102                 front = dlg;
10103                 dlg._lastAccess = new Date().getTime();
10104                 orderDialogs();
10105             }
10106             return dlg;
10107         },
10108
10109         /**
10110          * Sends the specified dialog to the back
10111          * @param {String/Object} dlg The id of the dialog or a dialog
10112          * @return {Roo.BasicDialog} this
10113          */
10114         sendToBack : function(dlg){
10115             dlg = this.get(dlg);
10116             dlg._lastAccess = -(new Date().getTime());
10117             orderDialogs();
10118             return dlg;
10119         },
10120
10121         /**
10122          * Hides all dialogs
10123          */
10124         hideAll : function(){
10125             for(var id in list){
10126                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10127                     list[id].hide();
10128                 }
10129             }
10130         }
10131     };
10132 }();
10133
10134 /**
10135  * @class Roo.LayoutDialog
10136  * @extends Roo.BasicDialog
10137  * @children Roo.ContentPanel
10138  * @parent builder none
10139  * Dialog which provides adjustments for working with a layout in a Dialog.
10140  * Add your necessary layout config options to the dialog's config.<br>
10141  * Example usage (including a nested layout):
10142  * <pre><code>
10143 if(!dialog){
10144     dialog = new Roo.LayoutDialog("download-dlg", {
10145         modal: true,
10146         width:600,
10147         height:450,
10148         shadow:true,
10149         minWidth:500,
10150         minHeight:350,
10151         autoTabs:true,
10152         proxyDrag:true,
10153         // layout config merges with the dialog config
10154         center:{
10155             tabPosition: "top",
10156             alwaysShowTabs: true
10157         }
10158     });
10159     dialog.addKeyListener(27, dialog.hide, dialog);
10160     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10161     dialog.addButton("Build It!", this.getDownload, this);
10162
10163     // we can even add nested layouts
10164     var innerLayout = new Roo.BorderLayout("dl-inner", {
10165         east: {
10166             initialSize: 200,
10167             autoScroll:true,
10168             split:true
10169         },
10170         center: {
10171             autoScroll:true
10172         }
10173     });
10174     innerLayout.beginUpdate();
10175     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10176     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10177     innerLayout.endUpdate(true);
10178
10179     var layout = dialog.getLayout();
10180     layout.beginUpdate();
10181     layout.add("center", new Roo.ContentPanel("standard-panel",
10182                         {title: "Download the Source", fitToFrame:true}));
10183     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10184                {title: "Build your own roo.js"}));
10185     layout.getRegion("center").showPanel(sp);
10186     layout.endUpdate();
10187 }
10188 </code></pre>
10189     * @constructor
10190     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10191     * @param {Object} config configuration options
10192   */
10193 Roo.LayoutDialog = function(el, cfg){
10194     
10195     var config=  cfg;
10196     if (typeof(cfg) == 'undefined') {
10197         config = Roo.apply({}, el);
10198         // not sure why we use documentElement here.. - it should always be body.
10199         // IE7 borks horribly if we use documentElement.
10200         // webkit also does not like documentElement - it creates a body element...
10201         el = Roo.get( document.body || document.documentElement ).createChild();
10202         //config.autoCreate = true;
10203     }
10204     
10205     
10206     config.autoTabs = false;
10207     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10208     this.body.setStyle({overflow:"hidden", position:"relative"});
10209     this.layout = new Roo.BorderLayout(this.body.dom, config);
10210     this.layout.monitorWindowResize = false;
10211     this.el.addClass("x-dlg-auto-layout");
10212     // fix case when center region overwrites center function
10213     this.center = Roo.BasicDialog.prototype.center;
10214     this.on("show", this.layout.layout, this.layout, true);
10215     if (config.items) {
10216         var xitems = config.items;
10217         delete config.items;
10218         Roo.each(xitems, this.addxtype, this);
10219     }
10220     
10221     
10222 };
10223 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10224     
10225     
10226     /**
10227      * @cfg {Roo.LayoutRegion} east  
10228      */
10229     /**
10230      * @cfg {Roo.LayoutRegion} west
10231      */
10232     /**
10233      * @cfg {Roo.LayoutRegion} south
10234      */
10235     /**
10236      * @cfg {Roo.LayoutRegion} north
10237      */
10238     /**
10239      * @cfg {Roo.LayoutRegion} center
10240      */
10241     /**
10242      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10243      */
10244     
10245     
10246     /**
10247      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10248      * @deprecated
10249      */
10250     endUpdate : function(){
10251         this.layout.endUpdate();
10252     },
10253
10254     /**
10255      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10256      *  @deprecated
10257      */
10258     beginUpdate : function(){
10259         this.layout.beginUpdate();
10260     },
10261
10262     /**
10263      * Get the BorderLayout for this dialog
10264      * @return {Roo.BorderLayout}
10265      */
10266     getLayout : function(){
10267         return this.layout;
10268     },
10269
10270     showEl : function(){
10271         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10272         if(Roo.isIE7){
10273             this.layout.layout();
10274         }
10275     },
10276
10277     // private
10278     // Use the syncHeightBeforeShow config option to control this automatically
10279     syncBodyHeight : function(){
10280         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10281         if(this.layout){this.layout.layout();}
10282     },
10283     
10284       /**
10285      * Add an xtype element (actually adds to the layout.)
10286      * @return {Object} xdata xtype object data.
10287      */
10288     
10289     addxtype : function(c) {
10290         return this.layout.addxtype(c);
10291     }
10292 });/*
10293  * Based on:
10294  * Ext JS Library 1.1.1
10295  * Copyright(c) 2006-2007, Ext JS, LLC.
10296  *
10297  * Originally Released Under LGPL - original licence link has changed is not relivant.
10298  *
10299  * Fork - LGPL
10300  * <script type="text/javascript">
10301  */
10302  
10303 /**
10304  * @class Roo.MessageBox
10305  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10306  * Example usage:
10307  *<pre><code>
10308 // Basic alert:
10309 Roo.Msg.alert('Status', 'Changes saved successfully.');
10310
10311 // Prompt for user data:
10312 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10313     if (btn == 'ok'){
10314         // process text value...
10315     }
10316 });
10317
10318 // Show a dialog using config options:
10319 Roo.Msg.show({
10320    title:'Save Changes?',
10321    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10322    buttons: Roo.Msg.YESNOCANCEL,
10323    fn: processResult,
10324    animEl: 'elId'
10325 });
10326 </code></pre>
10327  * @static
10328  */
10329 Roo.MessageBox = function(){
10330     var dlg, opt, mask, waitTimer;
10331     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10332     var buttons, activeTextEl, bwidth;
10333
10334     // private
10335     var handleButton = function(button){
10336         dlg.hide();
10337         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10338     };
10339
10340     // private
10341     var handleHide = function(){
10342         if(opt && opt.cls){
10343             dlg.el.removeClass(opt.cls);
10344         }
10345         if(waitTimer){
10346             Roo.TaskMgr.stop(waitTimer);
10347             waitTimer = null;
10348         }
10349     };
10350
10351     // private
10352     var updateButtons = function(b){
10353         var width = 0;
10354         if(!b){
10355             buttons["ok"].hide();
10356             buttons["cancel"].hide();
10357             buttons["yes"].hide();
10358             buttons["no"].hide();
10359             dlg.footer.dom.style.display = 'none';
10360             return width;
10361         }
10362         dlg.footer.dom.style.display = '';
10363         for(var k in buttons){
10364             if(typeof buttons[k] != "function"){
10365                 if(b[k]){
10366                     buttons[k].show();
10367                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10368                     width += buttons[k].el.getWidth()+15;
10369                 }else{
10370                     buttons[k].hide();
10371                 }
10372             }
10373         }
10374         return width;
10375     };
10376
10377     // private
10378     var handleEsc = function(d, k, e){
10379         if(opt && opt.closable !== false){
10380             dlg.hide();
10381         }
10382         if(e){
10383             e.stopEvent();
10384         }
10385     };
10386
10387     return {
10388         /**
10389          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10390          * @return {Roo.BasicDialog} The BasicDialog element
10391          */
10392         getDialog : function(){
10393            if(!dlg){
10394                 dlg = new Roo.BasicDialog("x-msg-box", {
10395                     autoCreate : true,
10396                     shadow: true,
10397                     draggable: true,
10398                     resizable:false,
10399                     constraintoviewport:false,
10400                     fixedcenter:true,
10401                     collapsible : false,
10402                     shim:true,
10403                     modal: true,
10404                     width:400, height:100,
10405                     buttonAlign:"center",
10406                     closeClick : function(){
10407                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10408                             handleButton("no");
10409                         }else{
10410                             handleButton("cancel");
10411                         }
10412                     }
10413                 });
10414                 dlg.on("hide", handleHide);
10415                 mask = dlg.mask;
10416                 dlg.addKeyListener(27, handleEsc);
10417                 buttons = {};
10418                 var bt = this.buttonText;
10419                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10420                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10421                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10422                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10423                 bodyEl = dlg.body.createChild({
10424
10425                     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>'
10426                 });
10427                 msgEl = bodyEl.dom.firstChild;
10428                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10429                 textboxEl.enableDisplayMode();
10430                 textboxEl.addKeyListener([10,13], function(){
10431                     if(dlg.isVisible() && opt && opt.buttons){
10432                         if(opt.buttons.ok){
10433                             handleButton("ok");
10434                         }else if(opt.buttons.yes){
10435                             handleButton("yes");
10436                         }
10437                     }
10438                 });
10439                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10440                 textareaEl.enableDisplayMode();
10441                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10442                 progressEl.enableDisplayMode();
10443                 var pf = progressEl.dom.firstChild;
10444                 if (pf) {
10445                     pp = Roo.get(pf.firstChild);
10446                     pp.setHeight(pf.offsetHeight);
10447                 }
10448                 
10449             }
10450             return dlg;
10451         },
10452
10453         /**
10454          * Updates the message box body text
10455          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10456          * the XHTML-compliant non-breaking space character '&amp;#160;')
10457          * @return {Roo.MessageBox} This message box
10458          */
10459         updateText : function(text){
10460             if(!dlg.isVisible() && !opt.width){
10461                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10462             }
10463             msgEl.innerHTML = text || '&#160;';
10464       
10465             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10466             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10467             var w = Math.max(
10468                     Math.min(opt.width || cw , this.maxWidth), 
10469                     Math.max(opt.minWidth || this.minWidth, bwidth)
10470             );
10471             if(opt.prompt){
10472                 activeTextEl.setWidth(w);
10473             }
10474             if(dlg.isVisible()){
10475                 dlg.fixedcenter = false;
10476             }
10477             // to big, make it scroll. = But as usual stupid IE does not support
10478             // !important..
10479             
10480             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10481                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10482                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10483             } else {
10484                 bodyEl.dom.style.height = '';
10485                 bodyEl.dom.style.overflowY = '';
10486             }
10487             if (cw > w) {
10488                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10489             } else {
10490                 bodyEl.dom.style.overflowX = '';
10491             }
10492             
10493             dlg.setContentSize(w, bodyEl.getHeight());
10494             if(dlg.isVisible()){
10495                 dlg.fixedcenter = true;
10496             }
10497             return this;
10498         },
10499
10500         /**
10501          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10502          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10503          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10504          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10505          * @return {Roo.MessageBox} This message box
10506          */
10507         updateProgress : function(value, text){
10508             if(text){
10509                 this.updateText(text);
10510             }
10511             if (pp) { // weird bug on my firefox - for some reason this is not defined
10512                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10513             }
10514             return this;
10515         },        
10516
10517         /**
10518          * Returns true if the message box is currently displayed
10519          * @return {Boolean} True if the message box is visible, else false
10520          */
10521         isVisible : function(){
10522             return dlg && dlg.isVisible();  
10523         },
10524
10525         /**
10526          * Hides the message box if it is displayed
10527          */
10528         hide : function(){
10529             if(this.isVisible()){
10530                 dlg.hide();
10531             }  
10532         },
10533
10534         /**
10535          * Displays a new message box, or reinitializes an existing message box, based on the config options
10536          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10537          * The following config object properties are supported:
10538          * <pre>
10539 Property    Type             Description
10540 ----------  ---------------  ------------------------------------------------------------------------------------
10541 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10542                                    closes (defaults to undefined)
10543 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10544                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10545 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10546                                    progress and wait dialogs will ignore this property and always hide the
10547                                    close button as they can only be closed programmatically.
10548 cls               String           A custom CSS class to apply to the message box element
10549 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10550                                    displayed (defaults to 75)
10551 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10552                                    function will be btn (the name of the button that was clicked, if applicable,
10553                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10554                                    Progress and wait dialogs will ignore this option since they do not respond to
10555                                    user actions and can only be closed programmatically, so any required function
10556                                    should be called by the same code after it closes the dialog.
10557 icon              String           A CSS class that provides a background image to be used as an icon for
10558                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10559 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10560 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10561 modal             Boolean          False to allow user interaction with the page while the message box is
10562                                    displayed (defaults to true)
10563 msg               String           A string that will replace the existing message box body text (defaults
10564                                    to the XHTML-compliant non-breaking space character '&#160;')
10565 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10566 progress          Boolean          True to display a progress bar (defaults to false)
10567 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10568 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10569 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10570 title             String           The title text
10571 value             String           The string value to set into the active textbox element if displayed
10572 wait              Boolean          True to display a progress bar (defaults to false)
10573 width             Number           The width of the dialog in pixels
10574 </pre>
10575          *
10576          * Example usage:
10577          * <pre><code>
10578 Roo.Msg.show({
10579    title: 'Address',
10580    msg: 'Please enter your address:',
10581    width: 300,
10582    buttons: Roo.MessageBox.OKCANCEL,
10583    multiline: true,
10584    fn: saveAddress,
10585    animEl: 'addAddressBtn'
10586 });
10587 </code></pre>
10588          * @param {Object} config Configuration options
10589          * @return {Roo.MessageBox} This message box
10590          */
10591         show : function(options)
10592         {
10593             
10594             // this causes nightmares if you show one dialog after another
10595             // especially on callbacks..
10596              
10597             if(this.isVisible()){
10598                 
10599                 this.hide();
10600                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10601                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10602                 Roo.log("New Dialog Message:" +  options.msg )
10603                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10604                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10605                 
10606             }
10607             var d = this.getDialog();
10608             opt = options;
10609             d.setTitle(opt.title || "&#160;");
10610             d.close.setDisplayed(opt.closable !== false);
10611             activeTextEl = textboxEl;
10612             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10613             if(opt.prompt){
10614                 if(opt.multiline){
10615                     textboxEl.hide();
10616                     textareaEl.show();
10617                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10618                         opt.multiline : this.defaultTextHeight);
10619                     activeTextEl = textareaEl;
10620                 }else{
10621                     textboxEl.show();
10622                     textareaEl.hide();
10623                 }
10624             }else{
10625                 textboxEl.hide();
10626                 textareaEl.hide();
10627             }
10628             progressEl.setDisplayed(opt.progress === true);
10629             this.updateProgress(0);
10630             activeTextEl.dom.value = opt.value || "";
10631             if(opt.prompt){
10632                 dlg.setDefaultButton(activeTextEl);
10633             }else{
10634                 var bs = opt.buttons;
10635                 var db = null;
10636                 if(bs && bs.ok){
10637                     db = buttons["ok"];
10638                 }else if(bs && bs.yes){
10639                     db = buttons["yes"];
10640                 }
10641                 dlg.setDefaultButton(db);
10642             }
10643             bwidth = updateButtons(opt.buttons);
10644             this.updateText(opt.msg);
10645             if(opt.cls){
10646                 d.el.addClass(opt.cls);
10647             }
10648             d.proxyDrag = opt.proxyDrag === true;
10649             d.modal = opt.modal !== false;
10650             d.mask = opt.modal !== false ? mask : false;
10651             if(!d.isVisible()){
10652                 // force it to the end of the z-index stack so it gets a cursor in FF
10653                 document.body.appendChild(dlg.el.dom);
10654                 d.animateTarget = null;
10655                 d.show(options.animEl);
10656             }
10657             return this;
10658         },
10659
10660         /**
10661          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10662          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10663          * and closing the message box when the process is complete.
10664          * @param {String} title The title bar text
10665          * @param {String} msg The message box body text
10666          * @return {Roo.MessageBox} This message box
10667          */
10668         progress : function(title, msg){
10669             this.show({
10670                 title : title,
10671                 msg : msg,
10672                 buttons: false,
10673                 progress:true,
10674                 closable:false,
10675                 minWidth: this.minProgressWidth,
10676                 modal : true
10677             });
10678             return this;
10679         },
10680
10681         /**
10682          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10683          * If a callback function is passed it will be called after the user clicks the button, and the
10684          * id of the button that was clicked will be passed as the only parameter to the callback
10685          * (could also be the top-right close button).
10686          * @param {String} title The title bar text
10687          * @param {String} msg The message box body text
10688          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10689          * @param {Object} scope (optional) The scope of the callback function
10690          * @return {Roo.MessageBox} This message box
10691          */
10692         alert : function(title, msg, fn, scope){
10693             this.show({
10694                 title : title,
10695                 msg : msg,
10696                 buttons: this.OK,
10697                 fn: fn,
10698                 scope : scope,
10699                 modal : true
10700             });
10701             return this;
10702         },
10703
10704         /**
10705          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10706          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10707          * You are responsible for closing the message box when the process is complete.
10708          * @param {String} msg The message box body text
10709          * @param {String} title (optional) The title bar text
10710          * @return {Roo.MessageBox} This message box
10711          */
10712         wait : function(msg, title){
10713             this.show({
10714                 title : title,
10715                 msg : msg,
10716                 buttons: false,
10717                 closable:false,
10718                 progress:true,
10719                 modal:true,
10720                 width:300,
10721                 wait:true
10722             });
10723             waitTimer = Roo.TaskMgr.start({
10724                 run: function(i){
10725                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10726                 },
10727                 interval: 1000
10728             });
10729             return this;
10730         },
10731
10732         /**
10733          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10734          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10735          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10736          * @param {String} title The title bar text
10737          * @param {String} msg The message box body text
10738          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10739          * @param {Object} scope (optional) The scope of the callback function
10740          * @return {Roo.MessageBox} This message box
10741          */
10742         confirm : function(title, msg, fn, scope){
10743             this.show({
10744                 title : title,
10745                 msg : msg,
10746                 buttons: this.YESNO,
10747                 fn: fn,
10748                 scope : scope,
10749                 modal : true
10750             });
10751             return this;
10752         },
10753
10754         /**
10755          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10756          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10757          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10758          * (could also be the top-right close button) and the text that was entered will be passed as the two
10759          * parameters to the callback.
10760          * @param {String} title The title bar text
10761          * @param {String} msg The message box body text
10762          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10763          * @param {Object} scope (optional) The scope of the callback function
10764          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10765          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10766          * @return {Roo.MessageBox} This message box
10767          */
10768         prompt : function(title, msg, fn, scope, multiline){
10769             this.show({
10770                 title : title,
10771                 msg : msg,
10772                 buttons: this.OKCANCEL,
10773                 fn: fn,
10774                 minWidth:250,
10775                 scope : scope,
10776                 prompt:true,
10777                 multiline: multiline,
10778                 modal : true
10779             });
10780             return this;
10781         },
10782
10783         /**
10784          * Button config that displays a single OK button
10785          * @type Object
10786          */
10787         OK : {ok:true},
10788         /**
10789          * Button config that displays Yes and No buttons
10790          * @type Object
10791          */
10792         YESNO : {yes:true, no:true},
10793         /**
10794          * Button config that displays OK and Cancel buttons
10795          * @type Object
10796          */
10797         OKCANCEL : {ok:true, cancel:true},
10798         /**
10799          * Button config that displays Yes, No and Cancel buttons
10800          * @type Object
10801          */
10802         YESNOCANCEL : {yes:true, no:true, cancel:true},
10803
10804         /**
10805          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10806          * @type Number
10807          */
10808         defaultTextHeight : 75,
10809         /**
10810          * The maximum width in pixels of the message box (defaults to 600)
10811          * @type Number
10812          */
10813         maxWidth : 600,
10814         /**
10815          * The minimum width in pixels of the message box (defaults to 100)
10816          * @type Number
10817          */
10818         minWidth : 100,
10819         /**
10820          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10821          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10822          * @type Number
10823          */
10824         minProgressWidth : 250,
10825         /**
10826          * An object containing the default button text strings that can be overriden for localized language support.
10827          * Supported properties are: ok, cancel, yes and no.
10828          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10829          * @type Object
10830          */
10831         buttonText : {
10832             ok : "OK",
10833             cancel : "Cancel",
10834             yes : "Yes",
10835             no : "No"
10836         }
10837     };
10838 }();
10839
10840 /**
10841  * Shorthand for {@link Roo.MessageBox}
10842  */
10843 Roo.Msg = Roo.MessageBox;/*
10844  * Based on:
10845  * Ext JS Library 1.1.1
10846  * Copyright(c) 2006-2007, Ext JS, LLC.
10847  *
10848  * Originally Released Under LGPL - original licence link has changed is not relivant.
10849  *
10850  * Fork - LGPL
10851  * <script type="text/javascript">
10852  */
10853 /**
10854  * @class Roo.QuickTips
10855  * Provides attractive and customizable tooltips for any element.
10856  * @static
10857  */
10858 Roo.QuickTips = function(){
10859     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10860     var ce, bd, xy, dd;
10861     var visible = false, disabled = true, inited = false;
10862     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10863     
10864     var onOver = function(e){
10865         if(disabled){
10866             return;
10867         }
10868         var t = e.getTarget();
10869         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10870             return;
10871         }
10872         if(ce && t == ce.el){
10873             clearTimeout(hideProc);
10874             return;
10875         }
10876         if(t && tagEls[t.id]){
10877             tagEls[t.id].el = t;
10878             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10879             return;
10880         }
10881         var ttp, et = Roo.fly(t);
10882         var ns = cfg.namespace;
10883         if(tm.interceptTitles && t.title){
10884             ttp = t.title;
10885             t.qtip = ttp;
10886             t.removeAttribute("title");
10887             e.preventDefault();
10888         }else{
10889             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10890         }
10891         if(ttp){
10892             showProc = show.defer(tm.showDelay, tm, [{
10893                 el: t, 
10894                 text: ttp.replace(/\\n/g,'<br/>'),
10895                 width: et.getAttributeNS(ns, cfg.width),
10896                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10897                 title: et.getAttributeNS(ns, cfg.title),
10898                     cls: et.getAttributeNS(ns, cfg.cls)
10899             }]);
10900         }
10901     };
10902     
10903     var onOut = function(e){
10904         clearTimeout(showProc);
10905         var t = e.getTarget();
10906         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10907             hideProc = setTimeout(hide, tm.hideDelay);
10908         }
10909     };
10910     
10911     var onMove = function(e){
10912         if(disabled){
10913             return;
10914         }
10915         xy = e.getXY();
10916         xy[1] += 18;
10917         if(tm.trackMouse && ce){
10918             el.setXY(xy);
10919         }
10920     };
10921     
10922     var onDown = function(e){
10923         clearTimeout(showProc);
10924         clearTimeout(hideProc);
10925         if(!e.within(el)){
10926             if(tm.hideOnClick){
10927                 hide();
10928                 tm.disable();
10929                 tm.enable.defer(100, tm);
10930             }
10931         }
10932     };
10933     
10934     var getPad = function(){
10935         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10936     };
10937
10938     var show = function(o){
10939         if(disabled){
10940             return;
10941         }
10942         clearTimeout(dismissProc);
10943         ce = o;
10944         if(removeCls){ // in case manually hidden
10945             el.removeClass(removeCls);
10946             removeCls = null;
10947         }
10948         if(ce.cls){
10949             el.addClass(ce.cls);
10950             removeCls = ce.cls;
10951         }
10952         if(ce.title){
10953             tipTitle.update(ce.title);
10954             tipTitle.show();
10955         }else{
10956             tipTitle.update('');
10957             tipTitle.hide();
10958         }
10959         el.dom.style.width  = tm.maxWidth+'px';
10960         //tipBody.dom.style.width = '';
10961         tipBodyText.update(o.text);
10962         var p = getPad(), w = ce.width;
10963         if(!w){
10964             var td = tipBodyText.dom;
10965             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10966             if(aw > tm.maxWidth){
10967                 w = tm.maxWidth;
10968             }else if(aw < tm.minWidth){
10969                 w = tm.minWidth;
10970             }else{
10971                 w = aw;
10972             }
10973         }
10974         //tipBody.setWidth(w);
10975         el.setWidth(parseInt(w, 10) + p);
10976         if(ce.autoHide === false){
10977             close.setDisplayed(true);
10978             if(dd){
10979                 dd.unlock();
10980             }
10981         }else{
10982             close.setDisplayed(false);
10983             if(dd){
10984                 dd.lock();
10985             }
10986         }
10987         if(xy){
10988             el.avoidY = xy[1]-18;
10989             el.setXY(xy);
10990         }
10991         if(tm.animate){
10992             el.setOpacity(.1);
10993             el.setStyle("visibility", "visible");
10994             el.fadeIn({callback: afterShow});
10995         }else{
10996             afterShow();
10997         }
10998     };
10999     
11000     var afterShow = function(){
11001         if(ce){
11002             el.show();
11003             esc.enable();
11004             if(tm.autoDismiss && ce.autoHide !== false){
11005                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11006             }
11007         }
11008     };
11009     
11010     var hide = function(noanim){
11011         clearTimeout(dismissProc);
11012         clearTimeout(hideProc);
11013         ce = null;
11014         if(el.isVisible()){
11015             esc.disable();
11016             if(noanim !== true && tm.animate){
11017                 el.fadeOut({callback: afterHide});
11018             }else{
11019                 afterHide();
11020             } 
11021         }
11022     };
11023     
11024     var afterHide = function(){
11025         el.hide();
11026         if(removeCls){
11027             el.removeClass(removeCls);
11028             removeCls = null;
11029         }
11030     };
11031     
11032     return {
11033         /**
11034         * @cfg {Number} minWidth
11035         * The minimum width of the quick tip (defaults to 40)
11036         */
11037        minWidth : 40,
11038         /**
11039         * @cfg {Number} maxWidth
11040         * The maximum width of the quick tip (defaults to 300)
11041         */
11042        maxWidth : 300,
11043         /**
11044         * @cfg {Boolean} interceptTitles
11045         * True to automatically use the element's DOM title value if available (defaults to false)
11046         */
11047        interceptTitles : false,
11048         /**
11049         * @cfg {Boolean} trackMouse
11050         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11051         */
11052        trackMouse : false,
11053         /**
11054         * @cfg {Boolean} hideOnClick
11055         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11056         */
11057        hideOnClick : true,
11058         /**
11059         * @cfg {Number} showDelay
11060         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11061         */
11062        showDelay : 500,
11063         /**
11064         * @cfg {Number} hideDelay
11065         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11066         */
11067        hideDelay : 200,
11068         /**
11069         * @cfg {Boolean} autoHide
11070         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11071         * Used in conjunction with hideDelay.
11072         */
11073        autoHide : true,
11074         /**
11075         * @cfg {Boolean}
11076         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11077         * (defaults to true).  Used in conjunction with autoDismissDelay.
11078         */
11079        autoDismiss : true,
11080         /**
11081         * @cfg {Number}
11082         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11083         */
11084        autoDismissDelay : 5000,
11085        /**
11086         * @cfg {Boolean} animate
11087         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11088         */
11089        animate : false,
11090
11091        /**
11092         * @cfg {String} title
11093         * Title text to display (defaults to '').  This can be any valid HTML markup.
11094         */
11095         title: '',
11096        /**
11097         * @cfg {String} text
11098         * Body text to display (defaults to '').  This can be any valid HTML markup.
11099         */
11100         text : '',
11101        /**
11102         * @cfg {String} cls
11103         * A CSS class to apply to the base quick tip element (defaults to '').
11104         */
11105         cls : '',
11106        /**
11107         * @cfg {Number} width
11108         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11109         * minWidth or maxWidth.
11110         */
11111         width : null,
11112
11113     /**
11114      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11115      * or display QuickTips in a page.
11116      */
11117        init : function(){
11118           tm = Roo.QuickTips;
11119           cfg = tm.tagConfig;
11120           if(!inited){
11121               if(!Roo.isReady){ // allow calling of init() before onReady
11122                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11123                   return;
11124               }
11125               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11126               el.fxDefaults = {stopFx: true};
11127               // maximum custom styling
11128               //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>');
11129               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>');              
11130               tipTitle = el.child('h3');
11131               tipTitle.enableDisplayMode("block");
11132               tipBody = el.child('div.x-tip-bd');
11133               tipBodyText = el.child('div.x-tip-bd-inner');
11134               //bdLeft = el.child('div.x-tip-bd-left');
11135               //bdRight = el.child('div.x-tip-bd-right');
11136               close = el.child('div.x-tip-close');
11137               close.enableDisplayMode("block");
11138               close.on("click", hide);
11139               var d = Roo.get(document);
11140               d.on("mousedown", onDown);
11141               d.on("mouseover", onOver);
11142               d.on("mouseout", onOut);
11143               d.on("mousemove", onMove);
11144               esc = d.addKeyListener(27, hide);
11145               esc.disable();
11146               if(Roo.dd.DD){
11147                   dd = el.initDD("default", null, {
11148                       onDrag : function(){
11149                           el.sync();  
11150                       }
11151                   });
11152                   dd.setHandleElId(tipTitle.id);
11153                   dd.lock();
11154               }
11155               inited = true;
11156           }
11157           this.enable(); 
11158        },
11159
11160     /**
11161      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11162      * are supported:
11163      * <pre>
11164 Property    Type                   Description
11165 ----------  ---------------------  ------------------------------------------------------------------------
11166 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11167      * </ul>
11168      * @param {Object} config The config object
11169      */
11170        register : function(config){
11171            var cs = config instanceof Array ? config : arguments;
11172            for(var i = 0, len = cs.length; i < len; i++) {
11173                var c = cs[i];
11174                var target = c.target;
11175                if(target){
11176                    if(target instanceof Array){
11177                        for(var j = 0, jlen = target.length; j < jlen; j++){
11178                            tagEls[target[j]] = c;
11179                        }
11180                    }else{
11181                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11182                    }
11183                }
11184            }
11185        },
11186
11187     /**
11188      * Removes this quick tip from its element and destroys it.
11189      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11190      */
11191        unregister : function(el){
11192            delete tagEls[Roo.id(el)];
11193        },
11194
11195     /**
11196      * Enable this quick tip.
11197      */
11198        enable : function(){
11199            if(inited && disabled){
11200                locks.pop();
11201                if(locks.length < 1){
11202                    disabled = false;
11203                }
11204            }
11205        },
11206
11207     /**
11208      * Disable this quick tip.
11209      */
11210        disable : function(){
11211           disabled = true;
11212           clearTimeout(showProc);
11213           clearTimeout(hideProc);
11214           clearTimeout(dismissProc);
11215           if(ce){
11216               hide(true);
11217           }
11218           locks.push(1);
11219        },
11220
11221     /**
11222      * Returns true if the quick tip is enabled, else false.
11223      */
11224        isEnabled : function(){
11225             return !disabled;
11226        },
11227
11228         // private
11229        tagConfig : {
11230            namespace : "roo", // was ext?? this may break..
11231            alt_namespace : "ext",
11232            attribute : "qtip",
11233            width : "width",
11234            target : "target",
11235            title : "qtitle",
11236            hide : "hide",
11237            cls : "qclass"
11238        }
11239    };
11240 }();
11241
11242 // backwards compat
11243 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11244  * Based on:
11245  * Ext JS Library 1.1.1
11246  * Copyright(c) 2006-2007, Ext JS, LLC.
11247  *
11248  * Originally Released Under LGPL - original licence link has changed is not relivant.
11249  *
11250  * Fork - LGPL
11251  * <script type="text/javascript">
11252  */
11253  
11254
11255 /**
11256  * @class Roo.tree.TreePanel
11257  * @extends Roo.data.Tree
11258  * @cfg {Roo.tree.TreeNode} root The root node
11259  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11260  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11261  * @cfg {Boolean} enableDD true to enable drag and drop
11262  * @cfg {Boolean} enableDrag true to enable just drag
11263  * @cfg {Boolean} enableDrop true to enable just drop
11264  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11265  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11266  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11267  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11268  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11269  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11270  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11271  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11272  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11273  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11274  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11275  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11276  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11277  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11278  * @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>
11279  * @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>
11280  * 
11281  * @constructor
11282  * @param {String/HTMLElement/Element} el The container element
11283  * @param {Object} config
11284  */
11285 Roo.tree.TreePanel = function(el, config){
11286     var root = false;
11287     var loader = false;
11288     if (config.root) {
11289         root = config.root;
11290         delete config.root;
11291     }
11292     if (config.loader) {
11293         loader = config.loader;
11294         delete config.loader;
11295     }
11296     
11297     Roo.apply(this, config);
11298     Roo.tree.TreePanel.superclass.constructor.call(this);
11299     this.el = Roo.get(el);
11300     this.el.addClass('x-tree');
11301     //console.log(root);
11302     if (root) {
11303         this.setRootNode( Roo.factory(root, Roo.tree));
11304     }
11305     if (loader) {
11306         this.loader = Roo.factory(loader, Roo.tree);
11307     }
11308    /**
11309     * Read-only. The id of the container element becomes this TreePanel's id.
11310     */
11311     this.id = this.el.id;
11312     this.addEvents({
11313         /**
11314         * @event beforeload
11315         * Fires before a node is loaded, return false to cancel
11316         * @param {Node} node The node being loaded
11317         */
11318         "beforeload" : true,
11319         /**
11320         * @event load
11321         * Fires when a node is loaded
11322         * @param {Node} node The node that was loaded
11323         */
11324         "load" : true,
11325         /**
11326         * @event textchange
11327         * Fires when the text for a node is changed
11328         * @param {Node} node The node
11329         * @param {String} text The new text
11330         * @param {String} oldText The old text
11331         */
11332         "textchange" : true,
11333         /**
11334         * @event beforeexpand
11335         * Fires before a node is expanded, return false to cancel.
11336         * @param {Node} node The node
11337         * @param {Boolean} deep
11338         * @param {Boolean} anim
11339         */
11340         "beforeexpand" : true,
11341         /**
11342         * @event beforecollapse
11343         * Fires before a node is collapsed, return false to cancel.
11344         * @param {Node} node The node
11345         * @param {Boolean} deep
11346         * @param {Boolean} anim
11347         */
11348         "beforecollapse" : true,
11349         /**
11350         * @event expand
11351         * Fires when a node is expanded
11352         * @param {Node} node The node
11353         */
11354         "expand" : true,
11355         /**
11356         * @event disabledchange
11357         * Fires when the disabled status of a node changes
11358         * @param {Node} node The node
11359         * @param {Boolean} disabled
11360         */
11361         "disabledchange" : true,
11362         /**
11363         * @event collapse
11364         * Fires when a node is collapsed
11365         * @param {Node} node The node
11366         */
11367         "collapse" : true,
11368         /**
11369         * @event beforeclick
11370         * Fires before click processing on a node. Return false to cancel the default action.
11371         * @param {Node} node The node
11372         * @param {Roo.EventObject} e The event object
11373         */
11374         "beforeclick":true,
11375         /**
11376         * @event checkchange
11377         * Fires when a node with a checkbox's checked property changes
11378         * @param {Node} this This node
11379         * @param {Boolean} checked
11380         */
11381         "checkchange":true,
11382         /**
11383         * @event click
11384         * Fires when a node is clicked
11385         * @param {Node} node The node
11386         * @param {Roo.EventObject} e The event object
11387         */
11388         "click":true,
11389         /**
11390         * @event dblclick
11391         * Fires when a node is double clicked
11392         * @param {Node} node The node
11393         * @param {Roo.EventObject} e The event object
11394         */
11395         "dblclick":true,
11396         /**
11397         * @event contextmenu
11398         * Fires when a node is right clicked
11399         * @param {Node} node The node
11400         * @param {Roo.EventObject} e The event object
11401         */
11402         "contextmenu":true,
11403         /**
11404         * @event beforechildrenrendered
11405         * Fires right before the child nodes for a node are rendered
11406         * @param {Node} node The node
11407         */
11408         "beforechildrenrendered":true,
11409         /**
11410         * @event startdrag
11411         * Fires when a node starts being dragged
11412         * @param {Roo.tree.TreePanel} this
11413         * @param {Roo.tree.TreeNode} node
11414         * @param {event} e The raw browser event
11415         */ 
11416        "startdrag" : true,
11417        /**
11418         * @event enddrag
11419         * Fires when a drag operation is complete
11420         * @param {Roo.tree.TreePanel} this
11421         * @param {Roo.tree.TreeNode} node
11422         * @param {event} e The raw browser event
11423         */
11424        "enddrag" : true,
11425        /**
11426         * @event dragdrop
11427         * Fires when a dragged node is dropped on a valid DD target
11428         * @param {Roo.tree.TreePanel} this
11429         * @param {Roo.tree.TreeNode} node
11430         * @param {DD} dd The dd it was dropped on
11431         * @param {event} e The raw browser event
11432         */
11433        "dragdrop" : true,
11434        /**
11435         * @event beforenodedrop
11436         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11437         * passed to handlers has the following properties:<br />
11438         * <ul style="padding:5px;padding-left:16px;">
11439         * <li>tree - The TreePanel</li>
11440         * <li>target - The node being targeted for the drop</li>
11441         * <li>data - The drag data from the drag source</li>
11442         * <li>point - The point of the drop - append, above or below</li>
11443         * <li>source - The drag source</li>
11444         * <li>rawEvent - Raw mouse event</li>
11445         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11446         * to be inserted by setting them on this object.</li>
11447         * <li>cancel - Set this to true to cancel the drop.</li>
11448         * </ul>
11449         * @param {Object} dropEvent
11450         */
11451        "beforenodedrop" : true,
11452        /**
11453         * @event nodedrop
11454         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11455         * passed to handlers has the following properties:<br />
11456         * <ul style="padding:5px;padding-left:16px;">
11457         * <li>tree - The TreePanel</li>
11458         * <li>target - The node being targeted for the drop</li>
11459         * <li>data - The drag data from the drag source</li>
11460         * <li>point - The point of the drop - append, above or below</li>
11461         * <li>source - The drag source</li>
11462         * <li>rawEvent - Raw mouse event</li>
11463         * <li>dropNode - Dropped node(s).</li>
11464         * </ul>
11465         * @param {Object} dropEvent
11466         */
11467        "nodedrop" : true,
11468         /**
11469         * @event nodedragover
11470         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11471         * passed to handlers has the following properties:<br />
11472         * <ul style="padding:5px;padding-left:16px;">
11473         * <li>tree - The TreePanel</li>
11474         * <li>target - The node being targeted for the drop</li>
11475         * <li>data - The drag data from the drag source</li>
11476         * <li>point - The point of the drop - append, above or below</li>
11477         * <li>source - The drag source</li>
11478         * <li>rawEvent - Raw mouse event</li>
11479         * <li>dropNode - Drop node(s) provided by the source.</li>
11480         * <li>cancel - Set this to true to signal drop not allowed.</li>
11481         * </ul>
11482         * @param {Object} dragOverEvent
11483         */
11484        "nodedragover" : true,
11485        /**
11486         * @event appendnode
11487         * Fires when append node to the tree
11488         * @param {Roo.tree.TreePanel} this
11489         * @param {Roo.tree.TreeNode} node
11490         * @param {Number} index The index of the newly appended node
11491         */
11492        "appendnode" : true
11493         
11494     });
11495     if(this.singleExpand){
11496        this.on("beforeexpand", this.restrictExpand, this);
11497     }
11498     if (this.editor) {
11499         this.editor.tree = this;
11500         this.editor = Roo.factory(this.editor, Roo.tree);
11501     }
11502     
11503     if (this.selModel) {
11504         this.selModel = Roo.factory(this.selModel, Roo.tree);
11505     }
11506    
11507 };
11508 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11509     rootVisible : true,
11510     animate: Roo.enableFx,
11511     lines : true,
11512     enableDD : false,
11513     hlDrop : Roo.enableFx,
11514   
11515     renderer: false,
11516     
11517     rendererTip: false,
11518     // private
11519     restrictExpand : function(node){
11520         var p = node.parentNode;
11521         if(p){
11522             if(p.expandedChild && p.expandedChild.parentNode == p){
11523                 p.expandedChild.collapse();
11524             }
11525             p.expandedChild = node;
11526         }
11527     },
11528
11529     // private override
11530     setRootNode : function(node){
11531         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11532         if(!this.rootVisible){
11533             node.ui = new Roo.tree.RootTreeNodeUI(node);
11534         }
11535         return node;
11536     },
11537
11538     /**
11539      * Returns the container element for this TreePanel
11540      */
11541     getEl : function(){
11542         return this.el;
11543     },
11544
11545     /**
11546      * Returns the default TreeLoader for this TreePanel
11547      */
11548     getLoader : function(){
11549         return this.loader;
11550     },
11551
11552     /**
11553      * Expand all nodes
11554      */
11555     expandAll : function(){
11556         this.root.expand(true);
11557     },
11558
11559     /**
11560      * Collapse all nodes
11561      */
11562     collapseAll : function(){
11563         this.root.collapse(true);
11564     },
11565
11566     /**
11567      * Returns the selection model used by this TreePanel
11568      */
11569     getSelectionModel : function(){
11570         if(!this.selModel){
11571             this.selModel = new Roo.tree.DefaultSelectionModel();
11572         }
11573         return this.selModel;
11574     },
11575
11576     /**
11577      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11578      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11579      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11580      * @return {Array}
11581      */
11582     getChecked : function(a, startNode){
11583         startNode = startNode || this.root;
11584         var r = [];
11585         var f = function(){
11586             if(this.attributes.checked){
11587                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11588             }
11589         }
11590         startNode.cascade(f);
11591         return r;
11592     },
11593
11594     /**
11595      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11596      * @param {String} path
11597      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11598      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11599      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11600      */
11601     expandPath : function(path, attr, callback){
11602         attr = attr || "id";
11603         var keys = path.split(this.pathSeparator);
11604         var curNode = this.root;
11605         if(curNode.attributes[attr] != keys[1]){ // invalid root
11606             if(callback){
11607                 callback(false, null);
11608             }
11609             return;
11610         }
11611         var index = 1;
11612         var f = function(){
11613             if(++index == keys.length){
11614                 if(callback){
11615                     callback(true, curNode);
11616                 }
11617                 return;
11618             }
11619             var c = curNode.findChild(attr, keys[index]);
11620             if(!c){
11621                 if(callback){
11622                     callback(false, curNode);
11623                 }
11624                 return;
11625             }
11626             curNode = c;
11627             c.expand(false, false, f);
11628         };
11629         curNode.expand(false, false, f);
11630     },
11631
11632     /**
11633      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11634      * @param {String} path
11635      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11636      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11637      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11638      */
11639     selectPath : function(path, attr, callback){
11640         attr = attr || "id";
11641         var keys = path.split(this.pathSeparator);
11642         var v = keys.pop();
11643         if(keys.length > 0){
11644             var f = function(success, node){
11645                 if(success && node){
11646                     var n = node.findChild(attr, v);
11647                     if(n){
11648                         n.select();
11649                         if(callback){
11650                             callback(true, n);
11651                         }
11652                     }else if(callback){
11653                         callback(false, n);
11654                     }
11655                 }else{
11656                     if(callback){
11657                         callback(false, n);
11658                     }
11659                 }
11660             };
11661             this.expandPath(keys.join(this.pathSeparator), attr, f);
11662         }else{
11663             this.root.select();
11664             if(callback){
11665                 callback(true, this.root);
11666             }
11667         }
11668     },
11669
11670     getTreeEl : function(){
11671         return this.el;
11672     },
11673
11674     /**
11675      * Trigger rendering of this TreePanel
11676      */
11677     render : function(){
11678         if (this.innerCt) {
11679             return this; // stop it rendering more than once!!
11680         }
11681         
11682         this.innerCt = this.el.createChild({tag:"ul",
11683                cls:"x-tree-root-ct " +
11684                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11685
11686         if(this.containerScroll){
11687             Roo.dd.ScrollManager.register(this.el);
11688         }
11689         if((this.enableDD || this.enableDrop) && !this.dropZone){
11690            /**
11691             * The dropZone used by this tree if drop is enabled
11692             * @type Roo.tree.TreeDropZone
11693             */
11694              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11695                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11696            });
11697         }
11698         if((this.enableDD || this.enableDrag) && !this.dragZone){
11699            /**
11700             * The dragZone used by this tree if drag is enabled
11701             * @type Roo.tree.TreeDragZone
11702             */
11703             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11704                ddGroup: this.ddGroup || "TreeDD",
11705                scroll: this.ddScroll
11706            });
11707         }
11708         this.getSelectionModel().init(this);
11709         if (!this.root) {
11710             Roo.log("ROOT not set in tree");
11711             return this;
11712         }
11713         this.root.render();
11714         if(!this.rootVisible){
11715             this.root.renderChildren();
11716         }
11717         return this;
11718     }
11719 });/*
11720  * Based on:
11721  * Ext JS Library 1.1.1
11722  * Copyright(c) 2006-2007, Ext JS, LLC.
11723  *
11724  * Originally Released Under LGPL - original licence link has changed is not relivant.
11725  *
11726  * Fork - LGPL
11727  * <script type="text/javascript">
11728  */
11729  
11730
11731 /**
11732  * @class Roo.tree.DefaultSelectionModel
11733  * @extends Roo.util.Observable
11734  * The default single selection for a TreePanel.
11735  * @param {Object} cfg Configuration
11736  */
11737 Roo.tree.DefaultSelectionModel = function(cfg){
11738    this.selNode = null;
11739    
11740    
11741    
11742    this.addEvents({
11743        /**
11744         * @event selectionchange
11745         * Fires when the selected node changes
11746         * @param {DefaultSelectionModel} this
11747         * @param {TreeNode} node the new selection
11748         */
11749        "selectionchange" : true,
11750
11751        /**
11752         * @event beforeselect
11753         * Fires before the selected node changes, return false to cancel the change
11754         * @param {DefaultSelectionModel} this
11755         * @param {TreeNode} node the new selection
11756         * @param {TreeNode} node the old selection
11757         */
11758        "beforeselect" : true
11759    });
11760    
11761     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11762 };
11763
11764 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11765     init : function(tree){
11766         this.tree = tree;
11767         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11768         tree.on("click", this.onNodeClick, this);
11769     },
11770     
11771     onNodeClick : function(node, e){
11772         if (e.ctrlKey && this.selNode == node)  {
11773             this.unselect(node);
11774             return;
11775         }
11776         this.select(node);
11777     },
11778     
11779     /**
11780      * Select a node.
11781      * @param {TreeNode} node The node to select
11782      * @return {TreeNode} The selected node
11783      */
11784     select : function(node){
11785         var last = this.selNode;
11786         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11787             if(last){
11788                 last.ui.onSelectedChange(false);
11789             }
11790             this.selNode = node;
11791             node.ui.onSelectedChange(true);
11792             this.fireEvent("selectionchange", this, node, last);
11793         }
11794         return node;
11795     },
11796     
11797     /**
11798      * Deselect a node.
11799      * @param {TreeNode} node The node to unselect
11800      */
11801     unselect : function(node){
11802         if(this.selNode == node){
11803             this.clearSelections();
11804         }    
11805     },
11806     
11807     /**
11808      * Clear all selections
11809      */
11810     clearSelections : function(){
11811         var n = this.selNode;
11812         if(n){
11813             n.ui.onSelectedChange(false);
11814             this.selNode = null;
11815             this.fireEvent("selectionchange", this, null);
11816         }
11817         return n;
11818     },
11819     
11820     /**
11821      * Get the selected node
11822      * @return {TreeNode} The selected node
11823      */
11824     getSelectedNode : function(){
11825         return this.selNode;    
11826     },
11827     
11828     /**
11829      * Returns true if the node is selected
11830      * @param {TreeNode} node The node to check
11831      * @return {Boolean}
11832      */
11833     isSelected : function(node){
11834         return this.selNode == node;  
11835     },
11836
11837     /**
11838      * Selects the node above the selected node in the tree, intelligently walking the nodes
11839      * @return TreeNode The new selection
11840      */
11841     selectPrevious : function(){
11842         var s = this.selNode || this.lastSelNode;
11843         if(!s){
11844             return null;
11845         }
11846         var ps = s.previousSibling;
11847         if(ps){
11848             if(!ps.isExpanded() || ps.childNodes.length < 1){
11849                 return this.select(ps);
11850             } else{
11851                 var lc = ps.lastChild;
11852                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11853                     lc = lc.lastChild;
11854                 }
11855                 return this.select(lc);
11856             }
11857         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11858             return this.select(s.parentNode);
11859         }
11860         return null;
11861     },
11862
11863     /**
11864      * Selects the node above the selected node in the tree, intelligently walking the nodes
11865      * @return TreeNode The new selection
11866      */
11867     selectNext : function(){
11868         var s = this.selNode || this.lastSelNode;
11869         if(!s){
11870             return null;
11871         }
11872         if(s.firstChild && s.isExpanded()){
11873              return this.select(s.firstChild);
11874          }else if(s.nextSibling){
11875              return this.select(s.nextSibling);
11876          }else if(s.parentNode){
11877             var newS = null;
11878             s.parentNode.bubble(function(){
11879                 if(this.nextSibling){
11880                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11881                     return false;
11882                 }
11883             });
11884             return newS;
11885          }
11886         return null;
11887     },
11888
11889     onKeyDown : function(e){
11890         var s = this.selNode || this.lastSelNode;
11891         // undesirable, but required
11892         var sm = this;
11893         if(!s){
11894             return;
11895         }
11896         var k = e.getKey();
11897         switch(k){
11898              case e.DOWN:
11899                  e.stopEvent();
11900                  this.selectNext();
11901              break;
11902              case e.UP:
11903                  e.stopEvent();
11904                  this.selectPrevious();
11905              break;
11906              case e.RIGHT:
11907                  e.preventDefault();
11908                  if(s.hasChildNodes()){
11909                      if(!s.isExpanded()){
11910                          s.expand();
11911                      }else if(s.firstChild){
11912                          this.select(s.firstChild, e);
11913                      }
11914                  }
11915              break;
11916              case e.LEFT:
11917                  e.preventDefault();
11918                  if(s.hasChildNodes() && s.isExpanded()){
11919                      s.collapse();
11920                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11921                      this.select(s.parentNode, e);
11922                  }
11923              break;
11924         };
11925     }
11926 });
11927
11928 /**
11929  * @class Roo.tree.MultiSelectionModel
11930  * @extends Roo.util.Observable
11931  * Multi selection for a TreePanel.
11932  * @param {Object} cfg Configuration
11933  */
11934 Roo.tree.MultiSelectionModel = function(){
11935    this.selNodes = [];
11936    this.selMap = {};
11937    this.addEvents({
11938        /**
11939         * @event selectionchange
11940         * Fires when the selected nodes change
11941         * @param {MultiSelectionModel} this
11942         * @param {Array} nodes Array of the selected nodes
11943         */
11944        "selectionchange" : true
11945    });
11946    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11947    
11948 };
11949
11950 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11951     init : function(tree){
11952         this.tree = tree;
11953         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11954         tree.on("click", this.onNodeClick, this);
11955     },
11956     
11957     onNodeClick : function(node, e){
11958         this.select(node, e, e.ctrlKey);
11959     },
11960     
11961     /**
11962      * Select a node.
11963      * @param {TreeNode} node The node to select
11964      * @param {EventObject} e (optional) An event associated with the selection
11965      * @param {Boolean} keepExisting True to retain existing selections
11966      * @return {TreeNode} The selected node
11967      */
11968     select : function(node, e, keepExisting){
11969         if(keepExisting !== true){
11970             this.clearSelections(true);
11971         }
11972         if(this.isSelected(node)){
11973             this.lastSelNode = node;
11974             return node;
11975         }
11976         this.selNodes.push(node);
11977         this.selMap[node.id] = node;
11978         this.lastSelNode = node;
11979         node.ui.onSelectedChange(true);
11980         this.fireEvent("selectionchange", this, this.selNodes);
11981         return node;
11982     },
11983     
11984     /**
11985      * Deselect a node.
11986      * @param {TreeNode} node The node to unselect
11987      */
11988     unselect : function(node){
11989         if(this.selMap[node.id]){
11990             node.ui.onSelectedChange(false);
11991             var sn = this.selNodes;
11992             var index = -1;
11993             if(sn.indexOf){
11994                 index = sn.indexOf(node);
11995             }else{
11996                 for(var i = 0, len = sn.length; i < len; i++){
11997                     if(sn[i] == node){
11998                         index = i;
11999                         break;
12000                     }
12001                 }
12002             }
12003             if(index != -1){
12004                 this.selNodes.splice(index, 1);
12005             }
12006             delete this.selMap[node.id];
12007             this.fireEvent("selectionchange", this, this.selNodes);
12008         }
12009     },
12010     
12011     /**
12012      * Clear all selections
12013      */
12014     clearSelections : function(suppressEvent){
12015         var sn = this.selNodes;
12016         if(sn.length > 0){
12017             for(var i = 0, len = sn.length; i < len; i++){
12018                 sn[i].ui.onSelectedChange(false);
12019             }
12020             this.selNodes = [];
12021             this.selMap = {};
12022             if(suppressEvent !== true){
12023                 this.fireEvent("selectionchange", this, this.selNodes);
12024             }
12025         }
12026     },
12027     
12028     /**
12029      * Returns true if the node is selected
12030      * @param {TreeNode} node The node to check
12031      * @return {Boolean}
12032      */
12033     isSelected : function(node){
12034         return this.selMap[node.id] ? true : false;  
12035     },
12036     
12037     /**
12038      * Returns an array of the selected nodes
12039      * @return {Array}
12040      */
12041     getSelectedNodes : function(){
12042         return this.selNodes;    
12043     },
12044
12045     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12046
12047     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12048
12049     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12050 });/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060  
12061 /**
12062  * @class Roo.tree.TreeNode
12063  * @extends Roo.data.Node
12064  * @cfg {String} text The text for this node
12065  * @cfg {Boolean} expanded true to start the node expanded
12066  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12067  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12068  * @cfg {Boolean} disabled true to start the node disabled
12069  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12070  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12071  * @cfg {String} cls A css class to be added to the node
12072  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12073  * @cfg {String} href URL of the link used for the node (defaults to #)
12074  * @cfg {String} hrefTarget target frame for the link
12075  * @cfg {String} qtip An Ext QuickTip for the node
12076  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12077  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12078  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12079  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12080  * (defaults to undefined with no checkbox rendered)
12081  * @constructor
12082  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12083  */
12084 Roo.tree.TreeNode = function(attributes){
12085     attributes = attributes || {};
12086     if(typeof attributes == "string"){
12087         attributes = {text: attributes};
12088     }
12089     this.childrenRendered = false;
12090     this.rendered = false;
12091     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12092     this.expanded = attributes.expanded === true;
12093     this.isTarget = attributes.isTarget !== false;
12094     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12095     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12096
12097     /**
12098      * Read-only. The text for this node. To change it use setText().
12099      * @type String
12100      */
12101     this.text = attributes.text;
12102     /**
12103      * True if this node is disabled.
12104      * @type Boolean
12105      */
12106     this.disabled = attributes.disabled === true;
12107
12108     this.addEvents({
12109         /**
12110         * @event textchange
12111         * Fires when the text for this node is changed
12112         * @param {Node} this This node
12113         * @param {String} text The new text
12114         * @param {String} oldText The old text
12115         */
12116         "textchange" : true,
12117         /**
12118         * @event beforeexpand
12119         * Fires before this node is expanded, return false to cancel.
12120         * @param {Node} this This node
12121         * @param {Boolean} deep
12122         * @param {Boolean} anim
12123         */
12124         "beforeexpand" : true,
12125         /**
12126         * @event beforecollapse
12127         * Fires before this node is collapsed, return false to cancel.
12128         * @param {Node} this This node
12129         * @param {Boolean} deep
12130         * @param {Boolean} anim
12131         */
12132         "beforecollapse" : true,
12133         /**
12134         * @event expand
12135         * Fires when this node is expanded
12136         * @param {Node} this This node
12137         */
12138         "expand" : true,
12139         /**
12140         * @event disabledchange
12141         * Fires when the disabled status of this node changes
12142         * @param {Node} this This node
12143         * @param {Boolean} disabled
12144         */
12145         "disabledchange" : true,
12146         /**
12147         * @event collapse
12148         * Fires when this node is collapsed
12149         * @param {Node} this This node
12150         */
12151         "collapse" : true,
12152         /**
12153         * @event beforeclick
12154         * Fires before click processing. Return false to cancel the default action.
12155         * @param {Node} this This node
12156         * @param {Roo.EventObject} e The event object
12157         */
12158         "beforeclick":true,
12159         /**
12160         * @event checkchange
12161         * Fires when a node with a checkbox's checked property changes
12162         * @param {Node} this This node
12163         * @param {Boolean} checked
12164         */
12165         "checkchange":true,
12166         /**
12167         * @event click
12168         * Fires when this node is clicked
12169         * @param {Node} this This node
12170         * @param {Roo.EventObject} e The event object
12171         */
12172         "click":true,
12173         /**
12174         * @event dblclick
12175         * Fires when this node is double clicked
12176         * @param {Node} this This node
12177         * @param {Roo.EventObject} e The event object
12178         */
12179         "dblclick":true,
12180         /**
12181         * @event contextmenu
12182         * Fires when this node is right clicked
12183         * @param {Node} this This node
12184         * @param {Roo.EventObject} e The event object
12185         */
12186         "contextmenu":true,
12187         /**
12188         * @event beforechildrenrendered
12189         * Fires right before the child nodes for this node are rendered
12190         * @param {Node} this This node
12191         */
12192         "beforechildrenrendered":true
12193     });
12194
12195     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12196
12197     /**
12198      * Read-only. The UI for this node
12199      * @type TreeNodeUI
12200      */
12201     this.ui = new uiClass(this);
12202     
12203     // finally support items[]
12204     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12205         return;
12206     }
12207     
12208     
12209     Roo.each(this.attributes.items, function(c) {
12210         this.appendChild(Roo.factory(c,Roo.Tree));
12211     }, this);
12212     delete this.attributes.items;
12213     
12214     
12215     
12216 };
12217 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12218     preventHScroll: true,
12219     /**
12220      * Returns true if this node is expanded
12221      * @return {Boolean}
12222      */
12223     isExpanded : function(){
12224         return this.expanded;
12225     },
12226
12227     /**
12228      * Returns the UI object for this node
12229      * @return {TreeNodeUI}
12230      */
12231     getUI : function(){
12232         return this.ui;
12233     },
12234
12235     // private override
12236     setFirstChild : function(node){
12237         var of = this.firstChild;
12238         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12239         if(this.childrenRendered && of && node != of){
12240             of.renderIndent(true, true);
12241         }
12242         if(this.rendered){
12243             this.renderIndent(true, true);
12244         }
12245     },
12246
12247     // private override
12248     setLastChild : function(node){
12249         var ol = this.lastChild;
12250         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12251         if(this.childrenRendered && ol && node != ol){
12252             ol.renderIndent(true, true);
12253         }
12254         if(this.rendered){
12255             this.renderIndent(true, true);
12256         }
12257     },
12258
12259     // these methods are overridden to provide lazy rendering support
12260     // private override
12261     appendChild : function()
12262     {
12263         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12264         if(node && this.childrenRendered){
12265             node.render();
12266         }
12267         this.ui.updateExpandIcon();
12268         return node;
12269     },
12270
12271     // private override
12272     removeChild : function(node){
12273         this.ownerTree.getSelectionModel().unselect(node);
12274         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12275         // if it's been rendered remove dom node
12276         if(this.childrenRendered){
12277             node.ui.remove();
12278         }
12279         if(this.childNodes.length < 1){
12280             this.collapse(false, false);
12281         }else{
12282             this.ui.updateExpandIcon();
12283         }
12284         if(!this.firstChild) {
12285             this.childrenRendered = false;
12286         }
12287         return node;
12288     },
12289
12290     // private override
12291     insertBefore : function(node, refNode){
12292         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12293         if(newNode && refNode && this.childrenRendered){
12294             node.render();
12295         }
12296         this.ui.updateExpandIcon();
12297         return newNode;
12298     },
12299
12300     /**
12301      * Sets the text for this node
12302      * @param {String} text
12303      */
12304     setText : function(text){
12305         var oldText = this.text;
12306         this.text = text;
12307         this.attributes.text = text;
12308         if(this.rendered){ // event without subscribing
12309             this.ui.onTextChange(this, text, oldText);
12310         }
12311         this.fireEvent("textchange", this, text, oldText);
12312     },
12313
12314     /**
12315      * Triggers selection of this node
12316      */
12317     select : function(){
12318         this.getOwnerTree().getSelectionModel().select(this);
12319     },
12320
12321     /**
12322      * Triggers deselection of this node
12323      */
12324     unselect : function(){
12325         this.getOwnerTree().getSelectionModel().unselect(this);
12326     },
12327
12328     /**
12329      * Returns true if this node is selected
12330      * @return {Boolean}
12331      */
12332     isSelected : function(){
12333         return this.getOwnerTree().getSelectionModel().isSelected(this);
12334     },
12335
12336     /**
12337      * Expand this node.
12338      * @param {Boolean} deep (optional) True to expand all children as well
12339      * @param {Boolean} anim (optional) false to cancel the default animation
12340      * @param {Function} callback (optional) A callback to be called when
12341      * expanding this node completes (does not wait for deep expand to complete).
12342      * Called with 1 parameter, this node.
12343      */
12344     expand : function(deep, anim, callback){
12345         if(!this.expanded){
12346             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12347                 return;
12348             }
12349             if(!this.childrenRendered){
12350                 this.renderChildren();
12351             }
12352             this.expanded = true;
12353             
12354             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12355                 this.ui.animExpand(function(){
12356                     this.fireEvent("expand", this);
12357                     if(typeof callback == "function"){
12358                         callback(this);
12359                     }
12360                     if(deep === true){
12361                         this.expandChildNodes(true);
12362                     }
12363                 }.createDelegate(this));
12364                 return;
12365             }else{
12366                 this.ui.expand();
12367                 this.fireEvent("expand", this);
12368                 if(typeof callback == "function"){
12369                     callback(this);
12370                 }
12371             }
12372         }else{
12373            if(typeof callback == "function"){
12374                callback(this);
12375            }
12376         }
12377         if(deep === true){
12378             this.expandChildNodes(true);
12379         }
12380     },
12381
12382     isHiddenRoot : function(){
12383         return this.isRoot && !this.getOwnerTree().rootVisible;
12384     },
12385
12386     /**
12387      * Collapse this node.
12388      * @param {Boolean} deep (optional) True to collapse all children as well
12389      * @param {Boolean} anim (optional) false to cancel the default animation
12390      */
12391     collapse : function(deep, anim){
12392         if(this.expanded && !this.isHiddenRoot()){
12393             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12394                 return;
12395             }
12396             this.expanded = false;
12397             if((this.getOwnerTree().animate && anim !== false) || anim){
12398                 this.ui.animCollapse(function(){
12399                     this.fireEvent("collapse", this);
12400                     if(deep === true){
12401                         this.collapseChildNodes(true);
12402                     }
12403                 }.createDelegate(this));
12404                 return;
12405             }else{
12406                 this.ui.collapse();
12407                 this.fireEvent("collapse", this);
12408             }
12409         }
12410         if(deep === true){
12411             var cs = this.childNodes;
12412             for(var i = 0, len = cs.length; i < len; i++) {
12413                 cs[i].collapse(true, false);
12414             }
12415         }
12416     },
12417
12418     // private
12419     delayedExpand : function(delay){
12420         if(!this.expandProcId){
12421             this.expandProcId = this.expand.defer(delay, this);
12422         }
12423     },
12424
12425     // private
12426     cancelExpand : function(){
12427         if(this.expandProcId){
12428             clearTimeout(this.expandProcId);
12429         }
12430         this.expandProcId = false;
12431     },
12432
12433     /**
12434      * Toggles expanded/collapsed state of the node
12435      */
12436     toggle : function(){
12437         if(this.expanded){
12438             this.collapse();
12439         }else{
12440             this.expand();
12441         }
12442     },
12443
12444     /**
12445      * Ensures all parent nodes are expanded
12446      */
12447     ensureVisible : function(callback){
12448         var tree = this.getOwnerTree();
12449         tree.expandPath(this.parentNode.getPath(), false, function(){
12450             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12451             Roo.callback(callback);
12452         }.createDelegate(this));
12453     },
12454
12455     /**
12456      * Expand all child nodes
12457      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12458      */
12459     expandChildNodes : function(deep){
12460         var cs = this.childNodes;
12461         for(var i = 0, len = cs.length; i < len; i++) {
12462                 cs[i].expand(deep);
12463         }
12464     },
12465
12466     /**
12467      * Collapse all child nodes
12468      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12469      */
12470     collapseChildNodes : function(deep){
12471         var cs = this.childNodes;
12472         for(var i = 0, len = cs.length; i < len; i++) {
12473                 cs[i].collapse(deep);
12474         }
12475     },
12476
12477     /**
12478      * Disables this node
12479      */
12480     disable : function(){
12481         this.disabled = true;
12482         this.unselect();
12483         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12484             this.ui.onDisableChange(this, true);
12485         }
12486         this.fireEvent("disabledchange", this, true);
12487     },
12488
12489     /**
12490      * Enables this node
12491      */
12492     enable : function(){
12493         this.disabled = false;
12494         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12495             this.ui.onDisableChange(this, false);
12496         }
12497         this.fireEvent("disabledchange", this, false);
12498     },
12499
12500     // private
12501     renderChildren : function(suppressEvent){
12502         if(suppressEvent !== false){
12503             this.fireEvent("beforechildrenrendered", this);
12504         }
12505         var cs = this.childNodes;
12506         for(var i = 0, len = cs.length; i < len; i++){
12507             cs[i].render(true);
12508         }
12509         this.childrenRendered = true;
12510     },
12511
12512     // private
12513     sort : function(fn, scope){
12514         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12515         if(this.childrenRendered){
12516             var cs = this.childNodes;
12517             for(var i = 0, len = cs.length; i < len; i++){
12518                 cs[i].render(true);
12519             }
12520         }
12521     },
12522
12523     // private
12524     render : function(bulkRender){
12525         this.ui.render(bulkRender);
12526         if(!this.rendered){
12527             this.rendered = true;
12528             if(this.expanded){
12529                 this.expanded = false;
12530                 this.expand(false, false);
12531             }
12532         }
12533     },
12534
12535     // private
12536     renderIndent : function(deep, refresh){
12537         if(refresh){
12538             this.ui.childIndent = null;
12539         }
12540         this.ui.renderIndent();
12541         if(deep === true && this.childrenRendered){
12542             var cs = this.childNodes;
12543             for(var i = 0, len = cs.length; i < len; i++){
12544                 cs[i].renderIndent(true, refresh);
12545             }
12546         }
12547     }
12548 });/*
12549  * Based on:
12550  * Ext JS Library 1.1.1
12551  * Copyright(c) 2006-2007, Ext JS, LLC.
12552  *
12553  * Originally Released Under LGPL - original licence link has changed is not relivant.
12554  *
12555  * Fork - LGPL
12556  * <script type="text/javascript">
12557  */
12558  
12559 /**
12560  * @class Roo.tree.AsyncTreeNode
12561  * @extends Roo.tree.TreeNode
12562  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12563  * @constructor
12564  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12565  */
12566  Roo.tree.AsyncTreeNode = function(config){
12567     this.loaded = false;
12568     this.loading = false;
12569     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12570     /**
12571     * @event beforeload
12572     * Fires before this node is loaded, return false to cancel
12573     * @param {Node} this This node
12574     */
12575     this.addEvents({'beforeload':true, 'load': true});
12576     /**
12577     * @event load
12578     * Fires when this node is loaded
12579     * @param {Node} this This node
12580     */
12581     /**
12582      * The loader used by this node (defaults to using the tree's defined loader)
12583      * @type TreeLoader
12584      * @property loader
12585      */
12586 };
12587 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12588     expand : function(deep, anim, callback){
12589         if(this.loading){ // if an async load is already running, waiting til it's done
12590             var timer;
12591             var f = function(){
12592                 if(!this.loading){ // done loading
12593                     clearInterval(timer);
12594                     this.expand(deep, anim, callback);
12595                 }
12596             }.createDelegate(this);
12597             timer = setInterval(f, 200);
12598             return;
12599         }
12600         if(!this.loaded){
12601             if(this.fireEvent("beforeload", this) === false){
12602                 return;
12603             }
12604             this.loading = true;
12605             this.ui.beforeLoad(this);
12606             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12607             if(loader){
12608                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12609                 return;
12610             }
12611         }
12612         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12613     },
12614     
12615     /**
12616      * Returns true if this node is currently loading
12617      * @return {Boolean}
12618      */
12619     isLoading : function(){
12620         return this.loading;  
12621     },
12622     
12623     loadComplete : function(deep, anim, callback){
12624         this.loading = false;
12625         this.loaded = true;
12626         this.ui.afterLoad(this);
12627         this.fireEvent("load", this);
12628         this.expand(deep, anim, callback);
12629     },
12630     
12631     /**
12632      * Returns true if this node has been loaded
12633      * @return {Boolean}
12634      */
12635     isLoaded : function(){
12636         return this.loaded;
12637     },
12638     
12639     hasChildNodes : function(){
12640         if(!this.isLeaf() && !this.loaded){
12641             return true;
12642         }else{
12643             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12644         }
12645     },
12646
12647     /**
12648      * Trigger a reload for this node
12649      * @param {Function} callback
12650      */
12651     reload : function(callback){
12652         this.collapse(false, false);
12653         while(this.firstChild){
12654             this.removeChild(this.firstChild);
12655         }
12656         this.childrenRendered = false;
12657         this.loaded = false;
12658         if(this.isHiddenRoot()){
12659             this.expanded = false;
12660         }
12661         this.expand(false, false, callback);
12662     }
12663 });/*
12664  * Based on:
12665  * Ext JS Library 1.1.1
12666  * Copyright(c) 2006-2007, Ext JS, LLC.
12667  *
12668  * Originally Released Under LGPL - original licence link has changed is not relivant.
12669  *
12670  * Fork - LGPL
12671  * <script type="text/javascript">
12672  */
12673  
12674 /**
12675  * @class Roo.tree.TreeNodeUI
12676  * @constructor
12677  * @param {Object} node The node to render
12678  * The TreeNode UI implementation is separate from the
12679  * tree implementation. Unless you are customizing the tree UI,
12680  * you should never have to use this directly.
12681  */
12682 Roo.tree.TreeNodeUI = function(node){
12683     this.node = node;
12684     this.rendered = false;
12685     this.animating = false;
12686     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12687 };
12688
12689 Roo.tree.TreeNodeUI.prototype = {
12690     removeChild : function(node){
12691         if(this.rendered){
12692             this.ctNode.removeChild(node.ui.getEl());
12693         }
12694     },
12695
12696     beforeLoad : function(){
12697          this.addClass("x-tree-node-loading");
12698     },
12699
12700     afterLoad : function(){
12701          this.removeClass("x-tree-node-loading");
12702     },
12703
12704     onTextChange : function(node, text, oldText){
12705         if(this.rendered){
12706             this.textNode.innerHTML = text;
12707         }
12708     },
12709
12710     onDisableChange : function(node, state){
12711         this.disabled = state;
12712         if(state){
12713             this.addClass("x-tree-node-disabled");
12714         }else{
12715             this.removeClass("x-tree-node-disabled");
12716         }
12717     },
12718
12719     onSelectedChange : function(state){
12720         if(state){
12721             this.focus();
12722             this.addClass("x-tree-selected");
12723         }else{
12724             //this.blur();
12725             this.removeClass("x-tree-selected");
12726         }
12727     },
12728
12729     onMove : function(tree, node, oldParent, newParent, index, refNode){
12730         this.childIndent = null;
12731         if(this.rendered){
12732             var targetNode = newParent.ui.getContainer();
12733             if(!targetNode){//target not rendered
12734                 this.holder = document.createElement("div");
12735                 this.holder.appendChild(this.wrap);
12736                 return;
12737             }
12738             var insertBefore = refNode ? refNode.ui.getEl() : null;
12739             if(insertBefore){
12740                 targetNode.insertBefore(this.wrap, insertBefore);
12741             }else{
12742                 targetNode.appendChild(this.wrap);
12743             }
12744             this.node.renderIndent(true);
12745         }
12746     },
12747
12748     addClass : function(cls){
12749         if(this.elNode){
12750             Roo.fly(this.elNode).addClass(cls);
12751         }
12752     },
12753
12754     removeClass : function(cls){
12755         if(this.elNode){
12756             Roo.fly(this.elNode).removeClass(cls);
12757         }
12758     },
12759
12760     remove : function(){
12761         if(this.rendered){
12762             this.holder = document.createElement("div");
12763             this.holder.appendChild(this.wrap);
12764         }
12765     },
12766
12767     fireEvent : function(){
12768         return this.node.fireEvent.apply(this.node, arguments);
12769     },
12770
12771     initEvents : function(){
12772         this.node.on("move", this.onMove, this);
12773         var E = Roo.EventManager;
12774         var a = this.anchor;
12775
12776         var el = Roo.fly(a, '_treeui');
12777
12778         if(Roo.isOpera){ // opera render bug ignores the CSS
12779             el.setStyle("text-decoration", "none");
12780         }
12781
12782         el.on("click", this.onClick, this);
12783         el.on("dblclick", this.onDblClick, this);
12784
12785         if(this.checkbox){
12786             Roo.EventManager.on(this.checkbox,
12787                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12788         }
12789
12790         el.on("contextmenu", this.onContextMenu, this);
12791
12792         var icon = Roo.fly(this.iconNode);
12793         icon.on("click", this.onClick, this);
12794         icon.on("dblclick", this.onDblClick, this);
12795         icon.on("contextmenu", this.onContextMenu, this);
12796         E.on(this.ecNode, "click", this.ecClick, this, true);
12797
12798         if(this.node.disabled){
12799             this.addClass("x-tree-node-disabled");
12800         }
12801         if(this.node.hidden){
12802             this.addClass("x-tree-node-disabled");
12803         }
12804         var ot = this.node.getOwnerTree();
12805         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12806         if(dd && (!this.node.isRoot || ot.rootVisible)){
12807             Roo.dd.Registry.register(this.elNode, {
12808                 node: this.node,
12809                 handles: this.getDDHandles(),
12810                 isHandle: false
12811             });
12812         }
12813     },
12814
12815     getDDHandles : function(){
12816         return [this.iconNode, this.textNode];
12817     },
12818
12819     hide : function(){
12820         if(this.rendered){
12821             this.wrap.style.display = "none";
12822         }
12823     },
12824
12825     show : function(){
12826         if(this.rendered){
12827             this.wrap.style.display = "";
12828         }
12829     },
12830
12831     onContextMenu : function(e){
12832         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12833             e.preventDefault();
12834             this.focus();
12835             this.fireEvent("contextmenu", this.node, e);
12836         }
12837     },
12838
12839     onClick : function(e){
12840         if(this.dropping){
12841             e.stopEvent();
12842             return;
12843         }
12844         if(this.fireEvent("beforeclick", this.node, e) !== false){
12845             if(!this.disabled && this.node.attributes.href){
12846                 this.fireEvent("click", this.node, e);
12847                 return;
12848             }
12849             e.preventDefault();
12850             if(this.disabled){
12851                 return;
12852             }
12853
12854             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12855                 this.node.toggle();
12856             }
12857
12858             this.fireEvent("click", this.node, e);
12859         }else{
12860             e.stopEvent();
12861         }
12862     },
12863
12864     onDblClick : function(e){
12865         e.preventDefault();
12866         if(this.disabled){
12867             return;
12868         }
12869         if(this.checkbox){
12870             this.toggleCheck();
12871         }
12872         if(!this.animating && this.node.hasChildNodes()){
12873             this.node.toggle();
12874         }
12875         this.fireEvent("dblclick", this.node, e);
12876     },
12877
12878     onCheckChange : function(){
12879         var checked = this.checkbox.checked;
12880         this.node.attributes.checked = checked;
12881         this.fireEvent('checkchange', this.node, checked);
12882     },
12883
12884     ecClick : function(e){
12885         if(!this.animating && this.node.hasChildNodes()){
12886             this.node.toggle();
12887         }
12888     },
12889
12890     startDrop : function(){
12891         this.dropping = true;
12892     },
12893
12894     // delayed drop so the click event doesn't get fired on a drop
12895     endDrop : function(){
12896        setTimeout(function(){
12897            this.dropping = false;
12898        }.createDelegate(this), 50);
12899     },
12900
12901     expand : function(){
12902         this.updateExpandIcon();
12903         this.ctNode.style.display = "";
12904     },
12905
12906     focus : function(){
12907         if(!this.node.preventHScroll){
12908             try{this.anchor.focus();
12909             }catch(e){}
12910         }else if(!Roo.isIE){
12911             try{
12912                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12913                 var l = noscroll.scrollLeft;
12914                 this.anchor.focus();
12915                 noscroll.scrollLeft = l;
12916             }catch(e){}
12917         }
12918     },
12919
12920     toggleCheck : function(value){
12921         var cb = this.checkbox;
12922         if(cb){
12923             cb.checked = (value === undefined ? !cb.checked : value);
12924         }
12925     },
12926
12927     blur : function(){
12928         try{
12929             this.anchor.blur();
12930         }catch(e){}
12931     },
12932
12933     animExpand : function(callback){
12934         var ct = Roo.get(this.ctNode);
12935         ct.stopFx();
12936         if(!this.node.hasChildNodes()){
12937             this.updateExpandIcon();
12938             this.ctNode.style.display = "";
12939             Roo.callback(callback);
12940             return;
12941         }
12942         this.animating = true;
12943         this.updateExpandIcon();
12944
12945         ct.slideIn('t', {
12946            callback : function(){
12947                this.animating = false;
12948                Roo.callback(callback);
12949             },
12950             scope: this,
12951             duration: this.node.ownerTree.duration || .25
12952         });
12953     },
12954
12955     highlight : function(){
12956         var tree = this.node.getOwnerTree();
12957         Roo.fly(this.wrap).highlight(
12958             tree.hlColor || "C3DAF9",
12959             {endColor: tree.hlBaseColor}
12960         );
12961     },
12962
12963     collapse : function(){
12964         this.updateExpandIcon();
12965         this.ctNode.style.display = "none";
12966     },
12967
12968     animCollapse : function(callback){
12969         var ct = Roo.get(this.ctNode);
12970         ct.enableDisplayMode('block');
12971         ct.stopFx();
12972
12973         this.animating = true;
12974         this.updateExpandIcon();
12975
12976         ct.slideOut('t', {
12977             callback : function(){
12978                this.animating = false;
12979                Roo.callback(callback);
12980             },
12981             scope: this,
12982             duration: this.node.ownerTree.duration || .25
12983         });
12984     },
12985
12986     getContainer : function(){
12987         return this.ctNode;
12988     },
12989
12990     getEl : function(){
12991         return this.wrap;
12992     },
12993
12994     appendDDGhost : function(ghostNode){
12995         ghostNode.appendChild(this.elNode.cloneNode(true));
12996     },
12997
12998     getDDRepairXY : function(){
12999         return Roo.lib.Dom.getXY(this.iconNode);
13000     },
13001
13002     onRender : function(){
13003         this.render();
13004     },
13005
13006     render : function(bulkRender){
13007         var n = this.node, a = n.attributes;
13008         var targetNode = n.parentNode ?
13009               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13010
13011         if(!this.rendered){
13012             this.rendered = true;
13013
13014             this.renderElements(n, a, targetNode, bulkRender);
13015
13016             if(a.qtip){
13017                if(this.textNode.setAttributeNS){
13018                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13019                    if(a.qtipTitle){
13020                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13021                    }
13022                }else{
13023                    this.textNode.setAttribute("ext:qtip", a.qtip);
13024                    if(a.qtipTitle){
13025                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13026                    }
13027                }
13028             }else if(a.qtipCfg){
13029                 a.qtipCfg.target = Roo.id(this.textNode);
13030                 Roo.QuickTips.register(a.qtipCfg);
13031             }
13032             this.initEvents();
13033             if(!this.node.expanded){
13034                 this.updateExpandIcon();
13035             }
13036         }else{
13037             if(bulkRender === true) {
13038                 targetNode.appendChild(this.wrap);
13039             }
13040         }
13041     },
13042
13043     renderElements : function(n, a, targetNode, bulkRender)
13044     {
13045         // add some indent caching, this helps performance when rendering a large tree
13046         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13047         var t = n.getOwnerTree();
13048         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13049         if (typeof(n.attributes.html) != 'undefined') {
13050             txt = n.attributes.html;
13051         }
13052         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13053         var cb = typeof a.checked == 'boolean';
13054         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13055         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13056             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13057             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13058             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13059             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13060             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13061              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13062                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13063             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13064             "</li>"];
13065
13066         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13067             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13068                                 n.nextSibling.ui.getEl(), buf.join(""));
13069         }else{
13070             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13071         }
13072
13073         this.elNode = this.wrap.childNodes[0];
13074         this.ctNode = this.wrap.childNodes[1];
13075         var cs = this.elNode.childNodes;
13076         this.indentNode = cs[0];
13077         this.ecNode = cs[1];
13078         this.iconNode = cs[2];
13079         var index = 3;
13080         if(cb){
13081             this.checkbox = cs[3];
13082             index++;
13083         }
13084         this.anchor = cs[index];
13085         this.textNode = cs[index].firstChild;
13086     },
13087
13088     getAnchor : function(){
13089         return this.anchor;
13090     },
13091
13092     getTextEl : function(){
13093         return this.textNode;
13094     },
13095
13096     getIconEl : function(){
13097         return this.iconNode;
13098     },
13099
13100     isChecked : function(){
13101         return this.checkbox ? this.checkbox.checked : false;
13102     },
13103
13104     updateExpandIcon : function(){
13105         if(this.rendered){
13106             var n = this.node, c1, c2;
13107             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13108             var hasChild = n.hasChildNodes();
13109             if(hasChild){
13110                 if(n.expanded){
13111                     cls += "-minus";
13112                     c1 = "x-tree-node-collapsed";
13113                     c2 = "x-tree-node-expanded";
13114                 }else{
13115                     cls += "-plus";
13116                     c1 = "x-tree-node-expanded";
13117                     c2 = "x-tree-node-collapsed";
13118                 }
13119                 if(this.wasLeaf){
13120                     this.removeClass("x-tree-node-leaf");
13121                     this.wasLeaf = false;
13122                 }
13123                 if(this.c1 != c1 || this.c2 != c2){
13124                     Roo.fly(this.elNode).replaceClass(c1, c2);
13125                     this.c1 = c1; this.c2 = c2;
13126                 }
13127             }else{
13128                 // this changes non-leafs into leafs if they have no children.
13129                 // it's not very rational behaviour..
13130                 
13131                 if(!this.wasLeaf && this.node.leaf){
13132                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13133                     delete this.c1;
13134                     delete this.c2;
13135                     this.wasLeaf = true;
13136                 }
13137             }
13138             var ecc = "x-tree-ec-icon "+cls;
13139             if(this.ecc != ecc){
13140                 this.ecNode.className = ecc;
13141                 this.ecc = ecc;
13142             }
13143         }
13144     },
13145
13146     getChildIndent : function(){
13147         if(!this.childIndent){
13148             var buf = [];
13149             var p = this.node;
13150             while(p){
13151                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13152                     if(!p.isLast()) {
13153                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13154                     } else {
13155                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13156                     }
13157                 }
13158                 p = p.parentNode;
13159             }
13160             this.childIndent = buf.join("");
13161         }
13162         return this.childIndent;
13163     },
13164
13165     renderIndent : function(){
13166         if(this.rendered){
13167             var indent = "";
13168             var p = this.node.parentNode;
13169             if(p){
13170                 indent = p.ui.getChildIndent();
13171             }
13172             if(this.indentMarkup != indent){ // don't rerender if not required
13173                 this.indentNode.innerHTML = indent;
13174                 this.indentMarkup = indent;
13175             }
13176             this.updateExpandIcon();
13177         }
13178     }
13179 };
13180
13181 Roo.tree.RootTreeNodeUI = function(){
13182     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13183 };
13184 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13185     render : function(){
13186         if(!this.rendered){
13187             var targetNode = this.node.ownerTree.innerCt.dom;
13188             this.node.expanded = true;
13189             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13190             this.wrap = this.ctNode = targetNode.firstChild;
13191         }
13192     },
13193     collapse : function(){
13194     },
13195     expand : function(){
13196     }
13197 });/*
13198  * Based on:
13199  * Ext JS Library 1.1.1
13200  * Copyright(c) 2006-2007, Ext JS, LLC.
13201  *
13202  * Originally Released Under LGPL - original licence link has changed is not relivant.
13203  *
13204  * Fork - LGPL
13205  * <script type="text/javascript">
13206  */
13207 /**
13208  * @class Roo.tree.TreeLoader
13209  * @extends Roo.util.Observable
13210  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13211  * nodes from a specified URL. The response must be a javascript Array definition
13212  * who's elements are node definition objects. eg:
13213  * <pre><code>
13214 {  success : true,
13215    data :      [
13216    
13217     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13218     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13219     ]
13220 }
13221
13222
13223 </code></pre>
13224  * <br><br>
13225  * The old style respose with just an array is still supported, but not recommended.
13226  * <br><br>
13227  *
13228  * A server request is sent, and child nodes are loaded only when a node is expanded.
13229  * The loading node's id is passed to the server under the parameter name "node" to
13230  * enable the server to produce the correct child nodes.
13231  * <br><br>
13232  * To pass extra parameters, an event handler may be attached to the "beforeload"
13233  * event, and the parameters specified in the TreeLoader's baseParams property:
13234  * <pre><code>
13235     myTreeLoader.on("beforeload", function(treeLoader, node) {
13236         this.baseParams.category = node.attributes.category;
13237     }, this);
13238     
13239 </code></pre>
13240  *
13241  * This would pass an HTTP parameter called "category" to the server containing
13242  * the value of the Node's "category" attribute.
13243  * @constructor
13244  * Creates a new Treeloader.
13245  * @param {Object} config A config object containing config properties.
13246  */
13247 Roo.tree.TreeLoader = function(config){
13248     this.baseParams = {};
13249     this.requestMethod = "POST";
13250     Roo.apply(this, config);
13251
13252     this.addEvents({
13253     
13254         /**
13255          * @event beforeload
13256          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13257          * @param {Object} This TreeLoader object.
13258          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13259          * @param {Object} callback The callback function specified in the {@link #load} call.
13260          */
13261         beforeload : true,
13262         /**
13263          * @event load
13264          * Fires when the node has been successfuly loaded.
13265          * @param {Object} This TreeLoader object.
13266          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13267          * @param {Object} response The response object containing the data from the server.
13268          */
13269         load : true,
13270         /**
13271          * @event loadexception
13272          * Fires if the network request failed.
13273          * @param {Object} This TreeLoader object.
13274          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13275          * @param {Object} response The response object containing the data from the server.
13276          */
13277         loadexception : true,
13278         /**
13279          * @event create
13280          * Fires before a node is created, enabling you to return custom Node types 
13281          * @param {Object} This TreeLoader object.
13282          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13283          */
13284         create : true
13285     });
13286
13287     Roo.tree.TreeLoader.superclass.constructor.call(this);
13288 };
13289
13290 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13291     /**
13292     * @cfg {String} dataUrl The URL from which to request a Json string which
13293     * specifies an array of node definition object representing the child nodes
13294     * to be loaded.
13295     */
13296     /**
13297     * @cfg {String} requestMethod either GET or POST
13298     * defaults to POST (due to BC)
13299     * to be loaded.
13300     */
13301     /**
13302     * @cfg {Object} baseParams (optional) An object containing properties which
13303     * specify HTTP parameters to be passed to each request for child nodes.
13304     */
13305     /**
13306     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13307     * created by this loader. If the attributes sent by the server have an attribute in this object,
13308     * they take priority.
13309     */
13310     /**
13311     * @cfg {Object} uiProviders (optional) An object containing properties which
13312     * 
13313     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13314     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13315     * <i>uiProvider</i> attribute of a returned child node is a string rather
13316     * than a reference to a TreeNodeUI implementation, this that string value
13317     * is used as a property name in the uiProviders object. You can define the provider named
13318     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13319     */
13320     uiProviders : {},
13321
13322     /**
13323     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13324     * child nodes before loading.
13325     */
13326     clearOnLoad : true,
13327
13328     /**
13329     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13330     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13331     * Grid query { data : [ .....] }
13332     */
13333     
13334     root : false,
13335      /**
13336     * @cfg {String} queryParam (optional) 
13337     * Name of the query as it will be passed on the querystring (defaults to 'node')
13338     * eg. the request will be ?node=[id]
13339     */
13340     
13341     
13342     queryParam: false,
13343     
13344     /**
13345      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13346      * This is called automatically when a node is expanded, but may be used to reload
13347      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13348      * @param {Roo.tree.TreeNode} node
13349      * @param {Function} callback
13350      */
13351     load : function(node, callback){
13352         if(this.clearOnLoad){
13353             while(node.firstChild){
13354                 node.removeChild(node.firstChild);
13355             }
13356         }
13357         if(node.attributes.children){ // preloaded json children
13358             var cs = node.attributes.children;
13359             for(var i = 0, len = cs.length; i < len; i++){
13360                 node.appendChild(this.createNode(cs[i]));
13361             }
13362             if(typeof callback == "function"){
13363                 callback();
13364             }
13365         }else if(this.dataUrl){
13366             this.requestData(node, callback);
13367         }
13368     },
13369
13370     getParams: function(node){
13371         var buf = [], bp = this.baseParams;
13372         for(var key in bp){
13373             if(typeof bp[key] != "function"){
13374                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13375             }
13376         }
13377         var n = this.queryParam === false ? 'node' : this.queryParam;
13378         buf.push(n + "=", encodeURIComponent(node.id));
13379         return buf.join("");
13380     },
13381
13382     requestData : function(node, callback){
13383         if(this.fireEvent("beforeload", this, node, callback) !== false){
13384             this.transId = Roo.Ajax.request({
13385                 method:this.requestMethod,
13386                 url: this.dataUrl||this.url,
13387                 success: this.handleResponse,
13388                 failure: this.handleFailure,
13389                 scope: this,
13390                 argument: {callback: callback, node: node},
13391                 params: this.getParams(node)
13392             });
13393         }else{
13394             // if the load is cancelled, make sure we notify
13395             // the node that we are done
13396             if(typeof callback == "function"){
13397                 callback();
13398             }
13399         }
13400     },
13401
13402     isLoading : function(){
13403         return this.transId ? true : false;
13404     },
13405
13406     abort : function(){
13407         if(this.isLoading()){
13408             Roo.Ajax.abort(this.transId);
13409         }
13410     },
13411
13412     // private
13413     createNode : function(attr)
13414     {
13415         // apply baseAttrs, nice idea Corey!
13416         if(this.baseAttrs){
13417             Roo.applyIf(attr, this.baseAttrs);
13418         }
13419         if(this.applyLoader !== false){
13420             attr.loader = this;
13421         }
13422         // uiProvider = depreciated..
13423         
13424         if(typeof(attr.uiProvider) == 'string'){
13425            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13426                 /**  eval:var:attr */ eval(attr.uiProvider);
13427         }
13428         if(typeof(this.uiProviders['default']) != 'undefined') {
13429             attr.uiProvider = this.uiProviders['default'];
13430         }
13431         
13432         this.fireEvent('create', this, attr);
13433         
13434         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13435         return(attr.leaf ?
13436                         new Roo.tree.TreeNode(attr) :
13437                         new Roo.tree.AsyncTreeNode(attr));
13438     },
13439
13440     processResponse : function(response, node, callback)
13441     {
13442         var json = response.responseText;
13443         try {
13444             
13445             var o = Roo.decode(json);
13446             
13447             if (this.root === false && typeof(o.success) != undefined) {
13448                 this.root = 'data'; // the default behaviour for list like data..
13449                 }
13450                 
13451             if (this.root !== false &&  !o.success) {
13452                 // it's a failure condition.
13453                 var a = response.argument;
13454                 this.fireEvent("loadexception", this, a.node, response);
13455                 Roo.log("Load failed - should have a handler really");
13456                 return;
13457             }
13458             
13459             
13460             
13461             if (this.root !== false) {
13462                  o = o[this.root];
13463             }
13464             
13465             for(var i = 0, len = o.length; i < len; i++){
13466                 var n = this.createNode(o[i]);
13467                 if(n){
13468                     node.appendChild(n);
13469                 }
13470             }
13471             if(typeof callback == "function"){
13472                 callback(this, node);
13473             }
13474         }catch(e){
13475             this.handleFailure(response);
13476         }
13477     },
13478
13479     handleResponse : function(response){
13480         this.transId = false;
13481         var a = response.argument;
13482         this.processResponse(response, a.node, a.callback);
13483         this.fireEvent("load", this, a.node, response);
13484     },
13485
13486     handleFailure : function(response)
13487     {
13488         // should handle failure better..
13489         this.transId = false;
13490         var a = response.argument;
13491         this.fireEvent("loadexception", this, a.node, response);
13492         if(typeof a.callback == "function"){
13493             a.callback(this, a.node);
13494         }
13495     }
13496 });/*
13497  * Based on:
13498  * Ext JS Library 1.1.1
13499  * Copyright(c) 2006-2007, Ext JS, LLC.
13500  *
13501  * Originally Released Under LGPL - original licence link has changed is not relivant.
13502  *
13503  * Fork - LGPL
13504  * <script type="text/javascript">
13505  */
13506
13507 /**
13508 * @class Roo.tree.TreeFilter
13509 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13510 * @param {TreePanel} tree
13511 * @param {Object} config (optional)
13512  */
13513 Roo.tree.TreeFilter = function(tree, config){
13514     this.tree = tree;
13515     this.filtered = {};
13516     Roo.apply(this, config);
13517 };
13518
13519 Roo.tree.TreeFilter.prototype = {
13520     clearBlank:false,
13521     reverse:false,
13522     autoClear:false,
13523     remove:false,
13524
13525      /**
13526      * Filter the data by a specific attribute.
13527      * @param {String/RegExp} value Either string that the attribute value
13528      * should start with or a RegExp to test against the attribute
13529      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13530      * @param {TreeNode} startNode (optional) The node to start the filter at.
13531      */
13532     filter : function(value, attr, startNode){
13533         attr = attr || "text";
13534         var f;
13535         if(typeof value == "string"){
13536             var vlen = value.length;
13537             // auto clear empty filter
13538             if(vlen == 0 && this.clearBlank){
13539                 this.clear();
13540                 return;
13541             }
13542             value = value.toLowerCase();
13543             f = function(n){
13544                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13545             };
13546         }else if(value.exec){ // regex?
13547             f = function(n){
13548                 return value.test(n.attributes[attr]);
13549             };
13550         }else{
13551             throw 'Illegal filter type, must be string or regex';
13552         }
13553         this.filterBy(f, null, startNode);
13554         },
13555
13556     /**
13557      * Filter by a function. The passed function will be called with each
13558      * node in the tree (or from the startNode). If the function returns true, the node is kept
13559      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13560      * @param {Function} fn The filter function
13561      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13562      */
13563     filterBy : function(fn, scope, startNode){
13564         startNode = startNode || this.tree.root;
13565         if(this.autoClear){
13566             this.clear();
13567         }
13568         var af = this.filtered, rv = this.reverse;
13569         var f = function(n){
13570             if(n == startNode){
13571                 return true;
13572             }
13573             if(af[n.id]){
13574                 return false;
13575             }
13576             var m = fn.call(scope || n, n);
13577             if(!m || rv){
13578                 af[n.id] = n;
13579                 n.ui.hide();
13580                 return false;
13581             }
13582             return true;
13583         };
13584         startNode.cascade(f);
13585         if(this.remove){
13586            for(var id in af){
13587                if(typeof id != "function"){
13588                    var n = af[id];
13589                    if(n && n.parentNode){
13590                        n.parentNode.removeChild(n);
13591                    }
13592                }
13593            }
13594         }
13595     },
13596
13597     /**
13598      * Clears the current filter. Note: with the "remove" option
13599      * set a filter cannot be cleared.
13600      */
13601     clear : function(){
13602         var t = this.tree;
13603         var af = this.filtered;
13604         for(var id in af){
13605             if(typeof id != "function"){
13606                 var n = af[id];
13607                 if(n){
13608                     n.ui.show();
13609                 }
13610             }
13611         }
13612         this.filtered = {};
13613     }
13614 };
13615 /*
13616  * Based on:
13617  * Ext JS Library 1.1.1
13618  * Copyright(c) 2006-2007, Ext JS, LLC.
13619  *
13620  * Originally Released Under LGPL - original licence link has changed is not relivant.
13621  *
13622  * Fork - LGPL
13623  * <script type="text/javascript">
13624  */
13625  
13626
13627 /**
13628  * @class Roo.tree.TreeSorter
13629  * Provides sorting of nodes in a TreePanel
13630  * 
13631  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13632  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13633  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13634  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13635  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13636  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13637  * @constructor
13638  * @param {TreePanel} tree
13639  * @param {Object} config
13640  */
13641 Roo.tree.TreeSorter = function(tree, config){
13642     Roo.apply(this, config);
13643     tree.on("beforechildrenrendered", this.doSort, this);
13644     tree.on("append", this.updateSort, this);
13645     tree.on("insert", this.updateSort, this);
13646     
13647     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13648     var p = this.property || "text";
13649     var sortType = this.sortType;
13650     var fs = this.folderSort;
13651     var cs = this.caseSensitive === true;
13652     var leafAttr = this.leafAttr || 'leaf';
13653
13654     this.sortFn = function(n1, n2){
13655         if(fs){
13656             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13657                 return 1;
13658             }
13659             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13660                 return -1;
13661             }
13662         }
13663         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13664         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13665         if(v1 < v2){
13666                         return dsc ? +1 : -1;
13667                 }else if(v1 > v2){
13668                         return dsc ? -1 : +1;
13669         }else{
13670                 return 0;
13671         }
13672     };
13673 };
13674
13675 Roo.tree.TreeSorter.prototype = {
13676     doSort : function(node){
13677         node.sort(this.sortFn);
13678     },
13679     
13680     compareNodes : function(n1, n2){
13681         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13682     },
13683     
13684     updateSort : function(tree, node){
13685         if(node.childrenRendered){
13686             this.doSort.defer(1, this, [node]);
13687         }
13688     }
13689 };/*
13690  * Based on:
13691  * Ext JS Library 1.1.1
13692  * Copyright(c) 2006-2007, Ext JS, LLC.
13693  *
13694  * Originally Released Under LGPL - original licence link has changed is not relivant.
13695  *
13696  * Fork - LGPL
13697  * <script type="text/javascript">
13698  */
13699
13700 if(Roo.dd.DropZone){
13701     
13702 Roo.tree.TreeDropZone = function(tree, config){
13703     this.allowParentInsert = false;
13704     this.allowContainerDrop = false;
13705     this.appendOnly = false;
13706     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13707     this.tree = tree;
13708     this.lastInsertClass = "x-tree-no-status";
13709     this.dragOverData = {};
13710 };
13711
13712 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13713     ddGroup : "TreeDD",
13714     scroll:  true,
13715     
13716     expandDelay : 1000,
13717     
13718     expandNode : function(node){
13719         if(node.hasChildNodes() && !node.isExpanded()){
13720             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13721         }
13722     },
13723     
13724     queueExpand : function(node){
13725         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13726     },
13727     
13728     cancelExpand : function(){
13729         if(this.expandProcId){
13730             clearTimeout(this.expandProcId);
13731             this.expandProcId = false;
13732         }
13733     },
13734     
13735     isValidDropPoint : function(n, pt, dd, e, data){
13736         if(!n || !data){ return false; }
13737         var targetNode = n.node;
13738         var dropNode = data.node;
13739         // default drop rules
13740         if(!(targetNode && targetNode.isTarget && pt)){
13741             return false;
13742         }
13743         if(pt == "append" && targetNode.allowChildren === false){
13744             return false;
13745         }
13746         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13747             return false;
13748         }
13749         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13750             return false;
13751         }
13752         // reuse the object
13753         var overEvent = this.dragOverData;
13754         overEvent.tree = this.tree;
13755         overEvent.target = targetNode;
13756         overEvent.data = data;
13757         overEvent.point = pt;
13758         overEvent.source = dd;
13759         overEvent.rawEvent = e;
13760         overEvent.dropNode = dropNode;
13761         overEvent.cancel = false;  
13762         var result = this.tree.fireEvent("nodedragover", overEvent);
13763         return overEvent.cancel === false && result !== false;
13764     },
13765     
13766     getDropPoint : function(e, n, dd)
13767     {
13768         var tn = n.node;
13769         if(tn.isRoot){
13770             return tn.allowChildren !== false ? "append" : false; // always append for root
13771         }
13772         var dragEl = n.ddel;
13773         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13774         var y = Roo.lib.Event.getPageY(e);
13775         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13776         
13777         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13778         var noAppend = tn.allowChildren === false;
13779         if(this.appendOnly || tn.parentNode.allowChildren === false){
13780             return noAppend ? false : "append";
13781         }
13782         var noBelow = false;
13783         if(!this.allowParentInsert){
13784             noBelow = tn.hasChildNodes() && tn.isExpanded();
13785         }
13786         var q = (b - t) / (noAppend ? 2 : 3);
13787         if(y >= t && y < (t + q)){
13788             return "above";
13789         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13790             return "below";
13791         }else{
13792             return "append";
13793         }
13794     },
13795     
13796     onNodeEnter : function(n, dd, e, data)
13797     {
13798         this.cancelExpand();
13799     },
13800     
13801     onNodeOver : function(n, dd, e, data)
13802     {
13803        
13804         var pt = this.getDropPoint(e, n, dd);
13805         var node = n.node;
13806         
13807         // auto node expand check
13808         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13809             this.queueExpand(node);
13810         }else if(pt != "append"){
13811             this.cancelExpand();
13812         }
13813         
13814         // set the insert point style on the target node
13815         var returnCls = this.dropNotAllowed;
13816         if(this.isValidDropPoint(n, pt, dd, e, data)){
13817            if(pt){
13818                var el = n.ddel;
13819                var cls;
13820                if(pt == "above"){
13821                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13822                    cls = "x-tree-drag-insert-above";
13823                }else if(pt == "below"){
13824                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13825                    cls = "x-tree-drag-insert-below";
13826                }else{
13827                    returnCls = "x-tree-drop-ok-append";
13828                    cls = "x-tree-drag-append";
13829                }
13830                if(this.lastInsertClass != cls){
13831                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13832                    this.lastInsertClass = cls;
13833                }
13834            }
13835        }
13836        return returnCls;
13837     },
13838     
13839     onNodeOut : function(n, dd, e, data){
13840         
13841         this.cancelExpand();
13842         this.removeDropIndicators(n);
13843     },
13844     
13845     onNodeDrop : function(n, dd, e, data){
13846         var point = this.getDropPoint(e, n, dd);
13847         var targetNode = n.node;
13848         targetNode.ui.startDrop();
13849         if(!this.isValidDropPoint(n, point, dd, e, data)){
13850             targetNode.ui.endDrop();
13851             return false;
13852         }
13853         // first try to find the drop node
13854         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13855         var dropEvent = {
13856             tree : this.tree,
13857             target: targetNode,
13858             data: data,
13859             point: point,
13860             source: dd,
13861             rawEvent: e,
13862             dropNode: dropNode,
13863             cancel: !dropNode   
13864         };
13865         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13866         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13867             targetNode.ui.endDrop();
13868             return false;
13869         }
13870         // allow target changing
13871         targetNode = dropEvent.target;
13872         if(point == "append" && !targetNode.isExpanded()){
13873             targetNode.expand(false, null, function(){
13874                 this.completeDrop(dropEvent);
13875             }.createDelegate(this));
13876         }else{
13877             this.completeDrop(dropEvent);
13878         }
13879         return true;
13880     },
13881     
13882     completeDrop : function(de){
13883         var ns = de.dropNode, p = de.point, t = de.target;
13884         if(!(ns instanceof Array)){
13885             ns = [ns];
13886         }
13887         var n;
13888         for(var i = 0, len = ns.length; i < len; i++){
13889             n = ns[i];
13890             if(p == "above"){
13891                 t.parentNode.insertBefore(n, t);
13892             }else if(p == "below"){
13893                 t.parentNode.insertBefore(n, t.nextSibling);
13894             }else{
13895                 t.appendChild(n);
13896             }
13897         }
13898         n.ui.focus();
13899         if(this.tree.hlDrop){
13900             n.ui.highlight();
13901         }
13902         t.ui.endDrop();
13903         this.tree.fireEvent("nodedrop", de);
13904     },
13905     
13906     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13907         if(this.tree.hlDrop){
13908             dropNode.ui.focus();
13909             dropNode.ui.highlight();
13910         }
13911         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13912     },
13913     
13914     getTree : function(){
13915         return this.tree;
13916     },
13917     
13918     removeDropIndicators : function(n){
13919         if(n && n.ddel){
13920             var el = n.ddel;
13921             Roo.fly(el).removeClass([
13922                     "x-tree-drag-insert-above",
13923                     "x-tree-drag-insert-below",
13924                     "x-tree-drag-append"]);
13925             this.lastInsertClass = "_noclass";
13926         }
13927     },
13928     
13929     beforeDragDrop : function(target, e, id){
13930         this.cancelExpand();
13931         return true;
13932     },
13933     
13934     afterRepair : function(data){
13935         if(data && Roo.enableFx){
13936             data.node.ui.highlight();
13937         }
13938         this.hideProxy();
13939     } 
13940     
13941 });
13942
13943 }
13944 /*
13945  * Based on:
13946  * Ext JS Library 1.1.1
13947  * Copyright(c) 2006-2007, Ext JS, LLC.
13948  *
13949  * Originally Released Under LGPL - original licence link has changed is not relivant.
13950  *
13951  * Fork - LGPL
13952  * <script type="text/javascript">
13953  */
13954  
13955
13956 if(Roo.dd.DragZone){
13957 Roo.tree.TreeDragZone = function(tree, config){
13958     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13959     this.tree = tree;
13960 };
13961
13962 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13963     ddGroup : "TreeDD",
13964    
13965     onBeforeDrag : function(data, e){
13966         var n = data.node;
13967         return n && n.draggable && !n.disabled;
13968     },
13969      
13970     
13971     onInitDrag : function(e){
13972         var data = this.dragData;
13973         this.tree.getSelectionModel().select(data.node);
13974         this.proxy.update("");
13975         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13976         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13977     },
13978     
13979     getRepairXY : function(e, data){
13980         return data.node.ui.getDDRepairXY();
13981     },
13982     
13983     onEndDrag : function(data, e){
13984         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13985         
13986         
13987     },
13988     
13989     onValidDrop : function(dd, e, id){
13990         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13991         this.hideProxy();
13992     },
13993     
13994     beforeInvalidDrop : function(e, id){
13995         // this scrolls the original position back into view
13996         var sm = this.tree.getSelectionModel();
13997         sm.clearSelections();
13998         sm.select(this.dragData.node);
13999     }
14000 });
14001 }/*
14002  * Based on:
14003  * Ext JS Library 1.1.1
14004  * Copyright(c) 2006-2007, Ext JS, LLC.
14005  *
14006  * Originally Released Under LGPL - original licence link has changed is not relivant.
14007  *
14008  * Fork - LGPL
14009  * <script type="text/javascript">
14010  */
14011 /**
14012  * @class Roo.tree.TreeEditor
14013  * @extends Roo.Editor
14014  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14015  * as the editor field.
14016  * @constructor
14017  * @param {Object} config (used to be the tree panel.)
14018  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14019  * 
14020  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14021  * @cfg {Roo.form.TextField} field [required] The field configuration
14022  *
14023  * 
14024  */
14025 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14026     var tree = config;
14027     var field;
14028     if (oldconfig) { // old style..
14029         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14030     } else {
14031         // new style..
14032         tree = config.tree;
14033         config.field = config.field  || {};
14034         config.field.xtype = 'TextField';
14035         field = Roo.factory(config.field, Roo.form);
14036     }
14037     config = config || {};
14038     
14039     
14040     this.addEvents({
14041         /**
14042          * @event beforenodeedit
14043          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14044          * false from the handler of this event.
14045          * @param {Editor} this
14046          * @param {Roo.tree.Node} node 
14047          */
14048         "beforenodeedit" : true
14049     });
14050     
14051     //Roo.log(config);
14052     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14053
14054     this.tree = tree;
14055
14056     tree.on('beforeclick', this.beforeNodeClick, this);
14057     tree.getTreeEl().on('mousedown', this.hide, this);
14058     this.on('complete', this.updateNode, this);
14059     this.on('beforestartedit', this.fitToTree, this);
14060     this.on('startedit', this.bindScroll, this, {delay:10});
14061     this.on('specialkey', this.onSpecialKey, this);
14062 };
14063
14064 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14065     /**
14066      * @cfg {String} alignment
14067      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14068      */
14069     alignment: "l-l",
14070     // inherit
14071     autoSize: false,
14072     /**
14073      * @cfg {Boolean} hideEl
14074      * True to hide the bound element while the editor is displayed (defaults to false)
14075      */
14076     hideEl : false,
14077     /**
14078      * @cfg {String} cls
14079      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14080      */
14081     cls: "x-small-editor x-tree-editor",
14082     /**
14083      * @cfg {Boolean} shim
14084      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14085      */
14086     shim:false,
14087     // inherit
14088     shadow:"frame",
14089     /**
14090      * @cfg {Number} maxWidth
14091      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14092      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14093      * scroll and client offsets into account prior to each edit.
14094      */
14095     maxWidth: 250,
14096
14097     editDelay : 350,
14098
14099     // private
14100     fitToTree : function(ed, el){
14101         var td = this.tree.getTreeEl().dom, nd = el.dom;
14102         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14103             td.scrollLeft = nd.offsetLeft;
14104         }
14105         var w = Math.min(
14106                 this.maxWidth,
14107                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14108         this.setSize(w, '');
14109         
14110         return this.fireEvent('beforenodeedit', this, this.editNode);
14111         
14112     },
14113
14114     // private
14115     triggerEdit : function(node){
14116         this.completeEdit();
14117         this.editNode = node;
14118         this.startEdit(node.ui.textNode, node.text);
14119     },
14120
14121     // private
14122     bindScroll : function(){
14123         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14124     },
14125
14126     // private
14127     beforeNodeClick : function(node, e){
14128         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14129         this.lastClick = new Date();
14130         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14131             e.stopEvent();
14132             this.triggerEdit(node);
14133             return false;
14134         }
14135         return true;
14136     },
14137
14138     // private
14139     updateNode : function(ed, value){
14140         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14141         this.editNode.setText(value);
14142     },
14143
14144     // private
14145     onHide : function(){
14146         Roo.tree.TreeEditor.superclass.onHide.call(this);
14147         if(this.editNode){
14148             this.editNode.ui.focus();
14149         }
14150     },
14151
14152     // private
14153     onSpecialKey : function(field, e){
14154         var k = e.getKey();
14155         if(k == e.ESC){
14156             e.stopEvent();
14157             this.cancelEdit();
14158         }else if(k == e.ENTER && !e.hasModifier()){
14159             e.stopEvent();
14160             this.completeEdit();
14161         }
14162     }
14163 });//<Script type="text/javascript">
14164 /*
14165  * Based on:
14166  * Ext JS Library 1.1.1
14167  * Copyright(c) 2006-2007, Ext JS, LLC.
14168  *
14169  * Originally Released Under LGPL - original licence link has changed is not relivant.
14170  *
14171  * Fork - LGPL
14172  * <script type="text/javascript">
14173  */
14174  
14175 /**
14176  * Not documented??? - probably should be...
14177  */
14178
14179 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14180     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14181     
14182     renderElements : function(n, a, targetNode, bulkRender){
14183         //consel.log("renderElements?");
14184         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14185
14186         var t = n.getOwnerTree();
14187         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14188         
14189         var cols = t.columns;
14190         var bw = t.borderWidth;
14191         var c = cols[0];
14192         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14193          var cb = typeof a.checked == "boolean";
14194         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14195         var colcls = 'x-t-' + tid + '-c0';
14196         var buf = [
14197             '<li class="x-tree-node">',
14198             
14199                 
14200                 '<div class="x-tree-node-el ', a.cls,'">',
14201                     // extran...
14202                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14203                 
14204                 
14205                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14206                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14207                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14208                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14209                            (a.iconCls ? ' '+a.iconCls : ''),
14210                            '" unselectable="on" />',
14211                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14212                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14213                              
14214                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14215                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14216                             '<span unselectable="on" qtip="' + tx + '">',
14217                              tx,
14218                              '</span></a>' ,
14219                     '</div>',
14220                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14221                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14222                  ];
14223         for(var i = 1, len = cols.length; i < len; i++){
14224             c = cols[i];
14225             colcls = 'x-t-' + tid + '-c' +i;
14226             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14227             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14228                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14229                       "</div>");
14230          }
14231          
14232          buf.push(
14233             '</a>',
14234             '<div class="x-clear"></div></div>',
14235             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14236             "</li>");
14237         
14238         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14239             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14240                                 n.nextSibling.ui.getEl(), buf.join(""));
14241         }else{
14242             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14243         }
14244         var el = this.wrap.firstChild;
14245         this.elRow = el;
14246         this.elNode = el.firstChild;
14247         this.ranchor = el.childNodes[1];
14248         this.ctNode = this.wrap.childNodes[1];
14249         var cs = el.firstChild.childNodes;
14250         this.indentNode = cs[0];
14251         this.ecNode = cs[1];
14252         this.iconNode = cs[2];
14253         var index = 3;
14254         if(cb){
14255             this.checkbox = cs[3];
14256             index++;
14257         }
14258         this.anchor = cs[index];
14259         
14260         this.textNode = cs[index].firstChild;
14261         
14262         //el.on("click", this.onClick, this);
14263         //el.on("dblclick", this.onDblClick, this);
14264         
14265         
14266        // console.log(this);
14267     },
14268     initEvents : function(){
14269         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14270         
14271             
14272         var a = this.ranchor;
14273
14274         var el = Roo.get(a);
14275
14276         if(Roo.isOpera){ // opera render bug ignores the CSS
14277             el.setStyle("text-decoration", "none");
14278         }
14279
14280         el.on("click", this.onClick, this);
14281         el.on("dblclick", this.onDblClick, this);
14282         el.on("contextmenu", this.onContextMenu, this);
14283         
14284     },
14285     
14286     /*onSelectedChange : function(state){
14287         if(state){
14288             this.focus();
14289             this.addClass("x-tree-selected");
14290         }else{
14291             //this.blur();
14292             this.removeClass("x-tree-selected");
14293         }
14294     },*/
14295     addClass : function(cls){
14296         if(this.elRow){
14297             Roo.fly(this.elRow).addClass(cls);
14298         }
14299         
14300     },
14301     
14302     
14303     removeClass : function(cls){
14304         if(this.elRow){
14305             Roo.fly(this.elRow).removeClass(cls);
14306         }
14307     }
14308
14309     
14310     
14311 });//<Script type="text/javascript">
14312
14313 /*
14314  * Based on:
14315  * Ext JS Library 1.1.1
14316  * Copyright(c) 2006-2007, Ext JS, LLC.
14317  *
14318  * Originally Released Under LGPL - original licence link has changed is not relivant.
14319  *
14320  * Fork - LGPL
14321  * <script type="text/javascript">
14322  */
14323  
14324
14325 /**
14326  * @class Roo.tree.ColumnTree
14327  * @extends Roo.tree.TreePanel
14328  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14329  * @cfg {int} borderWidth  compined right/left border allowance
14330  * @constructor
14331  * @param {String/HTMLElement/Element} el The container element
14332  * @param {Object} config
14333  */
14334 Roo.tree.ColumnTree =  function(el, config)
14335 {
14336    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14337    this.addEvents({
14338         /**
14339         * @event resize
14340         * Fire this event on a container when it resizes
14341         * @param {int} w Width
14342         * @param {int} h Height
14343         */
14344        "resize" : true
14345     });
14346     this.on('resize', this.onResize, this);
14347 };
14348
14349 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14350     //lines:false,
14351     
14352     
14353     borderWidth: Roo.isBorderBox ? 0 : 2, 
14354     headEls : false,
14355     
14356     render : function(){
14357         // add the header.....
14358        
14359         Roo.tree.ColumnTree.superclass.render.apply(this);
14360         
14361         this.el.addClass('x-column-tree');
14362         
14363         this.headers = this.el.createChild(
14364             {cls:'x-tree-headers'},this.innerCt.dom);
14365    
14366         var cols = this.columns, c;
14367         var totalWidth = 0;
14368         this.headEls = [];
14369         var  len = cols.length;
14370         for(var i = 0; i < len; i++){
14371              c = cols[i];
14372              totalWidth += c.width;
14373             this.headEls.push(this.headers.createChild({
14374                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14375                  cn: {
14376                      cls:'x-tree-hd-text',
14377                      html: c.header
14378                  },
14379                  style:'width:'+(c.width-this.borderWidth)+'px;'
14380              }));
14381         }
14382         this.headers.createChild({cls:'x-clear'});
14383         // prevent floats from wrapping when clipped
14384         this.headers.setWidth(totalWidth);
14385         //this.innerCt.setWidth(totalWidth);
14386         this.innerCt.setStyle({ overflow: 'auto' });
14387         this.onResize(this.width, this.height);
14388              
14389         
14390     },
14391     onResize : function(w,h)
14392     {
14393         this.height = h;
14394         this.width = w;
14395         // resize cols..
14396         this.innerCt.setWidth(this.width);
14397         this.innerCt.setHeight(this.height-20);
14398         
14399         // headers...
14400         var cols = this.columns, c;
14401         var totalWidth = 0;
14402         var expEl = false;
14403         var len = cols.length;
14404         for(var i = 0; i < len; i++){
14405             c = cols[i];
14406             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14407                 // it's the expander..
14408                 expEl  = this.headEls[i];
14409                 continue;
14410             }
14411             totalWidth += c.width;
14412             
14413         }
14414         if (expEl) {
14415             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14416         }
14417         this.headers.setWidth(w-20);
14418
14419         
14420         
14421         
14422     }
14423 });
14424 /*
14425  * Based on:
14426  * Ext JS Library 1.1.1
14427  * Copyright(c) 2006-2007, Ext JS, LLC.
14428  *
14429  * Originally Released Under LGPL - original licence link has changed is not relivant.
14430  *
14431  * Fork - LGPL
14432  * <script type="text/javascript">
14433  */
14434  
14435 /**
14436  * @class Roo.menu.Menu
14437  * @extends Roo.util.Observable
14438  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14439  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14440  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14441  * @constructor
14442  * Creates a new Menu
14443  * @param {Object} config Configuration options
14444  */
14445 Roo.menu.Menu = function(config){
14446     
14447     Roo.menu.Menu.superclass.constructor.call(this, config);
14448     
14449     this.id = this.id || Roo.id();
14450     this.addEvents({
14451         /**
14452          * @event beforeshow
14453          * Fires before this menu is displayed
14454          * @param {Roo.menu.Menu} this
14455          */
14456         beforeshow : true,
14457         /**
14458          * @event beforehide
14459          * Fires before this menu is hidden
14460          * @param {Roo.menu.Menu} this
14461          */
14462         beforehide : true,
14463         /**
14464          * @event show
14465          * Fires after this menu is displayed
14466          * @param {Roo.menu.Menu} this
14467          */
14468         show : true,
14469         /**
14470          * @event hide
14471          * Fires after this menu is hidden
14472          * @param {Roo.menu.Menu} this
14473          */
14474         hide : true,
14475         /**
14476          * @event click
14477          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14478          * @param {Roo.menu.Menu} this
14479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14480          * @param {Roo.EventObject} e
14481          */
14482         click : true,
14483         /**
14484          * @event mouseover
14485          * Fires when the mouse is hovering over this menu
14486          * @param {Roo.menu.Menu} this
14487          * @param {Roo.EventObject} e
14488          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14489          */
14490         mouseover : true,
14491         /**
14492          * @event mouseout
14493          * Fires when the mouse exits this menu
14494          * @param {Roo.menu.Menu} this
14495          * @param {Roo.EventObject} e
14496          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14497          */
14498         mouseout : true,
14499         /**
14500          * @event itemclick
14501          * Fires when a menu item contained in this menu is clicked
14502          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14503          * @param {Roo.EventObject} e
14504          */
14505         itemclick: true
14506     });
14507     if (this.registerMenu) {
14508         Roo.menu.MenuMgr.register(this);
14509     }
14510     
14511     var mis = this.items;
14512     this.items = new Roo.util.MixedCollection();
14513     if(mis){
14514         this.add.apply(this, mis);
14515     }
14516 };
14517
14518 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14519     /**
14520      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14521      */
14522     minWidth : 120,
14523     /**
14524      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14525      * for bottom-right shadow (defaults to "sides")
14526      */
14527     shadow : "sides",
14528     /**
14529      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14530      * this menu (defaults to "tl-tr?")
14531      */
14532     subMenuAlign : "tl-tr?",
14533     /**
14534      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14535      * relative to its element of origin (defaults to "tl-bl?")
14536      */
14537     defaultAlign : "tl-bl?",
14538     /**
14539      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14540      */
14541     allowOtherMenus : false,
14542     /**
14543      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14544      */
14545     registerMenu : true,
14546
14547     hidden:true,
14548
14549     // private
14550     render : function(){
14551         if(this.el){
14552             return;
14553         }
14554         var el = this.el = new Roo.Layer({
14555             cls: "x-menu",
14556             shadow:this.shadow,
14557             constrain: false,
14558             parentEl: this.parentEl || document.body,
14559             zindex:15000
14560         });
14561
14562         this.keyNav = new Roo.menu.MenuNav(this);
14563
14564         if(this.plain){
14565             el.addClass("x-menu-plain");
14566         }
14567         if(this.cls){
14568             el.addClass(this.cls);
14569         }
14570         // generic focus element
14571         this.focusEl = el.createChild({
14572             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14573         });
14574         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14575         //disabling touch- as it's causing issues ..
14576         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14577         ul.on('click'   , this.onClick, this);
14578         
14579         
14580         ul.on("mouseover", this.onMouseOver, this);
14581         ul.on("mouseout", this.onMouseOut, this);
14582         this.items.each(function(item){
14583             if (item.hidden) {
14584                 return;
14585             }
14586             
14587             var li = document.createElement("li");
14588             li.className = "x-menu-list-item";
14589             ul.dom.appendChild(li);
14590             item.render(li, this);
14591         }, this);
14592         this.ul = ul;
14593         this.autoWidth();
14594     },
14595
14596     // private
14597     autoWidth : function(){
14598         var el = this.el, ul = this.ul;
14599         if(!el){
14600             return;
14601         }
14602         var w = this.width;
14603         if(w){
14604             el.setWidth(w);
14605         }else if(Roo.isIE){
14606             el.setWidth(this.minWidth);
14607             var t = el.dom.offsetWidth; // force recalc
14608             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14609         }
14610     },
14611
14612     // private
14613     delayAutoWidth : function(){
14614         if(this.rendered){
14615             if(!this.awTask){
14616                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14617             }
14618             this.awTask.delay(20);
14619         }
14620     },
14621
14622     // private
14623     findTargetItem : function(e){
14624         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14625         if(t && t.menuItemId){
14626             return this.items.get(t.menuItemId);
14627         }
14628     },
14629
14630     // private
14631     onClick : function(e){
14632         Roo.log("menu.onClick");
14633         var t = this.findTargetItem(e);
14634         if(!t){
14635             return;
14636         }
14637         Roo.log(e);
14638         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14639             if(t == this.activeItem && t.shouldDeactivate(e)){
14640                 this.activeItem.deactivate();
14641                 delete this.activeItem;
14642                 return;
14643             }
14644             if(t.canActivate){
14645                 this.setActiveItem(t, true);
14646             }
14647             return;
14648             
14649             
14650         }
14651         
14652         t.onClick(e);
14653         this.fireEvent("click", this, t, e);
14654     },
14655
14656     // private
14657     setActiveItem : function(item, autoExpand){
14658         if(item != this.activeItem){
14659             if(this.activeItem){
14660                 this.activeItem.deactivate();
14661             }
14662             this.activeItem = item;
14663             item.activate(autoExpand);
14664         }else if(autoExpand){
14665             item.expandMenu();
14666         }
14667     },
14668
14669     // private
14670     tryActivate : function(start, step){
14671         var items = this.items;
14672         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14673             var item = items.get(i);
14674             if(!item.disabled && item.canActivate){
14675                 this.setActiveItem(item, false);
14676                 return item;
14677             }
14678         }
14679         return false;
14680     },
14681
14682     // private
14683     onMouseOver : function(e){
14684         var t;
14685         if(t = this.findTargetItem(e)){
14686             if(t.canActivate && !t.disabled){
14687                 this.setActiveItem(t, true);
14688             }
14689         }
14690         this.fireEvent("mouseover", this, e, t);
14691     },
14692
14693     // private
14694     onMouseOut : function(e){
14695         var t;
14696         if(t = this.findTargetItem(e)){
14697             if(t == this.activeItem && t.shouldDeactivate(e)){
14698                 this.activeItem.deactivate();
14699                 delete this.activeItem;
14700             }
14701         }
14702         this.fireEvent("mouseout", this, e, t);
14703     },
14704
14705     /**
14706      * Read-only.  Returns true if the menu is currently displayed, else false.
14707      * @type Boolean
14708      */
14709     isVisible : function(){
14710         return this.el && !this.hidden;
14711     },
14712
14713     /**
14714      * Displays this menu relative to another element
14715      * @param {String/HTMLElement/Roo.Element} element The element to align to
14716      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14717      * the element (defaults to this.defaultAlign)
14718      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14719      */
14720     show : function(el, pos, parentMenu){
14721         this.parentMenu = parentMenu;
14722         if(!this.el){
14723             this.render();
14724         }
14725         this.fireEvent("beforeshow", this);
14726         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14727     },
14728
14729     /**
14730      * Displays this menu at a specific xy position
14731      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14732      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14733      */
14734     showAt : function(xy, parentMenu, /* private: */_e){
14735         this.parentMenu = parentMenu;
14736         if(!this.el){
14737             this.render();
14738         }
14739         if(_e !== false){
14740             this.fireEvent("beforeshow", this);
14741             xy = this.el.adjustForConstraints(xy);
14742         }
14743         this.el.setXY(xy);
14744         this.el.show();
14745         this.hidden = false;
14746         this.focus();
14747         this.fireEvent("show", this);
14748     },
14749
14750     focus : function(){
14751         if(!this.hidden){
14752             this.doFocus.defer(50, this);
14753         }
14754     },
14755
14756     doFocus : function(){
14757         if(!this.hidden){
14758             this.focusEl.focus();
14759         }
14760     },
14761
14762     /**
14763      * Hides this menu and optionally all parent menus
14764      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14765      */
14766     hide : function(deep){
14767         if(this.el && this.isVisible()){
14768             this.fireEvent("beforehide", this);
14769             if(this.activeItem){
14770                 this.activeItem.deactivate();
14771                 this.activeItem = null;
14772             }
14773             this.el.hide();
14774             this.hidden = true;
14775             this.fireEvent("hide", this);
14776         }
14777         if(deep === true && this.parentMenu){
14778             this.parentMenu.hide(true);
14779         }
14780     },
14781
14782     /**
14783      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14784      * Any of the following are valid:
14785      * <ul>
14786      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14787      * <li>An HTMLElement object which will be converted to a menu item</li>
14788      * <li>A menu item config object that will be created as a new menu item</li>
14789      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14790      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14791      * </ul>
14792      * Usage:
14793      * <pre><code>
14794 // Create the menu
14795 var menu = new Roo.menu.Menu();
14796
14797 // Create a menu item to add by reference
14798 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14799
14800 // Add a bunch of items at once using different methods.
14801 // Only the last item added will be returned.
14802 var item = menu.add(
14803     menuItem,                // add existing item by ref
14804     'Dynamic Item',          // new TextItem
14805     '-',                     // new separator
14806     { text: 'Config Item' }  // new item by config
14807 );
14808 </code></pre>
14809      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14810      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14811      */
14812     add : function(){
14813         var a = arguments, l = a.length, item;
14814         for(var i = 0; i < l; i++){
14815             var el = a[i];
14816             if ((typeof(el) == "object") && el.xtype && el.xns) {
14817                 el = Roo.factory(el, Roo.menu);
14818             }
14819             
14820             if(el.render){ // some kind of Item
14821                 item = this.addItem(el);
14822             }else if(typeof el == "string"){ // string
14823                 if(el == "separator" || el == "-"){
14824                     item = this.addSeparator();
14825                 }else{
14826                     item = this.addText(el);
14827                 }
14828             }else if(el.tagName || el.el){ // element
14829                 item = this.addElement(el);
14830             }else if(typeof el == "object"){ // must be menu item config?
14831                 item = this.addMenuItem(el);
14832             }
14833         }
14834         return item;
14835     },
14836
14837     /**
14838      * Returns this menu's underlying {@link Roo.Element} object
14839      * @return {Roo.Element} The element
14840      */
14841     getEl : function(){
14842         if(!this.el){
14843             this.render();
14844         }
14845         return this.el;
14846     },
14847
14848     /**
14849      * Adds a separator bar to the menu
14850      * @return {Roo.menu.Item} The menu item that was added
14851      */
14852     addSeparator : function(){
14853         return this.addItem(new Roo.menu.Separator());
14854     },
14855
14856     /**
14857      * Adds an {@link Roo.Element} object to the menu
14858      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14859      * @return {Roo.menu.Item} The menu item that was added
14860      */
14861     addElement : function(el){
14862         return this.addItem(new Roo.menu.BaseItem(el));
14863     },
14864
14865     /**
14866      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14867      * @param {Roo.menu.Item} item The menu item to add
14868      * @return {Roo.menu.Item} The menu item that was added
14869      */
14870     addItem : function(item){
14871         this.items.add(item);
14872         if(this.ul){
14873             var li = document.createElement("li");
14874             li.className = "x-menu-list-item";
14875             this.ul.dom.appendChild(li);
14876             item.render(li, this);
14877             this.delayAutoWidth();
14878         }
14879         return item;
14880     },
14881
14882     /**
14883      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14884      * @param {Object} config A MenuItem config object
14885      * @return {Roo.menu.Item} The menu item that was added
14886      */
14887     addMenuItem : function(config){
14888         if(!(config instanceof Roo.menu.Item)){
14889             if(typeof config.checked == "boolean"){ // must be check menu item config?
14890                 config = new Roo.menu.CheckItem(config);
14891             }else{
14892                 config = new Roo.menu.Item(config);
14893             }
14894         }
14895         return this.addItem(config);
14896     },
14897
14898     /**
14899      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14900      * @param {String} text The text to display in the menu item
14901      * @return {Roo.menu.Item} The menu item that was added
14902      */
14903     addText : function(text){
14904         return this.addItem(new Roo.menu.TextItem({ text : text }));
14905     },
14906
14907     /**
14908      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14909      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14910      * @param {Roo.menu.Item} item The menu item to add
14911      * @return {Roo.menu.Item} The menu item that was added
14912      */
14913     insert : function(index, item){
14914         this.items.insert(index, item);
14915         if(this.ul){
14916             var li = document.createElement("li");
14917             li.className = "x-menu-list-item";
14918             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14919             item.render(li, this);
14920             this.delayAutoWidth();
14921         }
14922         return item;
14923     },
14924
14925     /**
14926      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14927      * @param {Roo.menu.Item} item The menu item to remove
14928      */
14929     remove : function(item){
14930         this.items.removeKey(item.id);
14931         item.destroy();
14932     },
14933
14934     /**
14935      * Removes and destroys all items in the menu
14936      */
14937     removeAll : function(){
14938         var f;
14939         while(f = this.items.first()){
14940             this.remove(f);
14941         }
14942     }
14943 });
14944
14945 // MenuNav is a private utility class used internally by the Menu
14946 Roo.menu.MenuNav = function(menu){
14947     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14948     this.scope = this.menu = menu;
14949 };
14950
14951 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14952     doRelay : function(e, h){
14953         var k = e.getKey();
14954         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14955             this.menu.tryActivate(0, 1);
14956             return false;
14957         }
14958         return h.call(this.scope || this, e, this.menu);
14959     },
14960
14961     up : function(e, m){
14962         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14963             m.tryActivate(m.items.length-1, -1);
14964         }
14965     },
14966
14967     down : function(e, m){
14968         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14969             m.tryActivate(0, 1);
14970         }
14971     },
14972
14973     right : function(e, m){
14974         if(m.activeItem){
14975             m.activeItem.expandMenu(true);
14976         }
14977     },
14978
14979     left : function(e, m){
14980         m.hide();
14981         if(m.parentMenu && m.parentMenu.activeItem){
14982             m.parentMenu.activeItem.activate();
14983         }
14984     },
14985
14986     enter : function(e, m){
14987         if(m.activeItem){
14988             e.stopPropagation();
14989             m.activeItem.onClick(e);
14990             m.fireEvent("click", this, m.activeItem);
14991             return true;
14992         }
14993     }
14994 });/*
14995  * Based on:
14996  * Ext JS Library 1.1.1
14997  * Copyright(c) 2006-2007, Ext JS, LLC.
14998  *
14999  * Originally Released Under LGPL - original licence link has changed is not relivant.
15000  *
15001  * Fork - LGPL
15002  * <script type="text/javascript">
15003  */
15004  
15005 /**
15006  * @class Roo.menu.MenuMgr
15007  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15008  * @static
15009  */
15010 Roo.menu.MenuMgr = function(){
15011    var menus, active, groups = {}, attached = false, lastShow = new Date();
15012
15013    // private - called when first menu is created
15014    function init(){
15015        menus = {};
15016        active = new Roo.util.MixedCollection();
15017        Roo.get(document).addKeyListener(27, function(){
15018            if(active.length > 0){
15019                hideAll();
15020            }
15021        });
15022    }
15023
15024    // private
15025    function hideAll(){
15026        if(active && active.length > 0){
15027            var c = active.clone();
15028            c.each(function(m){
15029                m.hide();
15030            });
15031        }
15032    }
15033
15034    // private
15035    function onHide(m){
15036        active.remove(m);
15037        if(active.length < 1){
15038            Roo.get(document).un("mousedown", onMouseDown);
15039            attached = false;
15040        }
15041    }
15042
15043    // private
15044    function onShow(m){
15045        var last = active.last();
15046        lastShow = new Date();
15047        active.add(m);
15048        if(!attached){
15049            Roo.get(document).on("mousedown", onMouseDown);
15050            attached = true;
15051        }
15052        if(m.parentMenu){
15053           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15054           m.parentMenu.activeChild = m;
15055        }else if(last && last.isVisible()){
15056           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15057        }
15058    }
15059
15060    // private
15061    function onBeforeHide(m){
15062        if(m.activeChild){
15063            m.activeChild.hide();
15064        }
15065        if(m.autoHideTimer){
15066            clearTimeout(m.autoHideTimer);
15067            delete m.autoHideTimer;
15068        }
15069    }
15070
15071    // private
15072    function onBeforeShow(m){
15073        var pm = m.parentMenu;
15074        if(!pm && !m.allowOtherMenus){
15075            hideAll();
15076        }else if(pm && pm.activeChild && active != m){
15077            pm.activeChild.hide();
15078        }
15079    }
15080
15081    // private
15082    function onMouseDown(e){
15083        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15084            hideAll();
15085        }
15086    }
15087
15088    // private
15089    function onBeforeCheck(mi, state){
15090        if(state){
15091            var g = groups[mi.group];
15092            for(var i = 0, l = g.length; i < l; i++){
15093                if(g[i] != mi){
15094                    g[i].setChecked(false);
15095                }
15096            }
15097        }
15098    }
15099
15100    return {
15101
15102        /**
15103         * Hides all menus that are currently visible
15104         */
15105        hideAll : function(){
15106             hideAll();  
15107        },
15108
15109        // private
15110        register : function(menu){
15111            if(!menus){
15112                init();
15113            }
15114            menus[menu.id] = menu;
15115            menu.on("beforehide", onBeforeHide);
15116            menu.on("hide", onHide);
15117            menu.on("beforeshow", onBeforeShow);
15118            menu.on("show", onShow);
15119            var g = menu.group;
15120            if(g && menu.events["checkchange"]){
15121                if(!groups[g]){
15122                    groups[g] = [];
15123                }
15124                groups[g].push(menu);
15125                menu.on("checkchange", onCheck);
15126            }
15127        },
15128
15129         /**
15130          * Returns a {@link Roo.menu.Menu} object
15131          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15132          * be used to generate and return a new Menu instance.
15133          */
15134        get : function(menu){
15135            if(typeof menu == "string"){ // menu id
15136                return menus[menu];
15137            }else if(menu.events){  // menu instance
15138                return menu;
15139            }else if(typeof menu.length == 'number'){ // array of menu items?
15140                return new Roo.menu.Menu({items:menu});
15141            }else{ // otherwise, must be a config
15142                return new Roo.menu.Menu(menu);
15143            }
15144        },
15145
15146        // private
15147        unregister : function(menu){
15148            delete menus[menu.id];
15149            menu.un("beforehide", onBeforeHide);
15150            menu.un("hide", onHide);
15151            menu.un("beforeshow", onBeforeShow);
15152            menu.un("show", onShow);
15153            var g = menu.group;
15154            if(g && menu.events["checkchange"]){
15155                groups[g].remove(menu);
15156                menu.un("checkchange", onCheck);
15157            }
15158        },
15159
15160        // private
15161        registerCheckable : function(menuItem){
15162            var g = menuItem.group;
15163            if(g){
15164                if(!groups[g]){
15165                    groups[g] = [];
15166                }
15167                groups[g].push(menuItem);
15168                menuItem.on("beforecheckchange", onBeforeCheck);
15169            }
15170        },
15171
15172        // private
15173        unregisterCheckable : function(menuItem){
15174            var g = menuItem.group;
15175            if(g){
15176                groups[g].remove(menuItem);
15177                menuItem.un("beforecheckchange", onBeforeCheck);
15178            }
15179        }
15180    };
15181 }();/*
15182  * Based on:
15183  * Ext JS Library 1.1.1
15184  * Copyright(c) 2006-2007, Ext JS, LLC.
15185  *
15186  * Originally Released Under LGPL - original licence link has changed is not relivant.
15187  *
15188  * Fork - LGPL
15189  * <script type="text/javascript">
15190  */
15191  
15192
15193 /**
15194  * @class Roo.menu.BaseItem
15195  * @extends Roo.Component
15196  * @abstract
15197  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15198  * management and base configuration options shared by all menu components.
15199  * @constructor
15200  * Creates a new BaseItem
15201  * @param {Object} config Configuration options
15202  */
15203 Roo.menu.BaseItem = function(config){
15204     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15205
15206     this.addEvents({
15207         /**
15208          * @event click
15209          * Fires when this item is clicked
15210          * @param {Roo.menu.BaseItem} this
15211          * @param {Roo.EventObject} e
15212          */
15213         click: true,
15214         /**
15215          * @event activate
15216          * Fires when this item is activated
15217          * @param {Roo.menu.BaseItem} this
15218          */
15219         activate : true,
15220         /**
15221          * @event deactivate
15222          * Fires when this item is deactivated
15223          * @param {Roo.menu.BaseItem} this
15224          */
15225         deactivate : true
15226     });
15227
15228     if(this.handler){
15229         this.on("click", this.handler, this.scope, true);
15230     }
15231 };
15232
15233 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15234     /**
15235      * @cfg {Function} handler
15236      * A function that will handle the click event of this menu item (defaults to undefined)
15237      */
15238     /**
15239      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15240      */
15241     canActivate : false,
15242     
15243      /**
15244      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15245      */
15246     hidden: false,
15247     
15248     /**
15249      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15250      */
15251     activeClass : "x-menu-item-active",
15252     /**
15253      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15254      */
15255     hideOnClick : true,
15256     /**
15257      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15258      */
15259     hideDelay : 100,
15260
15261     // private
15262     ctype: "Roo.menu.BaseItem",
15263
15264     // private
15265     actionMode : "container",
15266
15267     // private
15268     render : function(container, parentMenu){
15269         this.parentMenu = parentMenu;
15270         Roo.menu.BaseItem.superclass.render.call(this, container);
15271         this.container.menuItemId = this.id;
15272     },
15273
15274     // private
15275     onRender : function(container, position){
15276         this.el = Roo.get(this.el);
15277         container.dom.appendChild(this.el.dom);
15278     },
15279
15280     // private
15281     onClick : function(e){
15282         if(!this.disabled && this.fireEvent("click", this, e) !== false
15283                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15284             this.handleClick(e);
15285         }else{
15286             e.stopEvent();
15287         }
15288     },
15289
15290     // private
15291     activate : function(){
15292         if(this.disabled){
15293             return false;
15294         }
15295         var li = this.container;
15296         li.addClass(this.activeClass);
15297         this.region = li.getRegion().adjust(2, 2, -2, -2);
15298         this.fireEvent("activate", this);
15299         return true;
15300     },
15301
15302     // private
15303     deactivate : function(){
15304         this.container.removeClass(this.activeClass);
15305         this.fireEvent("deactivate", this);
15306     },
15307
15308     // private
15309     shouldDeactivate : function(e){
15310         return !this.region || !this.region.contains(e.getPoint());
15311     },
15312
15313     // private
15314     handleClick : function(e){
15315         if(this.hideOnClick){
15316             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15317         }
15318     },
15319
15320     // private
15321     expandMenu : function(autoActivate){
15322         // do nothing
15323     },
15324
15325     // private
15326     hideMenu : function(){
15327         // do nothing
15328     }
15329 });/*
15330  * Based on:
15331  * Ext JS Library 1.1.1
15332  * Copyright(c) 2006-2007, Ext JS, LLC.
15333  *
15334  * Originally Released Under LGPL - original licence link has changed is not relivant.
15335  *
15336  * Fork - LGPL
15337  * <script type="text/javascript">
15338  */
15339  
15340 /**
15341  * @class Roo.menu.Adapter
15342  * @extends Roo.menu.BaseItem
15343  * @abstract
15344  * 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.
15345  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15346  * @constructor
15347  * Creates a new Adapter
15348  * @param {Object} config Configuration options
15349  */
15350 Roo.menu.Adapter = function(component, config){
15351     Roo.menu.Adapter.superclass.constructor.call(this, config);
15352     this.component = component;
15353 };
15354 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15355     // private
15356     canActivate : true,
15357
15358     // private
15359     onRender : function(container, position){
15360         this.component.render(container);
15361         this.el = this.component.getEl();
15362     },
15363
15364     // private
15365     activate : function(){
15366         if(this.disabled){
15367             return false;
15368         }
15369         this.component.focus();
15370         this.fireEvent("activate", this);
15371         return true;
15372     },
15373
15374     // private
15375     deactivate : function(){
15376         this.fireEvent("deactivate", this);
15377     },
15378
15379     // private
15380     disable : function(){
15381         this.component.disable();
15382         Roo.menu.Adapter.superclass.disable.call(this);
15383     },
15384
15385     // private
15386     enable : function(){
15387         this.component.enable();
15388         Roo.menu.Adapter.superclass.enable.call(this);
15389     }
15390 });/*
15391  * Based on:
15392  * Ext JS Library 1.1.1
15393  * Copyright(c) 2006-2007, Ext JS, LLC.
15394  *
15395  * Originally Released Under LGPL - original licence link has changed is not relivant.
15396  *
15397  * Fork - LGPL
15398  * <script type="text/javascript">
15399  */
15400
15401 /**
15402  * @class Roo.menu.TextItem
15403  * @extends Roo.menu.BaseItem
15404  * Adds a static text string to a menu, usually used as either a heading or group separator.
15405  * Note: old style constructor with text is still supported.
15406  * 
15407  * @constructor
15408  * Creates a new TextItem
15409  * @param {Object} cfg Configuration
15410  */
15411 Roo.menu.TextItem = function(cfg){
15412     if (typeof(cfg) == 'string') {
15413         this.text = cfg;
15414     } else {
15415         Roo.apply(this,cfg);
15416     }
15417     
15418     Roo.menu.TextItem.superclass.constructor.call(this);
15419 };
15420
15421 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15422     /**
15423      * @cfg {String} text Text to show on item.
15424      */
15425     text : '',
15426     
15427     /**
15428      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15429      */
15430     hideOnClick : false,
15431     /**
15432      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15433      */
15434     itemCls : "x-menu-text",
15435
15436     // private
15437     onRender : function(){
15438         var s = document.createElement("span");
15439         s.className = this.itemCls;
15440         s.innerHTML = this.text;
15441         this.el = s;
15442         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15443     }
15444 });/*
15445  * Based on:
15446  * Ext JS Library 1.1.1
15447  * Copyright(c) 2006-2007, Ext JS, LLC.
15448  *
15449  * Originally Released Under LGPL - original licence link has changed is not relivant.
15450  *
15451  * Fork - LGPL
15452  * <script type="text/javascript">
15453  */
15454
15455 /**
15456  * @class Roo.menu.Separator
15457  * @extends Roo.menu.BaseItem
15458  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15459  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15460  * @constructor
15461  * @param {Object} config Configuration options
15462  */
15463 Roo.menu.Separator = function(config){
15464     Roo.menu.Separator.superclass.constructor.call(this, config);
15465 };
15466
15467 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15468     /**
15469      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15470      */
15471     itemCls : "x-menu-sep",
15472     /**
15473      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15474      */
15475     hideOnClick : false,
15476
15477     // private
15478     onRender : function(li){
15479         var s = document.createElement("span");
15480         s.className = this.itemCls;
15481         s.innerHTML = "&#160;";
15482         this.el = s;
15483         li.addClass("x-menu-sep-li");
15484         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15485     }
15486 });/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496 /**
15497  * @class Roo.menu.Item
15498  * @extends Roo.menu.BaseItem
15499  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15500  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15501  * activation and click handling.
15502  * @constructor
15503  * Creates a new Item
15504  * @param {Object} config Configuration options
15505  */
15506 Roo.menu.Item = function(config){
15507     Roo.menu.Item.superclass.constructor.call(this, config);
15508     if(this.menu){
15509         this.menu = Roo.menu.MenuMgr.get(this.menu);
15510     }
15511 };
15512 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15513     /**
15514      * @cfg {Roo.menu.Menu} menu
15515      * A Sub menu
15516      */
15517     /**
15518      * @cfg {String} text
15519      * The text to show on the menu item.
15520      */
15521     text: '',
15522      /**
15523      * @cfg {String} HTML to render in menu
15524      * The text to show on the menu item (HTML version).
15525      */
15526     html: '',
15527     /**
15528      * @cfg {String} icon
15529      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15530      */
15531     icon: undefined,
15532     /**
15533      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15534      */
15535     itemCls : "x-menu-item",
15536     /**
15537      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15538      */
15539     canActivate : true,
15540     /**
15541      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15542      */
15543     showDelay: 200,
15544     // doc'd in BaseItem
15545     hideDelay: 200,
15546
15547     // private
15548     ctype: "Roo.menu.Item",
15549     
15550     // private
15551     onRender : function(container, position){
15552         var el = document.createElement("a");
15553         el.hideFocus = true;
15554         el.unselectable = "on";
15555         el.href = this.href || "#";
15556         if(this.hrefTarget){
15557             el.target = this.hrefTarget;
15558         }
15559         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15560         
15561         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15562         
15563         el.innerHTML = String.format(
15564                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15565                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15566         this.el = el;
15567         Roo.menu.Item.superclass.onRender.call(this, container, position);
15568     },
15569
15570     /**
15571      * Sets the text to display in this menu item
15572      * @param {String} text The text to display
15573      * @param {Boolean} isHTML true to indicate text is pure html.
15574      */
15575     setText : function(text, isHTML){
15576         if (isHTML) {
15577             this.html = text;
15578         } else {
15579             this.text = text;
15580             this.html = '';
15581         }
15582         if(this.rendered){
15583             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15584      
15585             this.el.update(String.format(
15586                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15587                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15588             this.parentMenu.autoWidth();
15589         }
15590     },
15591
15592     // private
15593     handleClick : function(e){
15594         if(!this.href){ // if no link defined, stop the event automatically
15595             e.stopEvent();
15596         }
15597         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15598     },
15599
15600     // private
15601     activate : function(autoExpand){
15602         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15603             this.focus();
15604             if(autoExpand){
15605                 this.expandMenu();
15606             }
15607         }
15608         return true;
15609     },
15610
15611     // private
15612     shouldDeactivate : function(e){
15613         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15614             if(this.menu && this.menu.isVisible()){
15615                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15616             }
15617             return true;
15618         }
15619         return false;
15620     },
15621
15622     // private
15623     deactivate : function(){
15624         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15625         this.hideMenu();
15626     },
15627
15628     // private
15629     expandMenu : function(autoActivate){
15630         if(!this.disabled && this.menu){
15631             clearTimeout(this.hideTimer);
15632             delete this.hideTimer;
15633             if(!this.menu.isVisible() && !this.showTimer){
15634                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15635             }else if (this.menu.isVisible() && autoActivate){
15636                 this.menu.tryActivate(0, 1);
15637             }
15638         }
15639     },
15640
15641     // private
15642     deferExpand : function(autoActivate){
15643         delete this.showTimer;
15644         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15645         if(autoActivate){
15646             this.menu.tryActivate(0, 1);
15647         }
15648     },
15649
15650     // private
15651     hideMenu : function(){
15652         clearTimeout(this.showTimer);
15653         delete this.showTimer;
15654         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15655             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15656         }
15657     },
15658
15659     // private
15660     deferHide : function(){
15661         delete this.hideTimer;
15662         this.menu.hide();
15663     }
15664 });/*
15665  * Based on:
15666  * Ext JS Library 1.1.1
15667  * Copyright(c) 2006-2007, Ext JS, LLC.
15668  *
15669  * Originally Released Under LGPL - original licence link has changed is not relivant.
15670  *
15671  * Fork - LGPL
15672  * <script type="text/javascript">
15673  */
15674  
15675 /**
15676  * @class Roo.menu.CheckItem
15677  * @extends Roo.menu.Item
15678  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15679  * @constructor
15680  * Creates a new CheckItem
15681  * @param {Object} config Configuration options
15682  */
15683 Roo.menu.CheckItem = function(config){
15684     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15685     this.addEvents({
15686         /**
15687          * @event beforecheckchange
15688          * Fires before the checked value is set, providing an opportunity to cancel if needed
15689          * @param {Roo.menu.CheckItem} this
15690          * @param {Boolean} checked The new checked value that will be set
15691          */
15692         "beforecheckchange" : true,
15693         /**
15694          * @event checkchange
15695          * Fires after the checked value has been set
15696          * @param {Roo.menu.CheckItem} this
15697          * @param {Boolean} checked The checked value that was set
15698          */
15699         "checkchange" : true
15700     });
15701     if(this.checkHandler){
15702         this.on('checkchange', this.checkHandler, this.scope);
15703     }
15704 };
15705 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15706     /**
15707      * @cfg {String} group
15708      * All check items with the same group name will automatically be grouped into a single-select
15709      * radio button group (defaults to '')
15710      */
15711     /**
15712      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15713      */
15714     itemCls : "x-menu-item x-menu-check-item",
15715     /**
15716      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15717      */
15718     groupClass : "x-menu-group-item",
15719
15720     /**
15721      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15722      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15723      * initialized with checked = true will be rendered as checked.
15724      */
15725     checked: false,
15726
15727     // private
15728     ctype: "Roo.menu.CheckItem",
15729
15730     // private
15731     onRender : function(c){
15732         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15733         if(this.group){
15734             this.el.addClass(this.groupClass);
15735         }
15736         Roo.menu.MenuMgr.registerCheckable(this);
15737         if(this.checked){
15738             this.checked = false;
15739             this.setChecked(true, true);
15740         }
15741     },
15742
15743     // private
15744     destroy : function(){
15745         if(this.rendered){
15746             Roo.menu.MenuMgr.unregisterCheckable(this);
15747         }
15748         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15749     },
15750
15751     /**
15752      * Set the checked state of this item
15753      * @param {Boolean} checked The new checked value
15754      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15755      */
15756     setChecked : function(state, suppressEvent){
15757         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15758             if(this.container){
15759                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15760             }
15761             this.checked = state;
15762             if(suppressEvent !== true){
15763                 this.fireEvent("checkchange", this, state);
15764             }
15765         }
15766     },
15767
15768     // private
15769     handleClick : function(e){
15770        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15771            this.setChecked(!this.checked);
15772        }
15773        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15774     }
15775 });/*
15776  * Based on:
15777  * Ext JS Library 1.1.1
15778  * Copyright(c) 2006-2007, Ext JS, LLC.
15779  *
15780  * Originally Released Under LGPL - original licence link has changed is not relivant.
15781  *
15782  * Fork - LGPL
15783  * <script type="text/javascript">
15784  */
15785  
15786 /**
15787  * @class Roo.menu.DateItem
15788  * @extends Roo.menu.Adapter
15789  * A menu item that wraps the {@link Roo.DatPicker} component.
15790  * @constructor
15791  * Creates a new DateItem
15792  * @param {Object} config Configuration options
15793  */
15794 Roo.menu.DateItem = function(config){
15795     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15796     /** The Roo.DatePicker object @type Roo.DatePicker */
15797     this.picker = this.component;
15798     this.addEvents({select: true});
15799     
15800     this.picker.on("render", function(picker){
15801         picker.getEl().swallowEvent("click");
15802         picker.container.addClass("x-menu-date-item");
15803     });
15804
15805     this.picker.on("select", this.onSelect, this);
15806 };
15807
15808 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15809     // private
15810     onSelect : function(picker, date){
15811         this.fireEvent("select", this, date, picker);
15812         Roo.menu.DateItem.superclass.handleClick.call(this);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824  
15825 /**
15826  * @class Roo.menu.ColorItem
15827  * @extends Roo.menu.Adapter
15828  * A menu item that wraps the {@link Roo.ColorPalette} component.
15829  * @constructor
15830  * Creates a new ColorItem
15831  * @param {Object} config Configuration options
15832  */
15833 Roo.menu.ColorItem = function(config){
15834     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15835     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15836     this.palette = this.component;
15837     this.relayEvents(this.palette, ["select"]);
15838     if(this.selectHandler){
15839         this.on('select', this.selectHandler, this.scope);
15840     }
15841 };
15842 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852  
15853
15854 /**
15855  * @class Roo.menu.DateMenu
15856  * @extends Roo.menu.Menu
15857  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15858  * @constructor
15859  * Creates a new DateMenu
15860  * @param {Object} config Configuration options
15861  */
15862 Roo.menu.DateMenu = function(config){
15863     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15864     this.plain = true;
15865     var di = new Roo.menu.DateItem(config);
15866     this.add(di);
15867     /**
15868      * The {@link Roo.DatePicker} instance for this DateMenu
15869      * @type DatePicker
15870      */
15871     this.picker = di.picker;
15872     /**
15873      * @event select
15874      * @param {DatePicker} picker
15875      * @param {Date} date
15876      */
15877     this.relayEvents(di, ["select"]);
15878     this.on('beforeshow', function(){
15879         if(this.picker){
15880             this.picker.hideMonthPicker(false);
15881         }
15882     }, this);
15883 };
15884 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15885     cls:'x-date-menu'
15886 });/*
15887  * Based on:
15888  * Ext JS Library 1.1.1
15889  * Copyright(c) 2006-2007, Ext JS, LLC.
15890  *
15891  * Originally Released Under LGPL - original licence link has changed is not relivant.
15892  *
15893  * Fork - LGPL
15894  * <script type="text/javascript">
15895  */
15896  
15897
15898 /**
15899  * @class Roo.menu.ColorMenu
15900  * @extends Roo.menu.Menu
15901  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15902  * @constructor
15903  * Creates a new ColorMenu
15904  * @param {Object} config Configuration options
15905  */
15906 Roo.menu.ColorMenu = function(config){
15907     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15908     this.plain = true;
15909     var ci = new Roo.menu.ColorItem(config);
15910     this.add(ci);
15911     /**
15912      * The {@link Roo.ColorPalette} instance for this ColorMenu
15913      * @type ColorPalette
15914      */
15915     this.palette = ci.palette;
15916     /**
15917      * @event select
15918      * @param {ColorPalette} palette
15919      * @param {String} color
15920      */
15921     this.relayEvents(ci, ["select"]);
15922 };
15923 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15924  * Based on:
15925  * Ext JS Library 1.1.1
15926  * Copyright(c) 2006-2007, Ext JS, LLC.
15927  *
15928  * Originally Released Under LGPL - original licence link has changed is not relivant.
15929  *
15930  * Fork - LGPL
15931  * <script type="text/javascript">
15932  */
15933  
15934 /**
15935  * @class Roo.form.TextItem
15936  * @extends Roo.BoxComponent
15937  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15938  * @constructor
15939  * Creates a new TextItem
15940  * @param {Object} config Configuration options
15941  */
15942 Roo.form.TextItem = function(config){
15943     Roo.form.TextItem.superclass.constructor.call(this, config);
15944 };
15945
15946 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15947     
15948     /**
15949      * @cfg {String} tag the tag for this item (default div)
15950      */
15951     tag : 'div',
15952     /**
15953      * @cfg {String} html the content for this item
15954      */
15955     html : '',
15956     
15957     getAutoCreate : function()
15958     {
15959         var cfg = {
15960             id: this.id,
15961             tag: this.tag,
15962             html: this.html,
15963             cls: 'x-form-item'
15964         };
15965         
15966         return cfg;
15967         
15968     },
15969     
15970     onRender : function(ct, position)
15971     {
15972         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15973         
15974         if(!this.el){
15975             var cfg = this.getAutoCreate();
15976             if(!cfg.name){
15977                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15978             }
15979             if (!cfg.name.length) {
15980                 delete cfg.name;
15981             }
15982             this.el = ct.createChild(cfg, position);
15983         }
15984     },
15985     /*
15986      * setHTML
15987      * @param {String} html update the Contents of the element.
15988      */
15989     setHTML : function(html)
15990     {
15991         this.fieldEl.dom.innerHTML = html;
15992     }
15993     
15994 });/*
15995  * Based on:
15996  * Ext JS Library 1.1.1
15997  * Copyright(c) 2006-2007, Ext JS, LLC.
15998  *
15999  * Originally Released Under LGPL - original licence link has changed is not relivant.
16000  *
16001  * Fork - LGPL
16002  * <script type="text/javascript">
16003  */
16004  
16005 /**
16006  * @class Roo.form.Field
16007  * @extends Roo.BoxComponent
16008  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16009  * @constructor
16010  * Creates a new Field
16011  * @param {Object} config Configuration options
16012  */
16013 Roo.form.Field = function(config){
16014     Roo.form.Field.superclass.constructor.call(this, config);
16015 };
16016
16017 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16018     /**
16019      * @cfg {String} fieldLabel Label to use when rendering a form.
16020      */
16021        /**
16022      * @cfg {String} qtip Mouse over tip
16023      */
16024      
16025     /**
16026      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16027      */
16028     invalidClass : "x-form-invalid",
16029     /**
16030      * @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")
16031      */
16032     invalidText : "The value in this field is invalid",
16033     /**
16034      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16035      */
16036     focusClass : "x-form-focus",
16037     /**
16038      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16039       automatic validation (defaults to "keyup").
16040      */
16041     validationEvent : "keyup",
16042     /**
16043      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16044      */
16045     validateOnBlur : true,
16046     /**
16047      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16048      */
16049     validationDelay : 250,
16050     /**
16051      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16052      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16053      */
16054     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16055     /**
16056      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16057      */
16058     fieldClass : "x-form-field",
16059     /**
16060      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16061      *<pre>
16062 Value         Description
16063 -----------   ----------------------------------------------------------------------
16064 qtip          Display a quick tip when the user hovers over the field
16065 title         Display a default browser title attribute popup
16066 under         Add a block div beneath the field containing the error text
16067 side          Add an error icon to the right of the field with a popup on hover
16068 [element id]  Add the error text directly to the innerHTML of the specified element
16069 </pre>
16070      */
16071     msgTarget : 'qtip',
16072     /**
16073      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16074      */
16075     msgFx : 'normal',
16076
16077     /**
16078      * @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.
16079      */
16080     readOnly : false,
16081
16082     /**
16083      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16084      */
16085     disabled : false,
16086
16087     /**
16088      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16089      */
16090     inputType : undefined,
16091     
16092     /**
16093      * @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).
16094          */
16095         tabIndex : undefined,
16096         
16097     // private
16098     isFormField : true,
16099
16100     // private
16101     hasFocus : false,
16102     /**
16103      * @property {Roo.Element} fieldEl
16104      * Element Containing the rendered Field (with label etc.)
16105      */
16106     /**
16107      * @cfg {Mixed} value A value to initialize this field with.
16108      */
16109     value : undefined,
16110
16111     /**
16112      * @cfg {String} name The field's HTML name attribute.
16113      */
16114     /**
16115      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16116      */
16117     // private
16118     loadedValue : false,
16119      
16120      
16121         // private ??
16122         initComponent : function(){
16123         Roo.form.Field.superclass.initComponent.call(this);
16124         this.addEvents({
16125             /**
16126              * @event focus
16127              * Fires when this field receives input focus.
16128              * @param {Roo.form.Field} this
16129              */
16130             focus : true,
16131             /**
16132              * @event blur
16133              * Fires when this field loses input focus.
16134              * @param {Roo.form.Field} this
16135              */
16136             blur : true,
16137             /**
16138              * @event specialkey
16139              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16140              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16141              * @param {Roo.form.Field} this
16142              * @param {Roo.EventObject} e The event object
16143              */
16144             specialkey : true,
16145             /**
16146              * @event change
16147              * Fires just before the field blurs if the field value has changed.
16148              * @param {Roo.form.Field} this
16149              * @param {Mixed} newValue The new value
16150              * @param {Mixed} oldValue The original value
16151              */
16152             change : true,
16153             /**
16154              * @event invalid
16155              * Fires after the field has been marked as invalid.
16156              * @param {Roo.form.Field} this
16157              * @param {String} msg The validation message
16158              */
16159             invalid : true,
16160             /**
16161              * @event valid
16162              * Fires after the field has been validated with no errors.
16163              * @param {Roo.form.Field} this
16164              */
16165             valid : true,
16166              /**
16167              * @event keyup
16168              * Fires after the key up
16169              * @param {Roo.form.Field} this
16170              * @param {Roo.EventObject}  e The event Object
16171              */
16172             keyup : true
16173         });
16174     },
16175
16176     /**
16177      * Returns the name attribute of the field if available
16178      * @return {String} name The field name
16179      */
16180     getName: function(){
16181          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16182     },
16183
16184     // private
16185     onRender : function(ct, position){
16186         Roo.form.Field.superclass.onRender.call(this, ct, position);
16187         if(!this.el){
16188             var cfg = this.getAutoCreate();
16189             if(!cfg.name){
16190                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16191             }
16192             if (!cfg.name.length) {
16193                 delete cfg.name;
16194             }
16195             if(this.inputType){
16196                 cfg.type = this.inputType;
16197             }
16198             this.el = ct.createChild(cfg, position);
16199         }
16200         var type = this.el.dom.type;
16201         if(type){
16202             if(type == 'password'){
16203                 type = 'text';
16204             }
16205             this.el.addClass('x-form-'+type);
16206         }
16207         if(this.readOnly){
16208             this.el.dom.readOnly = true;
16209         }
16210         if(this.tabIndex !== undefined){
16211             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16212         }
16213
16214         this.el.addClass([this.fieldClass, this.cls]);
16215         this.initValue();
16216     },
16217
16218     /**
16219      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16220      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16221      * @return {Roo.form.Field} this
16222      */
16223     applyTo : function(target){
16224         this.allowDomMove = false;
16225         this.el = Roo.get(target);
16226         this.render(this.el.dom.parentNode);
16227         return this;
16228     },
16229
16230     // private
16231     initValue : function(){
16232         if(this.value !== undefined){
16233             this.setValue(this.value);
16234         }else if(this.el.dom.value.length > 0){
16235             this.setValue(this.el.dom.value);
16236         }
16237     },
16238
16239     /**
16240      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16241      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16242      */
16243     isDirty : function() {
16244         if(this.disabled) {
16245             return false;
16246         }
16247         return String(this.getValue()) !== String(this.originalValue);
16248     },
16249
16250     /**
16251      * stores the current value in loadedValue
16252      */
16253     resetHasChanged : function()
16254     {
16255         this.loadedValue = String(this.getValue());
16256     },
16257     /**
16258      * checks the current value against the 'loaded' value.
16259      * Note - will return false if 'resetHasChanged' has not been called first.
16260      */
16261     hasChanged : function()
16262     {
16263         if(this.disabled || this.readOnly) {
16264             return false;
16265         }
16266         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16267     },
16268     
16269     
16270     
16271     // private
16272     afterRender : function(){
16273         Roo.form.Field.superclass.afterRender.call(this);
16274         this.initEvents();
16275     },
16276
16277     // private
16278     fireKey : function(e){
16279         //Roo.log('field ' + e.getKey());
16280         if(e.isNavKeyPress()){
16281             this.fireEvent("specialkey", this, e);
16282         }
16283     },
16284
16285     /**
16286      * Resets the current field value to the originally loaded value and clears any validation messages
16287      */
16288     reset : function(){
16289         this.setValue(this.resetValue);
16290         this.originalValue = this.getValue();
16291         this.clearInvalid();
16292     },
16293
16294     // private
16295     initEvents : function(){
16296         // safari killled keypress - so keydown is now used..
16297         this.el.on("keydown" , this.fireKey,  this);
16298         this.el.on("focus", this.onFocus,  this);
16299         this.el.on("blur", this.onBlur,  this);
16300         this.el.relayEvent('keyup', this);
16301
16302         // reference to original value for reset
16303         this.originalValue = this.getValue();
16304         this.resetValue =  this.getValue();
16305     },
16306
16307     // private
16308     onFocus : function(){
16309         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16310             this.el.addClass(this.focusClass);
16311         }
16312         if(!this.hasFocus){
16313             this.hasFocus = true;
16314             this.startValue = this.getValue();
16315             this.fireEvent("focus", this);
16316         }
16317     },
16318
16319     beforeBlur : Roo.emptyFn,
16320
16321     // private
16322     onBlur : function(){
16323         this.beforeBlur();
16324         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16325             this.el.removeClass(this.focusClass);
16326         }
16327         this.hasFocus = false;
16328         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16329             this.validate();
16330         }
16331         var v = this.getValue();
16332         if(String(v) !== String(this.startValue)){
16333             this.fireEvent('change', this, v, this.startValue);
16334         }
16335         this.fireEvent("blur", this);
16336     },
16337
16338     /**
16339      * Returns whether or not the field value is currently valid
16340      * @param {Boolean} preventMark True to disable marking the field invalid
16341      * @return {Boolean} True if the value is valid, else false
16342      */
16343     isValid : function(preventMark){
16344         if(this.disabled){
16345             return true;
16346         }
16347         var restore = this.preventMark;
16348         this.preventMark = preventMark === true;
16349         var v = this.validateValue(this.processValue(this.getRawValue()));
16350         this.preventMark = restore;
16351         return v;
16352     },
16353
16354     /**
16355      * Validates the field value
16356      * @return {Boolean} True if the value is valid, else false
16357      */
16358     validate : function(){
16359         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16360             this.clearInvalid();
16361             return true;
16362         }
16363         return false;
16364     },
16365
16366     processValue : function(value){
16367         return value;
16368     },
16369
16370     // private
16371     // Subclasses should provide the validation implementation by overriding this
16372     validateValue : function(value){
16373         return true;
16374     },
16375
16376     /**
16377      * Mark this field as invalid
16378      * @param {String} msg The validation message
16379      */
16380     markInvalid : function(msg){
16381         if(!this.rendered || this.preventMark){ // not rendered
16382             return;
16383         }
16384         
16385         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16386         
16387         obj.el.addClass(this.invalidClass);
16388         msg = msg || this.invalidText;
16389         switch(this.msgTarget){
16390             case 'qtip':
16391                 obj.el.dom.qtip = msg;
16392                 obj.el.dom.qclass = 'x-form-invalid-tip';
16393                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16394                     Roo.QuickTips.enable();
16395                 }
16396                 break;
16397             case 'title':
16398                 this.el.dom.title = msg;
16399                 break;
16400             case 'under':
16401                 if(!this.errorEl){
16402                     var elp = this.el.findParent('.x-form-element', 5, true);
16403                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16404                     this.errorEl.setWidth(elp.getWidth(true)-20);
16405                 }
16406                 this.errorEl.update(msg);
16407                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16408                 break;
16409             case 'side':
16410                 if(!this.errorIcon){
16411                     var elp = this.el.findParent('.x-form-element', 5, true);
16412                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16413                 }
16414                 this.alignErrorIcon();
16415                 this.errorIcon.dom.qtip = msg;
16416                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16417                 this.errorIcon.show();
16418                 this.on('resize', this.alignErrorIcon, this);
16419                 break;
16420             default:
16421                 var t = Roo.getDom(this.msgTarget);
16422                 t.innerHTML = msg;
16423                 t.style.display = this.msgDisplay;
16424                 break;
16425         }
16426         this.fireEvent('invalid', this, msg);
16427     },
16428
16429     // private
16430     alignErrorIcon : function(){
16431         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16432     },
16433
16434     /**
16435      * Clear any invalid styles/messages for this field
16436      */
16437     clearInvalid : function(){
16438         if(!this.rendered || this.preventMark){ // not rendered
16439             return;
16440         }
16441         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16442         
16443         obj.el.removeClass(this.invalidClass);
16444         switch(this.msgTarget){
16445             case 'qtip':
16446                 obj.el.dom.qtip = '';
16447                 break;
16448             case 'title':
16449                 this.el.dom.title = '';
16450                 break;
16451             case 'under':
16452                 if(this.errorEl){
16453                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16454                 }
16455                 break;
16456             case 'side':
16457                 if(this.errorIcon){
16458                     this.errorIcon.dom.qtip = '';
16459                     this.errorIcon.hide();
16460                     this.un('resize', this.alignErrorIcon, this);
16461                 }
16462                 break;
16463             default:
16464                 var t = Roo.getDom(this.msgTarget);
16465                 t.innerHTML = '';
16466                 t.style.display = 'none';
16467                 break;
16468         }
16469         this.fireEvent('valid', this);
16470     },
16471
16472     /**
16473      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16474      * @return {Mixed} value The field value
16475      */
16476     getRawValue : function(){
16477         var v = this.el.getValue();
16478         
16479         return v;
16480     },
16481
16482     /**
16483      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16484      * @return {Mixed} value The field value
16485      */
16486     getValue : function(){
16487         var v = this.el.getValue();
16488          
16489         return v;
16490     },
16491
16492     /**
16493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16494      * @param {Mixed} value The value to set
16495      */
16496     setRawValue : function(v){
16497         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16498     },
16499
16500     /**
16501      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16502      * @param {Mixed} value The value to set
16503      */
16504     setValue : function(v){
16505         this.value = v;
16506         if(this.rendered){
16507             this.el.dom.value = (v === null || v === undefined ? '' : v);
16508              this.validate();
16509         }
16510     },
16511
16512     adjustSize : function(w, h){
16513         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16514         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16515         return s;
16516     },
16517
16518     adjustWidth : function(tag, w){
16519         tag = tag.toLowerCase();
16520         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16521             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16522                 if(tag == 'input'){
16523                     return w + 2;
16524                 }
16525                 if(tag == 'textarea'){
16526                     return w-2;
16527                 }
16528             }else if(Roo.isOpera){
16529                 if(tag == 'input'){
16530                     return w + 2;
16531                 }
16532                 if(tag == 'textarea'){
16533                     return w-2;
16534                 }
16535             }
16536         }
16537         return w;
16538     }
16539 });
16540
16541
16542 // anything other than normal should be considered experimental
16543 Roo.form.Field.msgFx = {
16544     normal : {
16545         show: function(msgEl, f){
16546             msgEl.setDisplayed('block');
16547         },
16548
16549         hide : function(msgEl, f){
16550             msgEl.setDisplayed(false).update('');
16551         }
16552     },
16553
16554     slide : {
16555         show: function(msgEl, f){
16556             msgEl.slideIn('t', {stopFx:true});
16557         },
16558
16559         hide : function(msgEl, f){
16560             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16561         }
16562     },
16563
16564     slideRight : {
16565         show: function(msgEl, f){
16566             msgEl.fixDisplay();
16567             msgEl.alignTo(f.el, 'tl-tr');
16568             msgEl.slideIn('l', {stopFx:true});
16569         },
16570
16571         hide : function(msgEl, f){
16572             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16573         }
16574     }
16575 };/*
16576  * Based on:
16577  * Ext JS Library 1.1.1
16578  * Copyright(c) 2006-2007, Ext JS, LLC.
16579  *
16580  * Originally Released Under LGPL - original licence link has changed is not relivant.
16581  *
16582  * Fork - LGPL
16583  * <script type="text/javascript">
16584  */
16585  
16586
16587 /**
16588  * @class Roo.form.TextField
16589  * @extends Roo.form.Field
16590  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16591  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16592  * @constructor
16593  * Creates a new TextField
16594  * @param {Object} config Configuration options
16595  */
16596 Roo.form.TextField = function(config){
16597     Roo.form.TextField.superclass.constructor.call(this, config);
16598     this.addEvents({
16599         /**
16600          * @event autosize
16601          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16602          * according to the default logic, but this event provides a hook for the developer to apply additional
16603          * logic at runtime to resize the field if needed.
16604              * @param {Roo.form.Field} this This text field
16605              * @param {Number} width The new field width
16606              */
16607         autosize : true
16608     });
16609 };
16610
16611 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16612     /**
16613      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16614      */
16615     grow : false,
16616     /**
16617      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16618      */
16619     growMin : 30,
16620     /**
16621      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16622      */
16623     growMax : 800,
16624     /**
16625      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16626      */
16627     vtype : null,
16628     /**
16629      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16630      */
16631     maskRe : null,
16632     /**
16633      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16634      */
16635     disableKeyFilter : false,
16636     /**
16637      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16638      */
16639     allowBlank : true,
16640     /**
16641      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16642      */
16643     minLength : 0,
16644     /**
16645      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16646      */
16647     maxLength : Number.MAX_VALUE,
16648     /**
16649      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16650      */
16651     minLengthText : "The minimum length for this field is {0}",
16652     /**
16653      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16654      */
16655     maxLengthText : "The maximum length for this field is {0}",
16656     /**
16657      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16658      */
16659     selectOnFocus : false,
16660     /**
16661      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16662      */    
16663     allowLeadingSpace : false,
16664     /**
16665      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16666      */
16667     blankText : "This field is required",
16668     /**
16669      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16670      * If available, this function will be called only after the basic validators all return true, and will be passed the
16671      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16672      */
16673     validator : null,
16674     /**
16675      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16676      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16677      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16678      */
16679     regex : null,
16680     /**
16681      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16682      */
16683     regexText : "",
16684     /**
16685      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16686      */
16687     emptyText : null,
16688    
16689
16690     // private
16691     initEvents : function()
16692     {
16693         if (this.emptyText) {
16694             this.el.attr('placeholder', this.emptyText);
16695         }
16696         
16697         Roo.form.TextField.superclass.initEvents.call(this);
16698         if(this.validationEvent == 'keyup'){
16699             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16700             this.el.on('keyup', this.filterValidation, this);
16701         }
16702         else if(this.validationEvent !== false){
16703             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16704         }
16705         
16706         if(this.selectOnFocus){
16707             this.on("focus", this.preFocus, this);
16708         }
16709         if (!this.allowLeadingSpace) {
16710             this.on('blur', this.cleanLeadingSpace, this);
16711         }
16712         
16713         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16714             this.el.on("keypress", this.filterKeys, this);
16715         }
16716         if(this.grow){
16717             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16718             this.el.on("click", this.autoSize,  this);
16719         }
16720         if(this.el.is('input[type=password]') && Roo.isSafari){
16721             this.el.on('keydown', this.SafariOnKeyDown, this);
16722         }
16723     },
16724
16725     processValue : function(value){
16726         if(this.stripCharsRe){
16727             var newValue = value.replace(this.stripCharsRe, '');
16728             if(newValue !== value){
16729                 this.setRawValue(newValue);
16730                 return newValue;
16731             }
16732         }
16733         return value;
16734     },
16735
16736     filterValidation : function(e){
16737         if(!e.isNavKeyPress()){
16738             this.validationTask.delay(this.validationDelay);
16739         }
16740     },
16741
16742     // private
16743     onKeyUp : function(e){
16744         if(!e.isNavKeyPress()){
16745             this.autoSize();
16746         }
16747     },
16748     // private - clean the leading white space
16749     cleanLeadingSpace : function(e)
16750     {
16751         if ( this.inputType == 'file') {
16752             return;
16753         }
16754         
16755         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16756     },
16757     /**
16758      * Resets the current field value to the originally-loaded value and clears any validation messages.
16759      *  
16760      */
16761     reset : function(){
16762         Roo.form.TextField.superclass.reset.call(this);
16763        
16764     }, 
16765     // private
16766     preFocus : function(){
16767         
16768         if(this.selectOnFocus){
16769             this.el.dom.select();
16770         }
16771     },
16772
16773     
16774     // private
16775     filterKeys : function(e){
16776         var k = e.getKey();
16777         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16778             return;
16779         }
16780         var c = e.getCharCode(), cc = String.fromCharCode(c);
16781         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16782             return;
16783         }
16784         if(!this.maskRe.test(cc)){
16785             e.stopEvent();
16786         }
16787     },
16788
16789     setValue : function(v){
16790         
16791         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16792         
16793         this.autoSize();
16794     },
16795
16796     /**
16797      * Validates a value according to the field's validation rules and marks the field as invalid
16798      * if the validation fails
16799      * @param {Mixed} value The value to validate
16800      * @return {Boolean} True if the value is valid, else false
16801      */
16802     validateValue : function(value){
16803         if(value.length < 1)  { // if it's blank
16804              if(this.allowBlank){
16805                 this.clearInvalid();
16806                 return true;
16807              }else{
16808                 this.markInvalid(this.blankText);
16809                 return false;
16810              }
16811         }
16812         if(value.length < this.minLength){
16813             this.markInvalid(String.format(this.minLengthText, this.minLength));
16814             return false;
16815         }
16816         if(value.length > this.maxLength){
16817             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16818             return false;
16819         }
16820         if(this.vtype){
16821             var vt = Roo.form.VTypes;
16822             if(!vt[this.vtype](value, this)){
16823                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16824                 return false;
16825             }
16826         }
16827         if(typeof this.validator == "function"){
16828             var msg = this.validator(value);
16829             if(msg !== true){
16830                 this.markInvalid(msg);
16831                 return false;
16832             }
16833         }
16834         if(this.regex && !this.regex.test(value)){
16835             this.markInvalid(this.regexText);
16836             return false;
16837         }
16838         return true;
16839     },
16840
16841     /**
16842      * Selects text in this field
16843      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16844      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16845      */
16846     selectText : function(start, end){
16847         var v = this.getRawValue();
16848         if(v.length > 0){
16849             start = start === undefined ? 0 : start;
16850             end = end === undefined ? v.length : end;
16851             var d = this.el.dom;
16852             if(d.setSelectionRange){
16853                 d.setSelectionRange(start, end);
16854             }else if(d.createTextRange){
16855                 var range = d.createTextRange();
16856                 range.moveStart("character", start);
16857                 range.moveEnd("character", v.length-end);
16858                 range.select();
16859             }
16860         }
16861     },
16862
16863     /**
16864      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16865      * This only takes effect if grow = true, and fires the autosize event.
16866      */
16867     autoSize : function(){
16868         if(!this.grow || !this.rendered){
16869             return;
16870         }
16871         if(!this.metrics){
16872             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16873         }
16874         var el = this.el;
16875         var v = el.dom.value;
16876         var d = document.createElement('div');
16877         d.appendChild(document.createTextNode(v));
16878         v = d.innerHTML;
16879         d = null;
16880         v += "&#160;";
16881         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16882         this.el.setWidth(w);
16883         this.fireEvent("autosize", this, w);
16884     },
16885     
16886     // private
16887     SafariOnKeyDown : function(event)
16888     {
16889         // this is a workaround for a password hang bug on chrome/ webkit.
16890         
16891         var isSelectAll = false;
16892         
16893         if(this.el.dom.selectionEnd > 0){
16894             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16895         }
16896         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16897             event.preventDefault();
16898             this.setValue('');
16899             return;
16900         }
16901         
16902         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16903             
16904             event.preventDefault();
16905             // this is very hacky as keydown always get's upper case.
16906             
16907             var cc = String.fromCharCode(event.getCharCode());
16908             
16909             
16910             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16911             
16912         }
16913         
16914         
16915     }
16916 });/*
16917  * Based on:
16918  * Ext JS Library 1.1.1
16919  * Copyright(c) 2006-2007, Ext JS, LLC.
16920  *
16921  * Originally Released Under LGPL - original licence link has changed is not relivant.
16922  *
16923  * Fork - LGPL
16924  * <script type="text/javascript">
16925  */
16926  
16927 /**
16928  * @class Roo.form.Hidden
16929  * @extends Roo.form.TextField
16930  * Simple Hidden element used on forms 
16931  * 
16932  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16933  * 
16934  * @constructor
16935  * Creates a new Hidden form element.
16936  * @param {Object} config Configuration options
16937  */
16938
16939
16940
16941 // easy hidden field...
16942 Roo.form.Hidden = function(config){
16943     Roo.form.Hidden.superclass.constructor.call(this, config);
16944 };
16945   
16946 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16947     fieldLabel:      '',
16948     inputType:      'hidden',
16949     width:          50,
16950     allowBlank:     true,
16951     labelSeparator: '',
16952     hidden:         true,
16953     itemCls :       'x-form-item-display-none'
16954
16955
16956 });
16957
16958
16959 /*
16960  * Based on:
16961  * Ext JS Library 1.1.1
16962  * Copyright(c) 2006-2007, Ext JS, LLC.
16963  *
16964  * Originally Released Under LGPL - original licence link has changed is not relivant.
16965  *
16966  * Fork - LGPL
16967  * <script type="text/javascript">
16968  */
16969  
16970 /**
16971  * @class Roo.form.TriggerField
16972  * @extends Roo.form.TextField
16973  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16974  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16975  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16976  * for which you can provide a custom implementation.  For example:
16977  * <pre><code>
16978 var trigger = new Roo.form.TriggerField();
16979 trigger.onTriggerClick = myTriggerFn;
16980 trigger.applyTo('my-field');
16981 </code></pre>
16982  *
16983  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16984  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16985  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16986  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16987  * @constructor
16988  * Create a new TriggerField.
16989  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16990  * to the base TextField)
16991  */
16992 Roo.form.TriggerField = function(config){
16993     this.mimicing = false;
16994     Roo.form.TriggerField.superclass.constructor.call(this, config);
16995 };
16996
16997 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16998     /**
16999      * @cfg {String} triggerClass A CSS class to apply to the trigger
17000      */
17001     /**
17002      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17003      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17004      */
17005     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17006     /**
17007      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17008      */
17009     hideTrigger:false,
17010
17011     /** @cfg {Boolean} grow @hide */
17012     /** @cfg {Number} growMin @hide */
17013     /** @cfg {Number} growMax @hide */
17014
17015     /**
17016      * @hide 
17017      * @method
17018      */
17019     autoSize: Roo.emptyFn,
17020     // private
17021     monitorTab : true,
17022     // private
17023     deferHeight : true,
17024
17025     
17026     actionMode : 'wrap',
17027     // private
17028     onResize : function(w, h){
17029         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17030         if(typeof w == 'number'){
17031             var x = w - this.trigger.getWidth();
17032             this.el.setWidth(this.adjustWidth('input', x));
17033             this.trigger.setStyle('left', x+'px');
17034         }
17035     },
17036
17037     // private
17038     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17039
17040     // private
17041     getResizeEl : function(){
17042         return this.wrap;
17043     },
17044
17045     // private
17046     getPositionEl : function(){
17047         return this.wrap;
17048     },
17049
17050     // private
17051     alignErrorIcon : function(){
17052         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17053     },
17054
17055     // private
17056     onRender : function(ct, position){
17057         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17058         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17059         this.trigger = this.wrap.createChild(this.triggerConfig ||
17060                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17061         if(this.hideTrigger){
17062             this.trigger.setDisplayed(false);
17063         }
17064         this.initTrigger();
17065         if(!this.width){
17066             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17067         }
17068     },
17069
17070     // private
17071     initTrigger : function(){
17072         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17073         this.trigger.addClassOnOver('x-form-trigger-over');
17074         this.trigger.addClassOnClick('x-form-trigger-click');
17075     },
17076
17077     // private
17078     onDestroy : function(){
17079         if(this.trigger){
17080             this.trigger.removeAllListeners();
17081             this.trigger.remove();
17082         }
17083         if(this.wrap){
17084             this.wrap.remove();
17085         }
17086         Roo.form.TriggerField.superclass.onDestroy.call(this);
17087     },
17088
17089     // private
17090     onFocus : function(){
17091         Roo.form.TriggerField.superclass.onFocus.call(this);
17092         if(!this.mimicing){
17093             this.wrap.addClass('x-trigger-wrap-focus');
17094             this.mimicing = true;
17095             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17096             if(this.monitorTab){
17097                 this.el.on("keydown", this.checkTab, this);
17098             }
17099         }
17100     },
17101
17102     // private
17103     checkTab : function(e){
17104         if(e.getKey() == e.TAB){
17105             this.triggerBlur();
17106         }
17107     },
17108
17109     // private
17110     onBlur : function(){
17111         // do nothing
17112     },
17113
17114     // private
17115     mimicBlur : function(e, t){
17116         if(!this.wrap.contains(t) && this.validateBlur()){
17117             this.triggerBlur();
17118         }
17119     },
17120
17121     // private
17122     triggerBlur : function(){
17123         this.mimicing = false;
17124         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17125         if(this.monitorTab){
17126             this.el.un("keydown", this.checkTab, this);
17127         }
17128         this.wrap.removeClass('x-trigger-wrap-focus');
17129         Roo.form.TriggerField.superclass.onBlur.call(this);
17130     },
17131
17132     // private
17133     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17134     validateBlur : function(e, t){
17135         return true;
17136     },
17137
17138     // private
17139     onDisable : function(){
17140         Roo.form.TriggerField.superclass.onDisable.call(this);
17141         if(this.wrap){
17142             this.wrap.addClass('x-item-disabled');
17143         }
17144     },
17145
17146     // private
17147     onEnable : function(){
17148         Roo.form.TriggerField.superclass.onEnable.call(this);
17149         if(this.wrap){
17150             this.wrap.removeClass('x-item-disabled');
17151         }
17152     },
17153
17154     // private
17155     onShow : function(){
17156         var ae = this.getActionEl();
17157         
17158         if(ae){
17159             ae.dom.style.display = '';
17160             ae.dom.style.visibility = 'visible';
17161         }
17162     },
17163
17164     // private
17165     
17166     onHide : function(){
17167         var ae = this.getActionEl();
17168         ae.dom.style.display = 'none';
17169     },
17170
17171     /**
17172      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17173      * by an implementing function.
17174      * @method
17175      * @param {EventObject} e
17176      */
17177     onTriggerClick : Roo.emptyFn
17178 });
17179
17180 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17181 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17182 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17183 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17184     initComponent : function(){
17185         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17186
17187         this.triggerConfig = {
17188             tag:'span', cls:'x-form-twin-triggers', cn:[
17189             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17190             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17191         ]};
17192     },
17193
17194     getTrigger : function(index){
17195         return this.triggers[index];
17196     },
17197
17198     initTrigger : function(){
17199         var ts = this.trigger.select('.x-form-trigger', true);
17200         this.wrap.setStyle('overflow', 'hidden');
17201         var triggerField = this;
17202         ts.each(function(t, all, index){
17203             t.hide = function(){
17204                 var w = triggerField.wrap.getWidth();
17205                 this.dom.style.display = 'none';
17206                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17207             };
17208             t.show = function(){
17209                 var w = triggerField.wrap.getWidth();
17210                 this.dom.style.display = '';
17211                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17212             };
17213             var triggerIndex = 'Trigger'+(index+1);
17214
17215             if(this['hide'+triggerIndex]){
17216                 t.dom.style.display = 'none';
17217             }
17218             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17219             t.addClassOnOver('x-form-trigger-over');
17220             t.addClassOnClick('x-form-trigger-click');
17221         }, this);
17222         this.triggers = ts.elements;
17223     },
17224
17225     onTrigger1Click : Roo.emptyFn,
17226     onTrigger2Click : Roo.emptyFn
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237  
17238 /**
17239  * @class Roo.form.TextArea
17240  * @extends Roo.form.TextField
17241  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17242  * support for auto-sizing.
17243  * @constructor
17244  * Creates a new TextArea
17245  * @param {Object} config Configuration options
17246  */
17247 Roo.form.TextArea = function(config){
17248     Roo.form.TextArea.superclass.constructor.call(this, config);
17249     // these are provided exchanges for backwards compat
17250     // minHeight/maxHeight were replaced by growMin/growMax to be
17251     // compatible with TextField growing config values
17252     if(this.minHeight !== undefined){
17253         this.growMin = this.minHeight;
17254     }
17255     if(this.maxHeight !== undefined){
17256         this.growMax = this.maxHeight;
17257     }
17258 };
17259
17260 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17261     /**
17262      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17263      */
17264     growMin : 60,
17265     /**
17266      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17267      */
17268     growMax: 1000,
17269     /**
17270      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17271      * in the field (equivalent to setting overflow: hidden, defaults to false)
17272      */
17273     preventScrollbars: false,
17274     /**
17275      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17276      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17277      */
17278
17279     // private
17280     onRender : function(ct, position){
17281         if(!this.el){
17282             this.defaultAutoCreate = {
17283                 tag: "textarea",
17284                 style:"width:300px;height:60px;",
17285                 autocomplete: "new-password"
17286             };
17287         }
17288         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17289         if(this.grow){
17290             this.textSizeEl = Roo.DomHelper.append(document.body, {
17291                 tag: "pre", cls: "x-form-grow-sizer"
17292             });
17293             if(this.preventScrollbars){
17294                 this.el.setStyle("overflow", "hidden");
17295             }
17296             this.el.setHeight(this.growMin);
17297         }
17298     },
17299
17300     onDestroy : function(){
17301         if(this.textSizeEl){
17302             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17303         }
17304         Roo.form.TextArea.superclass.onDestroy.call(this);
17305     },
17306
17307     // private
17308     onKeyUp : function(e){
17309         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17310             this.autoSize();
17311         }
17312     },
17313
17314     /**
17315      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17316      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17317      */
17318     autoSize : function(){
17319         if(!this.grow || !this.textSizeEl){
17320             return;
17321         }
17322         var el = this.el;
17323         var v = el.dom.value;
17324         var ts = this.textSizeEl;
17325
17326         ts.innerHTML = '';
17327         ts.appendChild(document.createTextNode(v));
17328         v = ts.innerHTML;
17329
17330         Roo.fly(ts).setWidth(this.el.getWidth());
17331         if(v.length < 1){
17332             v = "&#160;&#160;";
17333         }else{
17334             if(Roo.isIE){
17335                 v = v.replace(/\n/g, '<p>&#160;</p>');
17336             }
17337             v += "&#160;\n&#160;";
17338         }
17339         ts.innerHTML = v;
17340         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17341         if(h != this.lastHeight){
17342             this.lastHeight = h;
17343             this.el.setHeight(h);
17344             this.fireEvent("autosize", this, h);
17345         }
17346     }
17347 });/*
17348  * Based on:
17349  * Ext JS Library 1.1.1
17350  * Copyright(c) 2006-2007, Ext JS, LLC.
17351  *
17352  * Originally Released Under LGPL - original licence link has changed is not relivant.
17353  *
17354  * Fork - LGPL
17355  * <script type="text/javascript">
17356  */
17357  
17358
17359 /**
17360  * @class Roo.form.NumberField
17361  * @extends Roo.form.TextField
17362  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17363  * @constructor
17364  * Creates a new NumberField
17365  * @param {Object} config Configuration options
17366  */
17367 Roo.form.NumberField = function(config){
17368     Roo.form.NumberField.superclass.constructor.call(this, config);
17369 };
17370
17371 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17372     /**
17373      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17374      */
17375     fieldClass: "x-form-field x-form-num-field",
17376     /**
17377      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17378      */
17379     allowDecimals : true,
17380     /**
17381      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17382      */
17383     decimalSeparator : ".",
17384     /**
17385      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17386      */
17387     decimalPrecision : 2,
17388     /**
17389      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17390      */
17391     allowNegative : true,
17392     /**
17393      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17394      */
17395     minValue : Number.NEGATIVE_INFINITY,
17396     /**
17397      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17398      */
17399     maxValue : Number.MAX_VALUE,
17400     /**
17401      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17402      */
17403     minText : "The minimum value for this field is {0}",
17404     /**
17405      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17406      */
17407     maxText : "The maximum value for this field is {0}",
17408     /**
17409      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17410      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17411      */
17412     nanText : "{0} is not a valid number",
17413
17414     // private
17415     initEvents : function(){
17416         Roo.form.NumberField.superclass.initEvents.call(this);
17417         var allowed = "0123456789";
17418         if(this.allowDecimals){
17419             allowed += this.decimalSeparator;
17420         }
17421         if(this.allowNegative){
17422             allowed += "-";
17423         }
17424         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17425         var keyPress = function(e){
17426             var k = e.getKey();
17427             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17428                 return;
17429             }
17430             var c = e.getCharCode();
17431             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17432                 e.stopEvent();
17433             }
17434         };
17435         this.el.on("keypress", keyPress, this);
17436     },
17437
17438     // private
17439     validateValue : function(value){
17440         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17441             return false;
17442         }
17443         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17444              return true;
17445         }
17446         var num = this.parseValue(value);
17447         if(isNaN(num)){
17448             this.markInvalid(String.format(this.nanText, value));
17449             return false;
17450         }
17451         if(num < this.minValue){
17452             this.markInvalid(String.format(this.minText, this.minValue));
17453             return false;
17454         }
17455         if(num > this.maxValue){
17456             this.markInvalid(String.format(this.maxText, this.maxValue));
17457             return false;
17458         }
17459         return true;
17460     },
17461
17462     getValue : function(){
17463         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17464     },
17465
17466     // private
17467     parseValue : function(value){
17468         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17469         return isNaN(value) ? '' : value;
17470     },
17471
17472     // private
17473     fixPrecision : function(value){
17474         var nan = isNaN(value);
17475         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17476             return nan ? '' : value;
17477         }
17478         return parseFloat(value).toFixed(this.decimalPrecision);
17479     },
17480
17481     setValue : function(v){
17482         v = this.fixPrecision(v);
17483         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17484     },
17485
17486     // private
17487     decimalPrecisionFcn : function(v){
17488         return Math.floor(v);
17489     },
17490
17491     beforeBlur : function(){
17492         var v = this.parseValue(this.getRawValue());
17493         if(v){
17494             this.setValue(v);
17495         }
17496     }
17497 });/*
17498  * Based on:
17499  * Ext JS Library 1.1.1
17500  * Copyright(c) 2006-2007, Ext JS, LLC.
17501  *
17502  * Originally Released Under LGPL - original licence link has changed is not relivant.
17503  *
17504  * Fork - LGPL
17505  * <script type="text/javascript">
17506  */
17507  
17508 /**
17509  * @class Roo.form.DateField
17510  * @extends Roo.form.TriggerField
17511  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17512 * @constructor
17513 * Create a new DateField
17514 * @param {Object} config
17515  */
17516 Roo.form.DateField = function(config)
17517 {
17518     Roo.form.DateField.superclass.constructor.call(this, config);
17519     
17520       this.addEvents({
17521          
17522         /**
17523          * @event select
17524          * Fires when a date is selected
17525              * @param {Roo.form.DateField} combo This combo box
17526              * @param {Date} date The date selected
17527              */
17528         'select' : true
17529          
17530     });
17531     
17532     
17533     if(typeof this.minValue == "string") {
17534         this.minValue = this.parseDate(this.minValue);
17535     }
17536     if(typeof this.maxValue == "string") {
17537         this.maxValue = this.parseDate(this.maxValue);
17538     }
17539     this.ddMatch = null;
17540     if(this.disabledDates){
17541         var dd = this.disabledDates;
17542         var re = "(?:";
17543         for(var i = 0; i < dd.length; i++){
17544             re += dd[i];
17545             if(i != dd.length-1) {
17546                 re += "|";
17547             }
17548         }
17549         this.ddMatch = new RegExp(re + ")");
17550     }
17551 };
17552
17553 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17554     /**
17555      * @cfg {String} format
17556      * The default date format string which can be overriden for localization support.  The format must be
17557      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17558      */
17559     format : "m/d/y",
17560     /**
17561      * @cfg {String} altFormats
17562      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17563      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17564      */
17565     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17566     /**
17567      * @cfg {Array} disabledDays
17568      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17569      */
17570     disabledDays : null,
17571     /**
17572      * @cfg {String} disabledDaysText
17573      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17574      */
17575     disabledDaysText : "Disabled",
17576     /**
17577      * @cfg {Array} disabledDates
17578      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17579      * expression so they are very powerful. Some examples:
17580      * <ul>
17581      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17582      * <li>["03/08", "09/16"] would disable those days for every year</li>
17583      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17584      * <li>["03/../2006"] would disable every day in March 2006</li>
17585      * <li>["^03"] would disable every day in every March</li>
17586      * </ul>
17587      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17588      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17589      */
17590     disabledDates : null,
17591     /**
17592      * @cfg {String} disabledDatesText
17593      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17594      */
17595     disabledDatesText : "Disabled",
17596     /**
17597      * @cfg {Date/String} minValue
17598      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17599      * valid format (defaults to null).
17600      */
17601     minValue : null,
17602     /**
17603      * @cfg {Date/String} maxValue
17604      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17605      * valid format (defaults to null).
17606      */
17607     maxValue : null,
17608     /**
17609      * @cfg {String} minText
17610      * The error text to display when the date in the cell is before minValue (defaults to
17611      * 'The date in this field must be after {minValue}').
17612      */
17613     minText : "The date in this field must be equal to or after {0}",
17614     /**
17615      * @cfg {String} maxText
17616      * The error text to display when the date in the cell is after maxValue (defaults to
17617      * 'The date in this field must be before {maxValue}').
17618      */
17619     maxText : "The date in this field must be equal to or before {0}",
17620     /**
17621      * @cfg {String} invalidText
17622      * The error text to display when the date in the field is invalid (defaults to
17623      * '{value} is not a valid date - it must be in the format {format}').
17624      */
17625     invalidText : "{0} is not a valid date - it must be in the format {1}",
17626     /**
17627      * @cfg {String} triggerClass
17628      * An additional CSS class used to style the trigger button.  The trigger will always get the
17629      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17630      * which displays a calendar icon).
17631      */
17632     triggerClass : 'x-form-date-trigger',
17633     
17634
17635     /**
17636      * @cfg {Boolean} useIso
17637      * if enabled, then the date field will use a hidden field to store the 
17638      * real value as iso formated date. default (false)
17639      */ 
17640     useIso : false,
17641     /**
17642      * @cfg {String/Object} autoCreate
17643      * A DomHelper element spec, or true for a default element spec (defaults to
17644      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17645      */ 
17646     // private
17647     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17648     
17649     // private
17650     hiddenField: false,
17651     
17652     onRender : function(ct, position)
17653     {
17654         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17655         if (this.useIso) {
17656             //this.el.dom.removeAttribute('name'); 
17657             Roo.log("Changing name?");
17658             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17659             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17660                     'before', true);
17661             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17662             // prevent input submission
17663             this.hiddenName = this.name;
17664         }
17665             
17666             
17667     },
17668     
17669     // private
17670     validateValue : function(value)
17671     {
17672         value = this.formatDate(value);
17673         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17674             Roo.log('super failed');
17675             return false;
17676         }
17677         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17678              return true;
17679         }
17680         var svalue = value;
17681         value = this.parseDate(value);
17682         if(!value){
17683             Roo.log('parse date failed' + svalue);
17684             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17685             return false;
17686         }
17687         var time = value.getTime();
17688         if(this.minValue && time < this.minValue.getTime()){
17689             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17690             return false;
17691         }
17692         if(this.maxValue && time > this.maxValue.getTime()){
17693             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17694             return false;
17695         }
17696         if(this.disabledDays){
17697             var day = value.getDay();
17698             for(var i = 0; i < this.disabledDays.length; i++) {
17699                 if(day === this.disabledDays[i]){
17700                     this.markInvalid(this.disabledDaysText);
17701                     return false;
17702                 }
17703             }
17704         }
17705         var fvalue = this.formatDate(value);
17706         if(this.ddMatch && this.ddMatch.test(fvalue)){
17707             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17708             return false;
17709         }
17710         return true;
17711     },
17712
17713     // private
17714     // Provides logic to override the default TriggerField.validateBlur which just returns true
17715     validateBlur : function(){
17716         return !this.menu || !this.menu.isVisible();
17717     },
17718     
17719     getName: function()
17720     {
17721         // returns hidden if it's set..
17722         if (!this.rendered) {return ''};
17723         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17724         
17725     },
17726
17727     /**
17728      * Returns the current date value of the date field.
17729      * @return {Date} The date value
17730      */
17731     getValue : function(){
17732         
17733         return  this.hiddenField ?
17734                 this.hiddenField.value :
17735                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17736     },
17737
17738     /**
17739      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17740      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17741      * (the default format used is "m/d/y").
17742      * <br />Usage:
17743      * <pre><code>
17744 //All of these calls set the same date value (May 4, 2006)
17745
17746 //Pass a date object:
17747 var dt = new Date('5/4/06');
17748 dateField.setValue(dt);
17749
17750 //Pass a date string (default format):
17751 dateField.setValue('5/4/06');
17752
17753 //Pass a date string (custom format):
17754 dateField.format = 'Y-m-d';
17755 dateField.setValue('2006-5-4');
17756 </code></pre>
17757      * @param {String/Date} date The date or valid date string
17758      */
17759     setValue : function(date){
17760         if (this.hiddenField) {
17761             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17762         }
17763         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17764         // make sure the value field is always stored as a date..
17765         this.value = this.parseDate(date);
17766         
17767         
17768     },
17769
17770     // private
17771     parseDate : function(value){
17772         if(!value || value instanceof Date){
17773             return value;
17774         }
17775         var v = Date.parseDate(value, this.format);
17776          if (!v && this.useIso) {
17777             v = Date.parseDate(value, 'Y-m-d');
17778         }
17779         if(!v && this.altFormats){
17780             if(!this.altFormatsArray){
17781                 this.altFormatsArray = this.altFormats.split("|");
17782             }
17783             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17784                 v = Date.parseDate(value, this.altFormatsArray[i]);
17785             }
17786         }
17787                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17788                         v = '';
17789                 }
17790         return v;
17791     },
17792
17793     // private
17794     formatDate : function(date, fmt){
17795         return (!date || !(date instanceof Date)) ?
17796                date : date.dateFormat(fmt || this.format);
17797     },
17798
17799     // private
17800     menuListeners : {
17801         select: function(m, d){
17802             
17803             this.setValue(d);
17804             this.fireEvent('select', this, d);
17805         },
17806         show : function(){ // retain focus styling
17807             this.onFocus();
17808         },
17809         hide : function(){
17810             this.focus.defer(10, this);
17811             var ml = this.menuListeners;
17812             this.menu.un("select", ml.select,  this);
17813             this.menu.un("show", ml.show,  this);
17814             this.menu.un("hide", ml.hide,  this);
17815         }
17816     },
17817
17818     // private
17819     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17820     onTriggerClick : function(){
17821         if(this.disabled){
17822             return;
17823         }
17824         if(this.menu == null){
17825             this.menu = new Roo.menu.DateMenu();
17826         }
17827         Roo.apply(this.menu.picker,  {
17828             showClear: this.allowBlank,
17829             minDate : this.minValue,
17830             maxDate : this.maxValue,
17831             disabledDatesRE : this.ddMatch,
17832             disabledDatesText : this.disabledDatesText,
17833             disabledDays : this.disabledDays,
17834             disabledDaysText : this.disabledDaysText,
17835             format : this.useIso ? 'Y-m-d' : this.format,
17836             minText : String.format(this.minText, this.formatDate(this.minValue)),
17837             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17838         });
17839         this.menu.on(Roo.apply({}, this.menuListeners, {
17840             scope:this
17841         }));
17842         this.menu.picker.setValue(this.getValue() || new Date());
17843         this.menu.show(this.el, "tl-bl?");
17844     },
17845
17846     beforeBlur : function(){
17847         var v = this.parseDate(this.getRawValue());
17848         if(v){
17849             this.setValue(v);
17850         }
17851     },
17852
17853     /*@
17854      * overide
17855      * 
17856      */
17857     isDirty : function() {
17858         if(this.disabled) {
17859             return false;
17860         }
17861         
17862         if(typeof(this.startValue) === 'undefined'){
17863             return false;
17864         }
17865         
17866         return String(this.getValue()) !== String(this.startValue);
17867         
17868     },
17869     // @overide
17870     cleanLeadingSpace : function(e)
17871     {
17872        return;
17873     }
17874     
17875 });/*
17876  * Based on:
17877  * Ext JS Library 1.1.1
17878  * Copyright(c) 2006-2007, Ext JS, LLC.
17879  *
17880  * Originally Released Under LGPL - original licence link has changed is not relivant.
17881  *
17882  * Fork - LGPL
17883  * <script type="text/javascript">
17884  */
17885  
17886 /**
17887  * @class Roo.form.MonthField
17888  * @extends Roo.form.TriggerField
17889  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17890 * @constructor
17891 * Create a new MonthField
17892 * @param {Object} config
17893  */
17894 Roo.form.MonthField = function(config){
17895     
17896     Roo.form.MonthField.superclass.constructor.call(this, config);
17897     
17898       this.addEvents({
17899          
17900         /**
17901          * @event select
17902          * Fires when a date is selected
17903              * @param {Roo.form.MonthFieeld} combo This combo box
17904              * @param {Date} date The date selected
17905              */
17906         'select' : true
17907          
17908     });
17909     
17910     
17911     if(typeof this.minValue == "string") {
17912         this.minValue = this.parseDate(this.minValue);
17913     }
17914     if(typeof this.maxValue == "string") {
17915         this.maxValue = this.parseDate(this.maxValue);
17916     }
17917     this.ddMatch = null;
17918     if(this.disabledDates){
17919         var dd = this.disabledDates;
17920         var re = "(?:";
17921         for(var i = 0; i < dd.length; i++){
17922             re += dd[i];
17923             if(i != dd.length-1) {
17924                 re += "|";
17925             }
17926         }
17927         this.ddMatch = new RegExp(re + ")");
17928     }
17929 };
17930
17931 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17932     /**
17933      * @cfg {String} format
17934      * The default date format string which can be overriden for localization support.  The format must be
17935      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17936      */
17937     format : "M Y",
17938     /**
17939      * @cfg {String} altFormats
17940      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17941      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17942      */
17943     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17944     /**
17945      * @cfg {Array} disabledDays
17946      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17947      */
17948     disabledDays : [0,1,2,3,4,5,6],
17949     /**
17950      * @cfg {String} disabledDaysText
17951      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17952      */
17953     disabledDaysText : "Disabled",
17954     /**
17955      * @cfg {Array} disabledDates
17956      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17957      * expression so they are very powerful. Some examples:
17958      * <ul>
17959      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17960      * <li>["03/08", "09/16"] would disable those days for every year</li>
17961      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17962      * <li>["03/../2006"] would disable every day in March 2006</li>
17963      * <li>["^03"] would disable every day in every March</li>
17964      * </ul>
17965      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17966      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17967      */
17968     disabledDates : null,
17969     /**
17970      * @cfg {String} disabledDatesText
17971      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17972      */
17973     disabledDatesText : "Disabled",
17974     /**
17975      * @cfg {Date/String} minValue
17976      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17977      * valid format (defaults to null).
17978      */
17979     minValue : null,
17980     /**
17981      * @cfg {Date/String} maxValue
17982      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17983      * valid format (defaults to null).
17984      */
17985     maxValue : null,
17986     /**
17987      * @cfg {String} minText
17988      * The error text to display when the date in the cell is before minValue (defaults to
17989      * 'The date in this field must be after {minValue}').
17990      */
17991     minText : "The date in this field must be equal to or after {0}",
17992     /**
17993      * @cfg {String} maxTextf
17994      * The error text to display when the date in the cell is after maxValue (defaults to
17995      * 'The date in this field must be before {maxValue}').
17996      */
17997     maxText : "The date in this field must be equal to or before {0}",
17998     /**
17999      * @cfg {String} invalidText
18000      * The error text to display when the date in the field is invalid (defaults to
18001      * '{value} is not a valid date - it must be in the format {format}').
18002      */
18003     invalidText : "{0} is not a valid date - it must be in the format {1}",
18004     /**
18005      * @cfg {String} triggerClass
18006      * An additional CSS class used to style the trigger button.  The trigger will always get the
18007      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18008      * which displays a calendar icon).
18009      */
18010     triggerClass : 'x-form-date-trigger',
18011     
18012
18013     /**
18014      * @cfg {Boolean} useIso
18015      * if enabled, then the date field will use a hidden field to store the 
18016      * real value as iso formated date. default (true)
18017      */ 
18018     useIso : true,
18019     /**
18020      * @cfg {String/Object} autoCreate
18021      * A DomHelper element spec, or true for a default element spec (defaults to
18022      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18023      */ 
18024     // private
18025     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18026     
18027     // private
18028     hiddenField: false,
18029     
18030     hideMonthPicker : false,
18031     
18032     onRender : function(ct, position)
18033     {
18034         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18035         if (this.useIso) {
18036             this.el.dom.removeAttribute('name'); 
18037             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18038                     'before', true);
18039             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18040             // prevent input submission
18041             this.hiddenName = this.name;
18042         }
18043             
18044             
18045     },
18046     
18047     // private
18048     validateValue : function(value)
18049     {
18050         value = this.formatDate(value);
18051         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18052             return false;
18053         }
18054         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18055              return true;
18056         }
18057         var svalue = value;
18058         value = this.parseDate(value);
18059         if(!value){
18060             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18061             return false;
18062         }
18063         var time = value.getTime();
18064         if(this.minValue && time < this.minValue.getTime()){
18065             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18066             return false;
18067         }
18068         if(this.maxValue && time > this.maxValue.getTime()){
18069             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18070             return false;
18071         }
18072         /*if(this.disabledDays){
18073             var day = value.getDay();
18074             for(var i = 0; i < this.disabledDays.length; i++) {
18075                 if(day === this.disabledDays[i]){
18076                     this.markInvalid(this.disabledDaysText);
18077                     return false;
18078                 }
18079             }
18080         }
18081         */
18082         var fvalue = this.formatDate(value);
18083         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18084             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18085             return false;
18086         }
18087         */
18088         return true;
18089     },
18090
18091     // private
18092     // Provides logic to override the default TriggerField.validateBlur which just returns true
18093     validateBlur : function(){
18094         return !this.menu || !this.menu.isVisible();
18095     },
18096
18097     /**
18098      * Returns the current date value of the date field.
18099      * @return {Date} The date value
18100      */
18101     getValue : function(){
18102         
18103         
18104         
18105         return  this.hiddenField ?
18106                 this.hiddenField.value :
18107                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18108     },
18109
18110     /**
18111      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18112      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18113      * (the default format used is "m/d/y").
18114      * <br />Usage:
18115      * <pre><code>
18116 //All of these calls set the same date value (May 4, 2006)
18117
18118 //Pass a date object:
18119 var dt = new Date('5/4/06');
18120 monthField.setValue(dt);
18121
18122 //Pass a date string (default format):
18123 monthField.setValue('5/4/06');
18124
18125 //Pass a date string (custom format):
18126 monthField.format = 'Y-m-d';
18127 monthField.setValue('2006-5-4');
18128 </code></pre>
18129      * @param {String/Date} date The date or valid date string
18130      */
18131     setValue : function(date){
18132         Roo.log('month setValue' + date);
18133         // can only be first of month..
18134         
18135         var val = this.parseDate(date);
18136         
18137         if (this.hiddenField) {
18138             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18139         }
18140         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18141         this.value = this.parseDate(date);
18142     },
18143
18144     // private
18145     parseDate : function(value){
18146         if(!value || value instanceof Date){
18147             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18148             return value;
18149         }
18150         var v = Date.parseDate(value, this.format);
18151         if (!v && this.useIso) {
18152             v = Date.parseDate(value, 'Y-m-d');
18153         }
18154         if (v) {
18155             // 
18156             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18157         }
18158         
18159         
18160         if(!v && this.altFormats){
18161             if(!this.altFormatsArray){
18162                 this.altFormatsArray = this.altFormats.split("|");
18163             }
18164             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18165                 v = Date.parseDate(value, this.altFormatsArray[i]);
18166             }
18167         }
18168         return v;
18169     },
18170
18171     // private
18172     formatDate : function(date, fmt){
18173         return (!date || !(date instanceof Date)) ?
18174                date : date.dateFormat(fmt || this.format);
18175     },
18176
18177     // private
18178     menuListeners : {
18179         select: function(m, d){
18180             this.setValue(d);
18181             this.fireEvent('select', this, d);
18182         },
18183         show : function(){ // retain focus styling
18184             this.onFocus();
18185         },
18186         hide : function(){
18187             this.focus.defer(10, this);
18188             var ml = this.menuListeners;
18189             this.menu.un("select", ml.select,  this);
18190             this.menu.un("show", ml.show,  this);
18191             this.menu.un("hide", ml.hide,  this);
18192         }
18193     },
18194     // private
18195     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18196     onTriggerClick : function(){
18197         if(this.disabled){
18198             return;
18199         }
18200         if(this.menu == null){
18201             this.menu = new Roo.menu.DateMenu();
18202            
18203         }
18204         
18205         Roo.apply(this.menu.picker,  {
18206             
18207             showClear: this.allowBlank,
18208             minDate : this.minValue,
18209             maxDate : this.maxValue,
18210             disabledDatesRE : this.ddMatch,
18211             disabledDatesText : this.disabledDatesText,
18212             
18213             format : this.useIso ? 'Y-m-d' : this.format,
18214             minText : String.format(this.minText, this.formatDate(this.minValue)),
18215             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18216             
18217         });
18218          this.menu.on(Roo.apply({}, this.menuListeners, {
18219             scope:this
18220         }));
18221        
18222         
18223         var m = this.menu;
18224         var p = m.picker;
18225         
18226         // hide month picker get's called when we called by 'before hide';
18227         
18228         var ignorehide = true;
18229         p.hideMonthPicker  = function(disableAnim){
18230             if (ignorehide) {
18231                 return;
18232             }
18233              if(this.monthPicker){
18234                 Roo.log("hideMonthPicker called");
18235                 if(disableAnim === true){
18236                     this.monthPicker.hide();
18237                 }else{
18238                     this.monthPicker.slideOut('t', {duration:.2});
18239                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18240                     p.fireEvent("select", this, this.value);
18241                     m.hide();
18242                 }
18243             }
18244         }
18245         
18246         Roo.log('picker set value');
18247         Roo.log(this.getValue());
18248         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18249         m.show(this.el, 'tl-bl?');
18250         ignorehide  = false;
18251         // this will trigger hideMonthPicker..
18252         
18253         
18254         // hidden the day picker
18255         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18256         
18257         
18258         
18259       
18260         
18261         p.showMonthPicker.defer(100, p);
18262     
18263         
18264        
18265     },
18266
18267     beforeBlur : function(){
18268         var v = this.parseDate(this.getRawValue());
18269         if(v){
18270             this.setValue(v);
18271         }
18272     }
18273
18274     /** @cfg {Boolean} grow @hide */
18275     /** @cfg {Number} growMin @hide */
18276     /** @cfg {Number} growMax @hide */
18277     /**
18278      * @hide
18279      * @method autoSize
18280      */
18281 });/*
18282  * Based on:
18283  * Ext JS Library 1.1.1
18284  * Copyright(c) 2006-2007, Ext JS, LLC.
18285  *
18286  * Originally Released Under LGPL - original licence link has changed is not relivant.
18287  *
18288  * Fork - LGPL
18289  * <script type="text/javascript">
18290  */
18291  
18292
18293 /**
18294  * @class Roo.form.ComboBox
18295  * @extends Roo.form.TriggerField
18296  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18297  * @constructor
18298  * Create a new ComboBox.
18299  * @param {Object} config Configuration options
18300  */
18301 Roo.form.ComboBox = function(config){
18302     Roo.form.ComboBox.superclass.constructor.call(this, config);
18303     this.addEvents({
18304         /**
18305          * @event expand
18306          * Fires when the dropdown list is expanded
18307              * @param {Roo.form.ComboBox} combo This combo box
18308              */
18309         'expand' : true,
18310         /**
18311          * @event collapse
18312          * Fires when the dropdown list is collapsed
18313              * @param {Roo.form.ComboBox} combo This combo box
18314              */
18315         'collapse' : true,
18316         /**
18317          * @event beforeselect
18318          * Fires before a list item is selected. Return false to cancel the selection.
18319              * @param {Roo.form.ComboBox} combo This combo box
18320              * @param {Roo.data.Record} record The data record returned from the underlying store
18321              * @param {Number} index The index of the selected item in the dropdown list
18322              */
18323         'beforeselect' : true,
18324         /**
18325          * @event select
18326          * Fires when a list item is selected
18327              * @param {Roo.form.ComboBox} combo This combo box
18328              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18329              * @param {Number} index The index of the selected item in the dropdown list
18330              */
18331         'select' : true,
18332         /**
18333          * @event beforequery
18334          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18335          * The event object passed has these properties:
18336              * @param {Roo.form.ComboBox} combo This combo box
18337              * @param {String} query The query
18338              * @param {Boolean} forceAll true to force "all" query
18339              * @param {Boolean} cancel true to cancel the query
18340              * @param {Object} e The query event object
18341              */
18342         'beforequery': true,
18343          /**
18344          * @event add
18345          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18346              * @param {Roo.form.ComboBox} combo This combo box
18347              */
18348         'add' : true,
18349         /**
18350          * @event edit
18351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18352              * @param {Roo.form.ComboBox} combo This combo box
18353              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18354              */
18355         'edit' : true
18356         
18357         
18358     });
18359     if(this.transform){
18360         this.allowDomMove = false;
18361         var s = Roo.getDom(this.transform);
18362         if(!this.hiddenName){
18363             this.hiddenName = s.name;
18364         }
18365         if(!this.store){
18366             this.mode = 'local';
18367             var d = [], opts = s.options;
18368             for(var i = 0, len = opts.length;i < len; i++){
18369                 var o = opts[i];
18370                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18371                 if(o.selected) {
18372                     this.value = value;
18373                 }
18374                 d.push([value, o.text]);
18375             }
18376             this.store = new Roo.data.SimpleStore({
18377                 'id': 0,
18378                 fields: ['value', 'text'],
18379                 data : d
18380             });
18381             this.valueField = 'value';
18382             this.displayField = 'text';
18383         }
18384         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18385         if(!this.lazyRender){
18386             this.target = true;
18387             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18388             s.parentNode.removeChild(s); // remove it
18389             this.render(this.el.parentNode);
18390         }else{
18391             s.parentNode.removeChild(s); // remove it
18392         }
18393
18394     }
18395     if (this.store) {
18396         this.store = Roo.factory(this.store, Roo.data);
18397     }
18398     
18399     this.selectedIndex = -1;
18400     if(this.mode == 'local'){
18401         if(config.queryDelay === undefined){
18402             this.queryDelay = 10;
18403         }
18404         if(config.minChars === undefined){
18405             this.minChars = 0;
18406         }
18407     }
18408 };
18409
18410 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18411     /**
18412      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18413      */
18414     /**
18415      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18416      * rendering into an Roo.Editor, defaults to false)
18417      */
18418     /**
18419      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18420      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18421      */
18422     /**
18423      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18424      */
18425     /**
18426      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18427      * the dropdown list (defaults to undefined, with no header element)
18428      */
18429
18430      /**
18431      * @cfg {String/Roo.Template} tpl The template to use to render the output
18432      */
18433      
18434     // private
18435     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18436     /**
18437      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18438      */
18439     listWidth: undefined,
18440     /**
18441      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18442      * mode = 'remote' or 'text' if mode = 'local')
18443      */
18444     displayField: undefined,
18445     /**
18446      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18447      * mode = 'remote' or 'value' if mode = 'local'). 
18448      * Note: use of a valueField requires the user make a selection
18449      * in order for a value to be mapped.
18450      */
18451     valueField: undefined,
18452     
18453     
18454     /**
18455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18456      * field's data value (defaults to the underlying DOM element's name)
18457      */
18458     hiddenName: undefined,
18459     /**
18460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18461      */
18462     listClass: '',
18463     /**
18464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18465      */
18466     selectedClass: 'x-combo-selected',
18467     /**
18468      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18469      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18470      * which displays a downward arrow icon).
18471      */
18472     triggerClass : 'x-form-arrow-trigger',
18473     /**
18474      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18475      */
18476     shadow:'sides',
18477     /**
18478      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18479      * anchor positions (defaults to 'tl-bl')
18480      */
18481     listAlign: 'tl-bl?',
18482     /**
18483      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18484      */
18485     maxHeight: 300,
18486     /**
18487      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18488      * query specified by the allQuery config option (defaults to 'query')
18489      */
18490     triggerAction: 'query',
18491     /**
18492      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18493      * (defaults to 4, does not apply if editable = false)
18494      */
18495     minChars : 4,
18496     /**
18497      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18498      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18499      */
18500     typeAhead: false,
18501     /**
18502      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18503      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18504      */
18505     queryDelay: 500,
18506     /**
18507      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18508      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18509      */
18510     pageSize: 0,
18511     /**
18512      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18513      * when editable = true (defaults to false)
18514      */
18515     selectOnFocus:false,
18516     /**
18517      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18518      */
18519     queryParam: 'query',
18520     /**
18521      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18522      * when mode = 'remote' (defaults to 'Loading...')
18523      */
18524     loadingText: 'Loading...',
18525     /**
18526      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18527      */
18528     resizable: false,
18529     /**
18530      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18531      */
18532     handleHeight : 8,
18533     /**
18534      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18535      * traditional select (defaults to true)
18536      */
18537     editable: true,
18538     /**
18539      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18540      */
18541     allQuery: '',
18542     /**
18543      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18544      */
18545     mode: 'remote',
18546     /**
18547      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18548      * listWidth has a higher value)
18549      */
18550     minListWidth : 70,
18551     /**
18552      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18553      * allow the user to set arbitrary text into the field (defaults to false)
18554      */
18555     forceSelection:false,
18556     /**
18557      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18558      * if typeAhead = true (defaults to 250)
18559      */
18560     typeAheadDelay : 250,
18561     /**
18562      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18563      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18564      */
18565     valueNotFoundText : undefined,
18566     /**
18567      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18568      */
18569     blockFocus : false,
18570     
18571     /**
18572      * @cfg {Boolean} disableClear Disable showing of clear button.
18573      */
18574     disableClear : false,
18575     /**
18576      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18577      */
18578     alwaysQuery : false,
18579     
18580     //private
18581     addicon : false,
18582     editicon: false,
18583     
18584     // element that contains real text value.. (when hidden is used..)
18585      
18586     // private
18587     onRender : function(ct, position)
18588     {
18589         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18590         
18591         if(this.hiddenName){
18592             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18593                     'before', true);
18594             this.hiddenField.value =
18595                 this.hiddenValue !== undefined ? this.hiddenValue :
18596                 this.value !== undefined ? this.value : '';
18597
18598             // prevent input submission
18599             this.el.dom.removeAttribute('name');
18600              
18601              
18602         }
18603         
18604         if(Roo.isGecko){
18605             this.el.dom.setAttribute('autocomplete', 'off');
18606         }
18607
18608         var cls = 'x-combo-list';
18609
18610         this.list = new Roo.Layer({
18611             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18612         });
18613
18614         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18615         this.list.setWidth(lw);
18616         this.list.swallowEvent('mousewheel');
18617         this.assetHeight = 0;
18618
18619         if(this.title){
18620             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18621             this.assetHeight += this.header.getHeight();
18622         }
18623
18624         this.innerList = this.list.createChild({cls:cls+'-inner'});
18625         this.innerList.on('mouseover', this.onViewOver, this);
18626         this.innerList.on('mousemove', this.onViewMove, this);
18627         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18628         
18629         if(this.allowBlank && !this.pageSize && !this.disableClear){
18630             this.footer = this.list.createChild({cls:cls+'-ft'});
18631             this.pageTb = new Roo.Toolbar(this.footer);
18632            
18633         }
18634         if(this.pageSize){
18635             this.footer = this.list.createChild({cls:cls+'-ft'});
18636             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18637                     {pageSize: this.pageSize});
18638             
18639         }
18640         
18641         if (this.pageTb && this.allowBlank && !this.disableClear) {
18642             var _this = this;
18643             this.pageTb.add(new Roo.Toolbar.Fill(), {
18644                 cls: 'x-btn-icon x-btn-clear',
18645                 text: '&#160;',
18646                 handler: function()
18647                 {
18648                     _this.collapse();
18649                     _this.clearValue();
18650                     _this.onSelect(false, -1);
18651                 }
18652             });
18653         }
18654         if (this.footer) {
18655             this.assetHeight += this.footer.getHeight();
18656         }
18657         
18658
18659         if(!this.tpl){
18660             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18661         }
18662
18663         this.view = new Roo.View(this.innerList, this.tpl, {
18664             singleSelect:true,
18665             store: this.store,
18666             selectedClass: this.selectedClass
18667         });
18668
18669         this.view.on('click', this.onViewClick, this);
18670
18671         this.store.on('beforeload', this.onBeforeLoad, this);
18672         this.store.on('load', this.onLoad, this);
18673         this.store.on('loadexception', this.onLoadException, this);
18674
18675         if(this.resizable){
18676             this.resizer = new Roo.Resizable(this.list,  {
18677                pinned:true, handles:'se'
18678             });
18679             this.resizer.on('resize', function(r, w, h){
18680                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18681                 this.listWidth = w;
18682                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18683                 this.restrictHeight();
18684             }, this);
18685             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18686         }
18687         if(!this.editable){
18688             this.editable = true;
18689             this.setEditable(false);
18690         }  
18691         
18692         
18693         if (typeof(this.events.add.listeners) != 'undefined') {
18694             
18695             this.addicon = this.wrap.createChild(
18696                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18697        
18698             this.addicon.on('click', function(e) {
18699                 this.fireEvent('add', this);
18700             }, this);
18701         }
18702         if (typeof(this.events.edit.listeners) != 'undefined') {
18703             
18704             this.editicon = this.wrap.createChild(
18705                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18706             if (this.addicon) {
18707                 this.editicon.setStyle('margin-left', '40px');
18708             }
18709             this.editicon.on('click', function(e) {
18710                 
18711                 // we fire even  if inothing is selected..
18712                 this.fireEvent('edit', this, this.lastData );
18713                 
18714             }, this);
18715         }
18716         
18717         
18718         
18719     },
18720
18721     // private
18722     initEvents : function(){
18723         Roo.form.ComboBox.superclass.initEvents.call(this);
18724
18725         this.keyNav = new Roo.KeyNav(this.el, {
18726             "up" : function(e){
18727                 this.inKeyMode = true;
18728                 this.selectPrev();
18729             },
18730
18731             "down" : function(e){
18732                 if(!this.isExpanded()){
18733                     this.onTriggerClick();
18734                 }else{
18735                     this.inKeyMode = true;
18736                     this.selectNext();
18737                 }
18738             },
18739
18740             "enter" : function(e){
18741                 this.onViewClick();
18742                 //return true;
18743             },
18744
18745             "esc" : function(e){
18746                 this.collapse();
18747             },
18748
18749             "tab" : function(e){
18750                 this.onViewClick(false);
18751                 this.fireEvent("specialkey", this, e);
18752                 return true;
18753             },
18754
18755             scope : this,
18756
18757             doRelay : function(foo, bar, hname){
18758                 if(hname == 'down' || this.scope.isExpanded()){
18759                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18760                 }
18761                 return true;
18762             },
18763
18764             forceKeyDown: true
18765         });
18766         this.queryDelay = Math.max(this.queryDelay || 10,
18767                 this.mode == 'local' ? 10 : 250);
18768         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18769         if(this.typeAhead){
18770             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18771         }
18772         if(this.editable !== false){
18773             this.el.on("keyup", this.onKeyUp, this);
18774         }
18775         if(this.forceSelection){
18776             this.on('blur', this.doForce, this);
18777         }
18778     },
18779
18780     onDestroy : function(){
18781         if(this.view){
18782             this.view.setStore(null);
18783             this.view.el.removeAllListeners();
18784             this.view.el.remove();
18785             this.view.purgeListeners();
18786         }
18787         if(this.list){
18788             this.list.destroy();
18789         }
18790         if(this.store){
18791             this.store.un('beforeload', this.onBeforeLoad, this);
18792             this.store.un('load', this.onLoad, this);
18793             this.store.un('loadexception', this.onLoadException, this);
18794         }
18795         Roo.form.ComboBox.superclass.onDestroy.call(this);
18796     },
18797
18798     // private
18799     fireKey : function(e){
18800         if(e.isNavKeyPress() && !this.list.isVisible()){
18801             this.fireEvent("specialkey", this, e);
18802         }
18803     },
18804
18805     // private
18806     onResize: function(w, h){
18807         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18808         
18809         if(typeof w != 'number'){
18810             // we do not handle it!?!?
18811             return;
18812         }
18813         var tw = this.trigger.getWidth();
18814         tw += this.addicon ? this.addicon.getWidth() : 0;
18815         tw += this.editicon ? this.editicon.getWidth() : 0;
18816         var x = w - tw;
18817         this.el.setWidth( this.adjustWidth('input', x));
18818             
18819         this.trigger.setStyle('left', x+'px');
18820         
18821         if(this.list && this.listWidth === undefined){
18822             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18823             this.list.setWidth(lw);
18824             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18825         }
18826         
18827     
18828         
18829     },
18830
18831     /**
18832      * Allow or prevent the user from directly editing the field text.  If false is passed,
18833      * the user will only be able to select from the items defined in the dropdown list.  This method
18834      * is the runtime equivalent of setting the 'editable' config option at config time.
18835      * @param {Boolean} value True to allow the user to directly edit the field text
18836      */
18837     setEditable : function(value){
18838         if(value == this.editable){
18839             return;
18840         }
18841         this.editable = value;
18842         if(!value){
18843             this.el.dom.setAttribute('readOnly', true);
18844             this.el.on('mousedown', this.onTriggerClick,  this);
18845             this.el.addClass('x-combo-noedit');
18846         }else{
18847             this.el.dom.setAttribute('readOnly', false);
18848             this.el.un('mousedown', this.onTriggerClick,  this);
18849             this.el.removeClass('x-combo-noedit');
18850         }
18851     },
18852
18853     // private
18854     onBeforeLoad : function(){
18855         if(!this.hasFocus){
18856             return;
18857         }
18858         this.innerList.update(this.loadingText ?
18859                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18860         this.restrictHeight();
18861         this.selectedIndex = -1;
18862     },
18863
18864     // private
18865     onLoad : function(){
18866         if(!this.hasFocus){
18867             return;
18868         }
18869         if(this.store.getCount() > 0){
18870             this.expand();
18871             this.restrictHeight();
18872             if(this.lastQuery == this.allQuery){
18873                 if(this.editable){
18874                     this.el.dom.select();
18875                 }
18876                 if(!this.selectByValue(this.value, true)){
18877                     this.select(0, true);
18878                 }
18879             }else{
18880                 this.selectNext();
18881                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18882                     this.taTask.delay(this.typeAheadDelay);
18883                 }
18884             }
18885         }else{
18886             this.onEmptyResults();
18887         }
18888         //this.el.focus();
18889     },
18890     // private
18891     onLoadException : function()
18892     {
18893         this.collapse();
18894         Roo.log(this.store.reader.jsonData);
18895         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18896             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18897         }
18898         
18899         
18900     },
18901     // private
18902     onTypeAhead : function(){
18903         if(this.store.getCount() > 0){
18904             var r = this.store.getAt(0);
18905             var newValue = r.data[this.displayField];
18906             var len = newValue.length;
18907             var selStart = this.getRawValue().length;
18908             if(selStart != len){
18909                 this.setRawValue(newValue);
18910                 this.selectText(selStart, newValue.length);
18911             }
18912         }
18913     },
18914
18915     // private
18916     onSelect : function(record, index){
18917         if(this.fireEvent('beforeselect', this, record, index) !== false){
18918             this.setFromData(index > -1 ? record.data : false);
18919             this.collapse();
18920             this.fireEvent('select', this, record, index);
18921         }
18922     },
18923
18924     /**
18925      * Returns the currently selected field value or empty string if no value is set.
18926      * @return {String} value The selected value
18927      */
18928     getValue : function(){
18929         if(this.valueField){
18930             return typeof this.value != 'undefined' ? this.value : '';
18931         }
18932         return Roo.form.ComboBox.superclass.getValue.call(this);
18933     },
18934
18935     /**
18936      * Clears any text/value currently set in the field
18937      */
18938     clearValue : function(){
18939         if(this.hiddenField){
18940             this.hiddenField.value = '';
18941         }
18942         this.value = '';
18943         this.setRawValue('');
18944         this.lastSelectionText = '';
18945         
18946     },
18947
18948     /**
18949      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18950      * will be displayed in the field.  If the value does not match the data value of an existing item,
18951      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18952      * Otherwise the field will be blank (although the value will still be set).
18953      * @param {String} value The value to match
18954      */
18955     setValue : function(v){
18956         var text = v;
18957         if(this.valueField){
18958             var r = this.findRecord(this.valueField, v);
18959             if(r){
18960                 text = r.data[this.displayField];
18961             }else if(this.valueNotFoundText !== undefined){
18962                 text = this.valueNotFoundText;
18963             }
18964         }
18965         this.lastSelectionText = text;
18966         if(this.hiddenField){
18967             this.hiddenField.value = v;
18968         }
18969         Roo.form.ComboBox.superclass.setValue.call(this, text);
18970         this.value = v;
18971     },
18972     /**
18973      * @property {Object} the last set data for the element
18974      */
18975     
18976     lastData : false,
18977     /**
18978      * Sets the value of the field based on a object which is related to the record format for the store.
18979      * @param {Object} value the value to set as. or false on reset?
18980      */
18981     setFromData : function(o){
18982         var dv = ''; // display value
18983         var vv = ''; // value value..
18984         this.lastData = o;
18985         if (this.displayField) {
18986             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18987         } else {
18988             // this is an error condition!!!
18989             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18990         }
18991         
18992         if(this.valueField){
18993             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18994         }
18995         if(this.hiddenField){
18996             this.hiddenField.value = vv;
18997             
18998             this.lastSelectionText = dv;
18999             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19000             this.value = vv;
19001             return;
19002         }
19003         // no hidden field.. - we store the value in 'value', but still display
19004         // display field!!!!
19005         this.lastSelectionText = dv;
19006         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19007         this.value = vv;
19008         
19009         
19010     },
19011     // private
19012     reset : function(){
19013         // overridden so that last data is reset..
19014         this.setValue(this.resetValue);
19015         this.originalValue = this.getValue();
19016         this.clearInvalid();
19017         this.lastData = false;
19018         if (this.view) {
19019             this.view.clearSelections();
19020         }
19021     },
19022     // private
19023     findRecord : function(prop, value){
19024         var record;
19025         if(this.store.getCount() > 0){
19026             this.store.each(function(r){
19027                 if(r.data[prop] == value){
19028                     record = r;
19029                     return false;
19030                 }
19031                 return true;
19032             });
19033         }
19034         return record;
19035     },
19036     
19037     getName: function()
19038     {
19039         // returns hidden if it's set..
19040         if (!this.rendered) {return ''};
19041         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19042         
19043     },
19044     // private
19045     onViewMove : function(e, t){
19046         this.inKeyMode = false;
19047     },
19048
19049     // private
19050     onViewOver : function(e, t){
19051         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19052             return;
19053         }
19054         var item = this.view.findItemFromChild(t);
19055         if(item){
19056             var index = this.view.indexOf(item);
19057             this.select(index, false);
19058         }
19059     },
19060
19061     // private
19062     onViewClick : function(doFocus)
19063     {
19064         var index = this.view.getSelectedIndexes()[0];
19065         var r = this.store.getAt(index);
19066         if(r){
19067             this.onSelect(r, index);
19068         }
19069         if(doFocus !== false && !this.blockFocus){
19070             this.el.focus();
19071         }
19072     },
19073
19074     // private
19075     restrictHeight : function(){
19076         this.innerList.dom.style.height = '';
19077         var inner = this.innerList.dom;
19078         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19079         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19080         this.list.beginUpdate();
19081         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19082         this.list.alignTo(this.el, this.listAlign);
19083         this.list.endUpdate();
19084     },
19085
19086     // private
19087     onEmptyResults : function(){
19088         this.collapse();
19089     },
19090
19091     /**
19092      * Returns true if the dropdown list is expanded, else false.
19093      */
19094     isExpanded : function(){
19095         return this.list.isVisible();
19096     },
19097
19098     /**
19099      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19100      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19101      * @param {String} value The data value of the item to select
19102      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19103      * selected item if it is not currently in view (defaults to true)
19104      * @return {Boolean} True if the value matched an item in the list, else false
19105      */
19106     selectByValue : function(v, scrollIntoView){
19107         if(v !== undefined && v !== null){
19108             var r = this.findRecord(this.valueField || this.displayField, v);
19109             if(r){
19110                 this.select(this.store.indexOf(r), scrollIntoView);
19111                 return true;
19112             }
19113         }
19114         return false;
19115     },
19116
19117     /**
19118      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19120      * @param {Number} index The zero-based index of the list item to select
19121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19122      * selected item if it is not currently in view (defaults to true)
19123      */
19124     select : function(index, scrollIntoView){
19125         this.selectedIndex = index;
19126         this.view.select(index);
19127         if(scrollIntoView !== false){
19128             var el = this.view.getNode(index);
19129             if(el){
19130                 this.innerList.scrollChildIntoView(el, false);
19131             }
19132         }
19133     },
19134
19135     // private
19136     selectNext : function(){
19137         var ct = this.store.getCount();
19138         if(ct > 0){
19139             if(this.selectedIndex == -1){
19140                 this.select(0);
19141             }else if(this.selectedIndex < ct-1){
19142                 this.select(this.selectedIndex+1);
19143             }
19144         }
19145     },
19146
19147     // private
19148     selectPrev : function(){
19149         var ct = this.store.getCount();
19150         if(ct > 0){
19151             if(this.selectedIndex == -1){
19152                 this.select(0);
19153             }else if(this.selectedIndex != 0){
19154                 this.select(this.selectedIndex-1);
19155             }
19156         }
19157     },
19158
19159     // private
19160     onKeyUp : function(e){
19161         if(this.editable !== false && !e.isSpecialKey()){
19162             this.lastKey = e.getKey();
19163             this.dqTask.delay(this.queryDelay);
19164         }
19165     },
19166
19167     // private
19168     validateBlur : function(){
19169         return !this.list || !this.list.isVisible();   
19170     },
19171
19172     // private
19173     initQuery : function(){
19174         this.doQuery(this.getRawValue());
19175     },
19176
19177     // private
19178     doForce : function(){
19179         if(this.el.dom.value.length > 0){
19180             this.el.dom.value =
19181                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19182              
19183         }
19184     },
19185
19186     /**
19187      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19188      * query allowing the query action to be canceled if needed.
19189      * @param {String} query The SQL query to execute
19190      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19191      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19192      * saved in the current store (defaults to false)
19193      */
19194     doQuery : function(q, forceAll){
19195         if(q === undefined || q === null){
19196             q = '';
19197         }
19198         var qe = {
19199             query: q,
19200             forceAll: forceAll,
19201             combo: this,
19202             cancel:false
19203         };
19204         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19205             return false;
19206         }
19207         q = qe.query;
19208         forceAll = qe.forceAll;
19209         if(forceAll === true || (q.length >= this.minChars)){
19210             if(this.lastQuery != q || this.alwaysQuery){
19211                 this.lastQuery = q;
19212                 if(this.mode == 'local'){
19213                     this.selectedIndex = -1;
19214                     if(forceAll){
19215                         this.store.clearFilter();
19216                     }else{
19217                         this.store.filter(this.displayField, q);
19218                     }
19219                     this.onLoad();
19220                 }else{
19221                     this.store.baseParams[this.queryParam] = q;
19222                     this.store.load({
19223                         params: this.getParams(q)
19224                     });
19225                     this.expand();
19226                 }
19227             }else{
19228                 this.selectedIndex = -1;
19229                 this.onLoad();   
19230             }
19231         }
19232     },
19233
19234     // private
19235     getParams : function(q){
19236         var p = {};
19237         //p[this.queryParam] = q;
19238         if(this.pageSize){
19239             p.start = 0;
19240             p.limit = this.pageSize;
19241         }
19242         return p;
19243     },
19244
19245     /**
19246      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19247      */
19248     collapse : function(){
19249         if(!this.isExpanded()){
19250             return;
19251         }
19252         this.list.hide();
19253         Roo.get(document).un('mousedown', this.collapseIf, this);
19254         Roo.get(document).un('mousewheel', this.collapseIf, this);
19255         if (!this.editable) {
19256             Roo.get(document).un('keydown', this.listKeyPress, this);
19257         }
19258         this.fireEvent('collapse', this);
19259     },
19260
19261     // private
19262     collapseIf : function(e){
19263         if(!e.within(this.wrap) && !e.within(this.list)){
19264             this.collapse();
19265         }
19266     },
19267
19268     /**
19269      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19270      */
19271     expand : function(){
19272         if(this.isExpanded() || !this.hasFocus){
19273             return;
19274         }
19275         this.list.alignTo(this.el, this.listAlign);
19276         this.list.show();
19277         Roo.get(document).on('mousedown', this.collapseIf, this);
19278         Roo.get(document).on('mousewheel', this.collapseIf, this);
19279         if (!this.editable) {
19280             Roo.get(document).on('keydown', this.listKeyPress, this);
19281         }
19282         
19283         this.fireEvent('expand', this);
19284     },
19285
19286     // private
19287     // Implements the default empty TriggerField.onTriggerClick function
19288     onTriggerClick : function(){
19289         if(this.disabled){
19290             return;
19291         }
19292         if(this.isExpanded()){
19293             this.collapse();
19294             if (!this.blockFocus) {
19295                 this.el.focus();
19296             }
19297             
19298         }else {
19299             this.hasFocus = true;
19300             if(this.triggerAction == 'all') {
19301                 this.doQuery(this.allQuery, true);
19302             } else {
19303                 this.doQuery(this.getRawValue());
19304             }
19305             if (!this.blockFocus) {
19306                 this.el.focus();
19307             }
19308         }
19309     },
19310     listKeyPress : function(e)
19311     {
19312         //Roo.log('listkeypress');
19313         // scroll to first matching element based on key pres..
19314         if (e.isSpecialKey()) {
19315             return false;
19316         }
19317         var k = String.fromCharCode(e.getKey()).toUpperCase();
19318         //Roo.log(k);
19319         var match  = false;
19320         var csel = this.view.getSelectedNodes();
19321         var cselitem = false;
19322         if (csel.length) {
19323             var ix = this.view.indexOf(csel[0]);
19324             cselitem  = this.store.getAt(ix);
19325             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19326                 cselitem = false;
19327             }
19328             
19329         }
19330         
19331         this.store.each(function(v) { 
19332             if (cselitem) {
19333                 // start at existing selection.
19334                 if (cselitem.id == v.id) {
19335                     cselitem = false;
19336                 }
19337                 return;
19338             }
19339                 
19340             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19341                 match = this.store.indexOf(v);
19342                 return false;
19343             }
19344         }, this);
19345         
19346         if (match === false) {
19347             return true; // no more action?
19348         }
19349         // scroll to?
19350         this.view.select(match);
19351         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19352         sn.scrollIntoView(sn.dom.parentNode, false);
19353     } 
19354
19355     /** 
19356     * @cfg {Boolean} grow 
19357     * @hide 
19358     */
19359     /** 
19360     * @cfg {Number} growMin 
19361     * @hide 
19362     */
19363     /** 
19364     * @cfg {Number} growMax 
19365     * @hide 
19366     */
19367     /**
19368      * @hide
19369      * @method autoSize
19370      */
19371 });/*
19372  * Copyright(c) 2010-2012, Roo J Solutions Limited
19373  *
19374  * Licence LGPL
19375  *
19376  */
19377
19378 /**
19379  * @class Roo.form.ComboBoxArray
19380  * @extends Roo.form.TextField
19381  * A facebook style adder... for lists of email / people / countries  etc...
19382  * pick multiple items from a combo box, and shows each one.
19383  *
19384  *  Fred [x]  Brian [x]  [Pick another |v]
19385  *
19386  *
19387  *  For this to work: it needs various extra information
19388  *    - normal combo problay has
19389  *      name, hiddenName
19390  *    + displayField, valueField
19391  *
19392  *    For our purpose...
19393  *
19394  *
19395  *   If we change from 'extends' to wrapping...
19396  *   
19397  *  
19398  *
19399  
19400  
19401  * @constructor
19402  * Create a new ComboBoxArray.
19403  * @param {Object} config Configuration options
19404  */
19405  
19406
19407 Roo.form.ComboBoxArray = function(config)
19408 {
19409     this.addEvents({
19410         /**
19411          * @event beforeremove
19412          * Fires before remove the value from the list
19413              * @param {Roo.form.ComboBoxArray} _self This combo box array
19414              * @param {Roo.form.ComboBoxArray.Item} item removed item
19415              */
19416         'beforeremove' : true,
19417         /**
19418          * @event remove
19419          * Fires when remove the value from the list
19420              * @param {Roo.form.ComboBoxArray} _self This combo box array
19421              * @param {Roo.form.ComboBoxArray.Item} item removed item
19422              */
19423         'remove' : true
19424         
19425         
19426     });
19427     
19428     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19429     
19430     this.items = new Roo.util.MixedCollection(false);
19431     
19432     // construct the child combo...
19433     
19434     
19435     
19436     
19437    
19438     
19439 }
19440
19441  
19442 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19443
19444     /**
19445      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19446      */
19447     
19448     lastData : false,
19449     
19450     // behavies liek a hiddne field
19451     inputType:      'hidden',
19452     /**
19453      * @cfg {Number} width The width of the box that displays the selected element
19454      */ 
19455     width:          300,
19456
19457     
19458     
19459     /**
19460      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19461      */
19462     name : false,
19463     /**
19464      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19465      */
19466     hiddenName : false,
19467       /**
19468      * @cfg {String} seperator    The value seperator normally ',' 
19469      */
19470     seperator : ',',
19471     
19472     // private the array of items that are displayed..
19473     items  : false,
19474     // private - the hidden field el.
19475     hiddenEl : false,
19476     // private - the filed el..
19477     el : false,
19478     
19479     //validateValue : function() { return true; }, // all values are ok!
19480     //onAddClick: function() { },
19481     
19482     onRender : function(ct, position) 
19483     {
19484         
19485         // create the standard hidden element
19486         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19487         
19488         
19489         // give fake names to child combo;
19490         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19491         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19492         
19493         this.combo = Roo.factory(this.combo, Roo.form);
19494         this.combo.onRender(ct, position);
19495         if (typeof(this.combo.width) != 'undefined') {
19496             this.combo.onResize(this.combo.width,0);
19497         }
19498         
19499         this.combo.initEvents();
19500         
19501         // assigned so form know we need to do this..
19502         this.store          = this.combo.store;
19503         this.valueField     = this.combo.valueField;
19504         this.displayField   = this.combo.displayField ;
19505         
19506         
19507         this.combo.wrap.addClass('x-cbarray-grp');
19508         
19509         var cbwrap = this.combo.wrap.createChild(
19510             {tag: 'div', cls: 'x-cbarray-cb'},
19511             this.combo.el.dom
19512         );
19513         
19514              
19515         this.hiddenEl = this.combo.wrap.createChild({
19516             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19517         });
19518         this.el = this.combo.wrap.createChild({
19519             tag: 'input',  type:'hidden' , name: this.name, value : ''
19520         });
19521          //   this.el.dom.removeAttribute("name");
19522         
19523         
19524         this.outerWrap = this.combo.wrap;
19525         this.wrap = cbwrap;
19526         
19527         this.outerWrap.setWidth(this.width);
19528         this.outerWrap.dom.removeChild(this.el.dom);
19529         
19530         this.wrap.dom.appendChild(this.el.dom);
19531         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19532         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19533         
19534         this.combo.trigger.setStyle('position','relative');
19535         this.combo.trigger.setStyle('left', '0px');
19536         this.combo.trigger.setStyle('top', '2px');
19537         
19538         this.combo.el.setStyle('vertical-align', 'text-bottom');
19539         
19540         //this.trigger.setStyle('vertical-align', 'top');
19541         
19542         // this should use the code from combo really... on('add' ....)
19543         if (this.adder) {
19544             
19545         
19546             this.adder = this.outerWrap.createChild(
19547                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19548             var _t = this;
19549             this.adder.on('click', function(e) {
19550                 _t.fireEvent('adderclick', this, e);
19551             }, _t);
19552         }
19553         //var _t = this;
19554         //this.adder.on('click', this.onAddClick, _t);
19555         
19556         
19557         this.combo.on('select', function(cb, rec, ix) {
19558             this.addItem(rec.data);
19559             
19560             cb.setValue('');
19561             cb.el.dom.value = '';
19562             //cb.lastData = rec.data;
19563             // add to list
19564             
19565         }, this);
19566         
19567         
19568     },
19569     
19570     
19571     getName: function()
19572     {
19573         // returns hidden if it's set..
19574         if (!this.rendered) {return ''};
19575         return  this.hiddenName ? this.hiddenName : this.name;
19576         
19577     },
19578     
19579     
19580     onResize: function(w, h){
19581         
19582         return;
19583         // not sure if this is needed..
19584         //this.combo.onResize(w,h);
19585         
19586         if(typeof w != 'number'){
19587             // we do not handle it!?!?
19588             return;
19589         }
19590         var tw = this.combo.trigger.getWidth();
19591         tw += this.addicon ? this.addicon.getWidth() : 0;
19592         tw += this.editicon ? this.editicon.getWidth() : 0;
19593         var x = w - tw;
19594         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19595             
19596         this.combo.trigger.setStyle('left', '0px');
19597         
19598         if(this.list && this.listWidth === undefined){
19599             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19600             this.list.setWidth(lw);
19601             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19602         }
19603         
19604     
19605         
19606     },
19607     
19608     addItem: function(rec)
19609     {
19610         var valueField = this.combo.valueField;
19611         var displayField = this.combo.displayField;
19612         
19613         if (this.items.indexOfKey(rec[valueField]) > -1) {
19614             //console.log("GOT " + rec.data.id);
19615             return;
19616         }
19617         
19618         var x = new Roo.form.ComboBoxArray.Item({
19619             //id : rec[this.idField],
19620             data : rec,
19621             displayField : displayField ,
19622             tipField : displayField ,
19623             cb : this
19624         });
19625         // use the 
19626         this.items.add(rec[valueField],x);
19627         // add it before the element..
19628         this.updateHiddenEl();
19629         x.render(this.outerWrap, this.wrap.dom);
19630         // add the image handler..
19631     },
19632     
19633     updateHiddenEl : function()
19634     {
19635         this.validate();
19636         if (!this.hiddenEl) {
19637             return;
19638         }
19639         var ar = [];
19640         var idField = this.combo.valueField;
19641         
19642         this.items.each(function(f) {
19643             ar.push(f.data[idField]);
19644         });
19645         this.hiddenEl.dom.value = ar.join(this.seperator);
19646         this.validate();
19647     },
19648     
19649     reset : function()
19650     {
19651         this.items.clear();
19652         
19653         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19654            el.remove();
19655         });
19656         
19657         this.el.dom.value = '';
19658         if (this.hiddenEl) {
19659             this.hiddenEl.dom.value = '';
19660         }
19661         
19662     },
19663     getValue: function()
19664     {
19665         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19666     },
19667     setValue: function(v) // not a valid action - must use addItems..
19668     {
19669         
19670         this.reset();
19671          
19672         if (this.store.isLocal && (typeof(v) == 'string')) {
19673             // then we can use the store to find the values..
19674             // comma seperated at present.. this needs to allow JSON based encoding..
19675             this.hiddenEl.value  = v;
19676             var v_ar = [];
19677             Roo.each(v.split(this.seperator), function(k) {
19678                 Roo.log("CHECK " + this.valueField + ',' + k);
19679                 var li = this.store.query(this.valueField, k);
19680                 if (!li.length) {
19681                     return;
19682                 }
19683                 var add = {};
19684                 add[this.valueField] = k;
19685                 add[this.displayField] = li.item(0).data[this.displayField];
19686                 
19687                 this.addItem(add);
19688             }, this) 
19689              
19690         }
19691         if (typeof(v) == 'object' ) {
19692             // then let's assume it's an array of objects..
19693             Roo.each(v, function(l) {
19694                 var add = l;
19695                 if (typeof(l) == 'string') {
19696                     add = {};
19697                     add[this.valueField] = l;
19698                     add[this.displayField] = l
19699                 }
19700                 this.addItem(add);
19701             }, this);
19702              
19703         }
19704         
19705         
19706     },
19707     setFromData: function(v)
19708     {
19709         // this recieves an object, if setValues is called.
19710         this.reset();
19711         this.el.dom.value = v[this.displayField];
19712         this.hiddenEl.dom.value = v[this.valueField];
19713         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19714             return;
19715         }
19716         var kv = v[this.valueField];
19717         var dv = v[this.displayField];
19718         kv = typeof(kv) != 'string' ? '' : kv;
19719         dv = typeof(dv) != 'string' ? '' : dv;
19720         
19721         
19722         var keys = kv.split(this.seperator);
19723         var display = dv.split(this.seperator);
19724         for (var i = 0 ; i < keys.length; i++) {
19725             add = {};
19726             add[this.valueField] = keys[i];
19727             add[this.displayField] = display[i];
19728             this.addItem(add);
19729         }
19730       
19731         
19732     },
19733     
19734     /**
19735      * Validates the combox array value
19736      * @return {Boolean} True if the value is valid, else false
19737      */
19738     validate : function(){
19739         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19740             this.clearInvalid();
19741             return true;
19742         }
19743         return false;
19744     },
19745     
19746     validateValue : function(value){
19747         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19748         
19749     },
19750     
19751     /*@
19752      * overide
19753      * 
19754      */
19755     isDirty : function() {
19756         if(this.disabled) {
19757             return false;
19758         }
19759         
19760         try {
19761             var d = Roo.decode(String(this.originalValue));
19762         } catch (e) {
19763             return String(this.getValue()) !== String(this.originalValue);
19764         }
19765         
19766         var originalValue = [];
19767         
19768         for (var i = 0; i < d.length; i++){
19769             originalValue.push(d[i][this.valueField]);
19770         }
19771         
19772         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19773         
19774     }
19775     
19776 });
19777
19778
19779
19780 /**
19781  * @class Roo.form.ComboBoxArray.Item
19782  * @extends Roo.BoxComponent
19783  * A selected item in the list
19784  *  Fred [x]  Brian [x]  [Pick another |v]
19785  * 
19786  * @constructor
19787  * Create a new item.
19788  * @param {Object} config Configuration options
19789  */
19790  
19791 Roo.form.ComboBoxArray.Item = function(config) {
19792     config.id = Roo.id();
19793     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19794 }
19795
19796 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19797     data : {},
19798     cb: false,
19799     displayField : false,
19800     tipField : false,
19801     
19802     
19803     defaultAutoCreate : {
19804         tag: 'div',
19805         cls: 'x-cbarray-item',
19806         cn : [ 
19807             { tag: 'div' },
19808             {
19809                 tag: 'img',
19810                 width:16,
19811                 height : 16,
19812                 src : Roo.BLANK_IMAGE_URL ,
19813                 align: 'center'
19814             }
19815         ]
19816         
19817     },
19818     
19819  
19820     onRender : function(ct, position)
19821     {
19822         Roo.form.Field.superclass.onRender.call(this, ct, position);
19823         
19824         if(!this.el){
19825             var cfg = this.getAutoCreate();
19826             this.el = ct.createChild(cfg, position);
19827         }
19828         
19829         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19830         
19831         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19832             this.cb.renderer(this.data) :
19833             String.format('{0}',this.data[this.displayField]);
19834         
19835             
19836         this.el.child('div').dom.setAttribute('qtip',
19837                         String.format('{0}',this.data[this.tipField])
19838         );
19839         
19840         this.el.child('img').on('click', this.remove, this);
19841         
19842     },
19843    
19844     remove : function()
19845     {
19846         if(this.cb.disabled){
19847             return;
19848         }
19849         
19850         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19851             this.cb.items.remove(this);
19852             this.el.child('img').un('click', this.remove, this);
19853             this.el.remove();
19854             this.cb.updateHiddenEl();
19855
19856             this.cb.fireEvent('remove', this.cb, this);
19857         }
19858         
19859     }
19860 });/*
19861  * RooJS Library 1.1.1
19862  * Copyright(c) 2008-2011  Alan Knowles
19863  *
19864  * License - LGPL
19865  */
19866  
19867
19868 /**
19869  * @class Roo.form.ComboNested
19870  * @extends Roo.form.ComboBox
19871  * A combobox for that allows selection of nested items in a list,
19872  * eg.
19873  *
19874  *  Book
19875  *    -> red
19876  *    -> green
19877  *  Table
19878  *    -> square
19879  *      ->red
19880  *      ->green
19881  *    -> rectangle
19882  *      ->green
19883  *      
19884  * 
19885  * @constructor
19886  * Create a new ComboNested
19887  * @param {Object} config Configuration options
19888  */
19889 Roo.form.ComboNested = function(config){
19890     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19891     // should verify some data...
19892     // like
19893     // hiddenName = required..
19894     // displayField = required
19895     // valudField == required
19896     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19897     var _t = this;
19898     Roo.each(req, function(e) {
19899         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19900             throw "Roo.form.ComboNested : missing value for: " + e;
19901         }
19902     });
19903      
19904     
19905 };
19906
19907 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19908    
19909     /*
19910      * @config {Number} max Number of columns to show
19911      */
19912     
19913     maxColumns : 3,
19914    
19915     list : null, // the outermost div..
19916     innerLists : null, // the
19917     views : null,
19918     stores : null,
19919     // private
19920     loadingChildren : false,
19921     
19922     onRender : function(ct, position)
19923     {
19924         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19925         
19926         if(this.hiddenName){
19927             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19928                     'before', true);
19929             this.hiddenField.value =
19930                 this.hiddenValue !== undefined ? this.hiddenValue :
19931                 this.value !== undefined ? this.value : '';
19932
19933             // prevent input submission
19934             this.el.dom.removeAttribute('name');
19935              
19936              
19937         }
19938         
19939         if(Roo.isGecko){
19940             this.el.dom.setAttribute('autocomplete', 'off');
19941         }
19942
19943         var cls = 'x-combo-list';
19944
19945         this.list = new Roo.Layer({
19946             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19947         });
19948
19949         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19950         this.list.setWidth(lw);
19951         this.list.swallowEvent('mousewheel');
19952         this.assetHeight = 0;
19953
19954         if(this.title){
19955             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19956             this.assetHeight += this.header.getHeight();
19957         }
19958         this.innerLists = [];
19959         this.views = [];
19960         this.stores = [];
19961         for (var i =0 ; i < this.maxColumns; i++) {
19962             this.onRenderList( cls, i);
19963         }
19964         
19965         // always needs footer, as we are going to have an 'OK' button.
19966         this.footer = this.list.createChild({cls:cls+'-ft'});
19967         this.pageTb = new Roo.Toolbar(this.footer);  
19968         var _this = this;
19969         this.pageTb.add(  {
19970             
19971             text: 'Done',
19972             handler: function()
19973             {
19974                 _this.collapse();
19975             }
19976         });
19977         
19978         if ( this.allowBlank && !this.disableClear) {
19979             
19980             this.pageTb.add(new Roo.Toolbar.Fill(), {
19981                 cls: 'x-btn-icon x-btn-clear',
19982                 text: '&#160;',
19983                 handler: function()
19984                 {
19985                     _this.collapse();
19986                     _this.clearValue();
19987                     _this.onSelect(false, -1);
19988                 }
19989             });
19990         }
19991         if (this.footer) {
19992             this.assetHeight += this.footer.getHeight();
19993         }
19994         
19995     },
19996     onRenderList : function (  cls, i)
19997     {
19998         
19999         var lw = Math.floor(
20000                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20001         );
20002         
20003         this.list.setWidth(lw); // default to '1'
20004
20005         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20006         //il.on('mouseover', this.onViewOver, this, { list:  i });
20007         //il.on('mousemove', this.onViewMove, this, { list:  i });
20008         il.setWidth(lw);
20009         il.setStyle({ 'overflow-x' : 'hidden'});
20010
20011         if(!this.tpl){
20012             this.tpl = new Roo.Template({
20013                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20014                 isEmpty: function (value, allValues) {
20015                     //Roo.log(value);
20016                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20017                     return dl ? 'has-children' : 'no-children'
20018                 }
20019             });
20020         }
20021         
20022         var store  = this.store;
20023         if (i > 0) {
20024             store  = new Roo.data.SimpleStore({
20025                 //fields : this.store.reader.meta.fields,
20026                 reader : this.store.reader,
20027                 data : [ ]
20028             });
20029         }
20030         this.stores[i]  = store;
20031                   
20032         var view = this.views[i] = new Roo.View(
20033             il,
20034             this.tpl,
20035             {
20036                 singleSelect:true,
20037                 store: store,
20038                 selectedClass: this.selectedClass
20039             }
20040         );
20041         view.getEl().setWidth(lw);
20042         view.getEl().setStyle({
20043             position: i < 1 ? 'relative' : 'absolute',
20044             top: 0,
20045             left: (i * lw ) + 'px',
20046             display : i > 0 ? 'none' : 'block'
20047         });
20048         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20049         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20050         //view.on('click', this.onViewClick, this, { list : i });
20051
20052         store.on('beforeload', this.onBeforeLoad, this);
20053         store.on('load',  this.onLoad, this, { list  : i});
20054         store.on('loadexception', this.onLoadException, this);
20055
20056         // hide the other vies..
20057         
20058         
20059         
20060     },
20061       
20062     restrictHeight : function()
20063     {
20064         var mh = 0;
20065         Roo.each(this.innerLists, function(il,i) {
20066             var el = this.views[i].getEl();
20067             el.dom.style.height = '';
20068             var inner = el.dom;
20069             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20070             // only adjust heights on other ones..
20071             mh = Math.max(h, mh);
20072             if (i < 1) {
20073                 
20074                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20075                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20076                
20077             }
20078             
20079             
20080         }, this);
20081         
20082         this.list.beginUpdate();
20083         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20084         this.list.alignTo(this.el, this.listAlign);
20085         this.list.endUpdate();
20086         
20087     },
20088      
20089     
20090     // -- store handlers..
20091     // private
20092     onBeforeLoad : function()
20093     {
20094         if(!this.hasFocus){
20095             return;
20096         }
20097         this.innerLists[0].update(this.loadingText ?
20098                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20099         this.restrictHeight();
20100         this.selectedIndex = -1;
20101     },
20102     // private
20103     onLoad : function(a,b,c,d)
20104     {
20105         if (!this.loadingChildren) {
20106             // then we are loading the top level. - hide the children
20107             for (var i = 1;i < this.views.length; i++) {
20108                 this.views[i].getEl().setStyle({ display : 'none' });
20109             }
20110             var lw = Math.floor(
20111                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20112             );
20113         
20114              this.list.setWidth(lw); // default to '1'
20115
20116             
20117         }
20118         if(!this.hasFocus){
20119             return;
20120         }
20121         
20122         if(this.store.getCount() > 0) {
20123             this.expand();
20124             this.restrictHeight();   
20125         } else {
20126             this.onEmptyResults();
20127         }
20128         
20129         if (!this.loadingChildren) {
20130             this.selectActive();
20131         }
20132         /*
20133         this.stores[1].loadData([]);
20134         this.stores[2].loadData([]);
20135         this.views
20136         */    
20137     
20138         //this.el.focus();
20139     },
20140     
20141     
20142     // private
20143     onLoadException : function()
20144     {
20145         this.collapse();
20146         Roo.log(this.store.reader.jsonData);
20147         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20148             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20149         }
20150         
20151         
20152     },
20153     // no cleaning of leading spaces on blur here.
20154     cleanLeadingSpace : function(e) { },
20155     
20156
20157     onSelectChange : function (view, sels, opts )
20158     {
20159         var ix = view.getSelectedIndexes();
20160          
20161         if (opts.list > this.maxColumns - 2) {
20162             if (view.store.getCount()<  1) {
20163                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20164
20165             } else  {
20166                 if (ix.length) {
20167                     // used to clear ?? but if we are loading unselected 
20168                     this.setFromData(view.store.getAt(ix[0]).data);
20169                 }
20170                 
20171             }
20172             
20173             return;
20174         }
20175         
20176         if (!ix.length) {
20177             // this get's fired when trigger opens..
20178            // this.setFromData({});
20179             var str = this.stores[opts.list+1];
20180             str.data.clear(); // removeall wihtout the fire events..
20181             return;
20182         }
20183         
20184         var rec = view.store.getAt(ix[0]);
20185          
20186         this.setFromData(rec.data);
20187         this.fireEvent('select', this, rec, ix[0]);
20188         
20189         var lw = Math.floor(
20190              (
20191                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20192              ) / this.maxColumns
20193         );
20194         this.loadingChildren = true;
20195         this.stores[opts.list+1].loadDataFromChildren( rec );
20196         this.loadingChildren = false;
20197         var dl = this.stores[opts.list+1]. getTotalCount();
20198         
20199         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20200         
20201         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20202         for (var i = opts.list+2; i < this.views.length;i++) {
20203             this.views[i].getEl().setStyle({ display : 'none' });
20204         }
20205         
20206         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20207         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20208         
20209         if (this.isLoading) {
20210            // this.selectActive(opts.list);
20211         }
20212          
20213     },
20214     
20215     
20216     
20217     
20218     onDoubleClick : function()
20219     {
20220         this.collapse(); //??
20221     },
20222     
20223      
20224     
20225     
20226     
20227     // private
20228     recordToStack : function(store, prop, value, stack)
20229     {
20230         var cstore = new Roo.data.SimpleStore({
20231             //fields : this.store.reader.meta.fields, // we need array reader.. for
20232             reader : this.store.reader,
20233             data : [ ]
20234         });
20235         var _this = this;
20236         var record  = false;
20237         var srec = false;
20238         if(store.getCount() < 1){
20239             return false;
20240         }
20241         store.each(function(r){
20242             if(r.data[prop] == value){
20243                 record = r;
20244             srec = r;
20245                 return false;
20246             }
20247             if (r.data.cn && r.data.cn.length) {
20248                 cstore.loadDataFromChildren( r);
20249                 var cret = _this.recordToStack(cstore, prop, value, stack);
20250                 if (cret !== false) {
20251                     record = cret;
20252                     srec = r;
20253                     return false;
20254                 }
20255             }
20256              
20257             return true;
20258         });
20259         if (record == false) {
20260             return false
20261         }
20262         stack.unshift(srec);
20263         return record;
20264     },
20265     
20266     /*
20267      * find the stack of stores that match our value.
20268      *
20269      * 
20270      */
20271     
20272     selectActive : function ()
20273     {
20274         // if store is not loaded, then we will need to wait for that to happen first.
20275         var stack = [];
20276         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20277         for (var i = 0; i < stack.length; i++ ) {
20278             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20279         }
20280         
20281     }
20282         
20283          
20284     
20285     
20286     
20287     
20288 });/*
20289  * Based on:
20290  * Ext JS Library 1.1.1
20291  * Copyright(c) 2006-2007, Ext JS, LLC.
20292  *
20293  * Originally Released Under LGPL - original licence link has changed is not relivant.
20294  *
20295  * Fork - LGPL
20296  * <script type="text/javascript">
20297  */
20298 /**
20299  * @class Roo.form.Checkbox
20300  * @extends Roo.form.Field
20301  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20302  * @constructor
20303  * Creates a new Checkbox
20304  * @param {Object} config Configuration options
20305  */
20306 Roo.form.Checkbox = function(config){
20307     Roo.form.Checkbox.superclass.constructor.call(this, config);
20308     this.addEvents({
20309         /**
20310          * @event check
20311          * Fires when the checkbox is checked or unchecked.
20312              * @param {Roo.form.Checkbox} this This checkbox
20313              * @param {Boolean} checked The new checked value
20314              */
20315         check : true
20316     });
20317 };
20318
20319 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20320     /**
20321      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20322      */
20323     focusClass : undefined,
20324     /**
20325      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20326      */
20327     fieldClass: "x-form-field",
20328     /**
20329      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20330      */
20331     checked: false,
20332     /**
20333      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20334      * {tag: "input", type: "checkbox", autocomplete: "off"})
20335      */
20336     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20337     /**
20338      * @cfg {String} boxLabel The text that appears beside the checkbox
20339      */
20340     boxLabel : "",
20341     /**
20342      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20343      */  
20344     inputValue : '1',
20345     /**
20346      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20347      */
20348      valueOff: '0', // value when not checked..
20349
20350     actionMode : 'viewEl', 
20351     //
20352     // private
20353     itemCls : 'x-menu-check-item x-form-item',
20354     groupClass : 'x-menu-group-item',
20355     inputType : 'hidden',
20356     
20357     
20358     inSetChecked: false, // check that we are not calling self...
20359     
20360     inputElement: false, // real input element?
20361     basedOn: false, // ????
20362     
20363     isFormField: true, // not sure where this is needed!!!!
20364
20365     onResize : function(){
20366         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20367         if(!this.boxLabel){
20368             this.el.alignTo(this.wrap, 'c-c');
20369         }
20370     },
20371
20372     initEvents : function(){
20373         Roo.form.Checkbox.superclass.initEvents.call(this);
20374         this.el.on("click", this.onClick,  this);
20375         this.el.on("change", this.onClick,  this);
20376     },
20377
20378
20379     getResizeEl : function(){
20380         return this.wrap;
20381     },
20382
20383     getPositionEl : function(){
20384         return this.wrap;
20385     },
20386
20387     // private
20388     onRender : function(ct, position){
20389         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20390         /*
20391         if(this.inputValue !== undefined){
20392             this.el.dom.value = this.inputValue;
20393         }
20394         */
20395         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20396         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20397         var viewEl = this.wrap.createChild({ 
20398             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20399         this.viewEl = viewEl;   
20400         this.wrap.on('click', this.onClick,  this); 
20401         
20402         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20403         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20404         
20405         
20406         
20407         if(this.boxLabel){
20408             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20409         //    viewEl.on('click', this.onClick,  this); 
20410         }
20411         //if(this.checked){
20412             this.setChecked(this.checked);
20413         //}else{
20414             //this.checked = this.el.dom;
20415         //}
20416
20417     },
20418
20419     // private
20420     initValue : Roo.emptyFn,
20421
20422     /**
20423      * Returns the checked state of the checkbox.
20424      * @return {Boolean} True if checked, else false
20425      */
20426     getValue : function(){
20427         if(this.el){
20428             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20429         }
20430         return this.valueOff;
20431         
20432     },
20433
20434         // private
20435     onClick : function(){ 
20436         if (this.disabled) {
20437             return;
20438         }
20439         this.setChecked(!this.checked);
20440
20441         //if(this.el.dom.checked != this.checked){
20442         //    this.setValue(this.el.dom.checked);
20443        // }
20444     },
20445
20446     /**
20447      * Sets the checked state of the checkbox.
20448      * On is always based on a string comparison between inputValue and the param.
20449      * @param {Boolean/String} value - the value to set 
20450      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20451      */
20452     setValue : function(v,suppressEvent){
20453         
20454         
20455         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20456         //if(this.el && this.el.dom){
20457         //    this.el.dom.checked = this.checked;
20458         //    this.el.dom.defaultChecked = this.checked;
20459         //}
20460         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20461         //this.fireEvent("check", this, this.checked);
20462     },
20463     // private..
20464     setChecked : function(state,suppressEvent)
20465     {
20466         if (this.inSetChecked) {
20467             this.checked = state;
20468             return;
20469         }
20470         
20471     
20472         if(this.wrap){
20473             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20474         }
20475         this.checked = state;
20476         if(suppressEvent !== true){
20477             this.fireEvent('check', this, state);
20478         }
20479         this.inSetChecked = true;
20480         this.el.dom.value = state ? this.inputValue : this.valueOff;
20481         this.inSetChecked = false;
20482         
20483     },
20484     // handle setting of hidden value by some other method!!?!?
20485     setFromHidden: function()
20486     {
20487         if(!this.el){
20488             return;
20489         }
20490         //console.log("SET FROM HIDDEN");
20491         //alert('setFrom hidden');
20492         this.setValue(this.el.dom.value);
20493     },
20494     
20495     onDestroy : function()
20496     {
20497         if(this.viewEl){
20498             Roo.get(this.viewEl).remove();
20499         }
20500          
20501         Roo.form.Checkbox.superclass.onDestroy.call(this);
20502     },
20503     
20504     setBoxLabel : function(str)
20505     {
20506         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20507     }
20508
20509 });/*
20510  * Based on:
20511  * Ext JS Library 1.1.1
20512  * Copyright(c) 2006-2007, Ext JS, LLC.
20513  *
20514  * Originally Released Under LGPL - original licence link has changed is not relivant.
20515  *
20516  * Fork - LGPL
20517  * <script type="text/javascript">
20518  */
20519  
20520 /**
20521  * @class Roo.form.Radio
20522  * @extends Roo.form.Checkbox
20523  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20524  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20525  * @constructor
20526  * Creates a new Radio
20527  * @param {Object} config Configuration options
20528  */
20529 Roo.form.Radio = function(){
20530     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20531 };
20532 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20533     inputType: 'radio',
20534
20535     /**
20536      * If this radio is part of a group, it will return the selected value
20537      * @return {String}
20538      */
20539     getGroupValue : function(){
20540         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20541     },
20542     
20543     
20544     onRender : function(ct, position){
20545         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20546         
20547         if(this.inputValue !== undefined){
20548             this.el.dom.value = this.inputValue;
20549         }
20550          
20551         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20552         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20553         //var viewEl = this.wrap.createChild({ 
20554         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20555         //this.viewEl = viewEl;   
20556         //this.wrap.on('click', this.onClick,  this); 
20557         
20558         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20559         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20560         
20561         
20562         
20563         if(this.boxLabel){
20564             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20565         //    viewEl.on('click', this.onClick,  this); 
20566         }
20567          if(this.checked){
20568             this.el.dom.checked =   'checked' ;
20569         }
20570          
20571     } 
20572     
20573     
20574 });Roo.rtf = {}; // namespace
20575 Roo.rtf.Hex = function(hex)
20576 {
20577     this.hexstr = hex;
20578 };
20579 Roo.rtf.Paragraph = function(opts)
20580 {
20581     this.content = []; ///??? is that used?
20582 };Roo.rtf.Span = function(opts)
20583 {
20584     this.value = opts.value;
20585 };
20586
20587 Roo.rtf.Group = function(parent)
20588 {
20589     // we dont want to acutally store parent - it will make debug a nightmare..
20590     this.content = [];
20591     this.cn  = [];
20592      
20593        
20594     
20595 };
20596
20597 Roo.rtf.Group.prototype = {
20598     ignorable : false,
20599     content: false,
20600     cn: false,
20601     addContent : function(node) {
20602         // could set styles...
20603         this.content.push(node);
20604     },
20605     addChild : function(cn)
20606     {
20607         this.cn.push(cn);
20608     },
20609     // only for images really...
20610     toDataURL : function()
20611     {
20612         var mimetype = false;
20613         switch(true) {
20614             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20615                 mimetype = "image/png";
20616                 break;
20617              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20618                 mimetype = "image/jpeg";
20619                 break;
20620             default :
20621                 return 'about:blank'; // ?? error?
20622         }
20623         
20624         
20625         var hexstring = this.content[this.content.length-1].value;
20626         
20627         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20628             return String.fromCharCode(parseInt(a, 16));
20629         }).join(""));
20630     }
20631     
20632 };
20633 // this looks like it's normally the {rtf{ .... }}
20634 Roo.rtf.Document = function()
20635 {
20636     // we dont want to acutally store parent - it will make debug a nightmare..
20637     this.rtlch  = [];
20638     this.content = [];
20639     this.cn = [];
20640     
20641 };
20642 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20643     addChild : function(cn)
20644     {
20645         this.cn.push(cn);
20646         switch(cn.type) {
20647             case 'rtlch': // most content seems to be inside this??
20648             case 'listtext':
20649             case 'shpinst':
20650                 this.rtlch.push(cn);
20651                 return;
20652             default:
20653                 this[cn.type] = cn;
20654         }
20655         
20656     },
20657     
20658     getElementsByType : function(type)
20659     {
20660         var ret =  [];
20661         this._getElementsByType(type, ret, this.cn, 'rtf');
20662         return ret;
20663     },
20664     _getElementsByType : function (type, ret, search_array, path)
20665     {
20666         search_array.forEach(function(n,i) {
20667             if (n.type == type) {
20668                 n.path = path + '/' + n.type + ':' + i;
20669                 ret.push(n);
20670             }
20671             if (n.cn.length > 0) {
20672                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20673             }
20674         },this);
20675     }
20676     
20677 });
20678  
20679 Roo.rtf.Ctrl = function(opts)
20680 {
20681     this.value = opts.value;
20682     this.param = opts.param;
20683 };
20684 /**
20685  *
20686  *
20687  * based on this https://github.com/iarna/rtf-parser
20688  * it's really only designed to extract pict from pasted RTF 
20689  *
20690  * usage:
20691  *
20692  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20693  *  
20694  *
20695  */
20696
20697  
20698
20699
20700
20701 Roo.rtf.Parser = function(text) {
20702     //super({objectMode: true})
20703     this.text = '';
20704     this.parserState = this.parseText;
20705     
20706     // these are for interpeter...
20707     this.doc = {};
20708     ///this.parserState = this.parseTop
20709     this.groupStack = [];
20710     this.hexStore = [];
20711     this.doc = false;
20712     
20713     this.groups = []; // where we put the return.
20714     
20715     for (var ii = 0; ii < text.length; ++ii) {
20716         ++this.cpos;
20717         
20718         if (text[ii] === '\n') {
20719             ++this.row;
20720             this.col = 1;
20721         } else {
20722             ++this.col;
20723         }
20724         this.parserState(text[ii]);
20725     }
20726     
20727     
20728     
20729 };
20730 Roo.rtf.Parser.prototype = {
20731     text : '', // string being parsed..
20732     controlWord : '',
20733     controlWordParam :  '',
20734     hexChar : '',
20735     doc : false,
20736     group: false,
20737     groupStack : false,
20738     hexStore : false,
20739     
20740     
20741     cpos : 0, 
20742     row : 1, // reportin?
20743     col : 1, //
20744
20745      
20746     push : function (el)
20747     {
20748         var m = 'cmd'+ el.type;
20749         if (typeof(this[m]) == 'undefined') {
20750             Roo.log('invalid cmd:' + el.type);
20751             return;
20752         }
20753         this[m](el);
20754         //Roo.log(el);
20755     },
20756     flushHexStore : function()
20757     {
20758         if (this.hexStore.length < 1) {
20759             return;
20760         }
20761         var hexstr = this.hexStore.map(
20762             function(cmd) {
20763                 return cmd.value;
20764         }).join('');
20765         
20766         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20767               
20768             
20769         this.hexStore.splice(0)
20770         
20771     },
20772     
20773     cmdgroupstart : function()
20774     {
20775         this.flushHexStore();
20776         if (this.group) {
20777             this.groupStack.push(this.group);
20778         }
20779          // parent..
20780         if (this.doc === false) {
20781             this.group = this.doc = new Roo.rtf.Document();
20782             return;
20783             
20784         }
20785         this.group = new Roo.rtf.Group(this.group);
20786     },
20787     cmdignorable : function()
20788     {
20789         this.flushHexStore();
20790         this.group.ignorable = true;
20791     },
20792     cmdendparagraph : function()
20793     {
20794         this.flushHexStore();
20795         this.group.addContent(new Roo.rtf.Paragraph());
20796     },
20797     cmdgroupend : function ()
20798     {
20799         this.flushHexStore();
20800         var endingGroup = this.group;
20801         
20802         
20803         this.group = this.groupStack.pop();
20804         if (this.group) {
20805             this.group.addChild(endingGroup);
20806         }
20807         
20808         
20809         
20810         var doc = this.group || this.doc;
20811         //if (endingGroup instanceof FontTable) {
20812         //  doc.fonts = endingGroup.table
20813         //} else if (endingGroup instanceof ColorTable) {
20814         //  doc.colors = endingGroup.table
20815         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20816         if (endingGroup.ignorable === false) {
20817             //code
20818             this.groups.push(endingGroup);
20819            // Roo.log( endingGroup );
20820         }
20821             //Roo.each(endingGroup.content, function(item)) {
20822             //    doc.addContent(item);
20823             //}
20824             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20825         //}
20826     },
20827     cmdtext : function (cmd)
20828     {
20829         this.flushHexStore();
20830         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20831             //this.group = this.doc
20832         }
20833         this.group.addContent(new Roo.rtf.Span(cmd));
20834     },
20835     cmdcontrolword : function (cmd)
20836     {
20837         this.flushHexStore();
20838         if (!this.group.type) {
20839             this.group.type = cmd.value;
20840             return;
20841         }
20842         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20843         // we actually don't care about ctrl words...
20844         return ;
20845         /*
20846         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20847         if (this[method]) {
20848             this[method](cmd.param)
20849         } else {
20850             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20851         }
20852         */
20853     },
20854     cmdhexchar : function(cmd) {
20855         this.hexStore.push(cmd);
20856     },
20857     cmderror : function(cmd) {
20858         throw new Exception (cmd.value);
20859     },
20860     
20861     /*
20862       _flush (done) {
20863         if (this.text !== '\u0000') this.emitText()
20864         done()
20865       }
20866       */
20867       
20868       
20869     parseText : function(c)
20870     {
20871         if (c === '\\') {
20872             this.parserState = this.parseEscapes;
20873         } else if (c === '{') {
20874             this.emitStartGroup();
20875         } else if (c === '}') {
20876             this.emitEndGroup();
20877         } else if (c === '\x0A' || c === '\x0D') {
20878             // cr/lf are noise chars
20879         } else {
20880             this.text += c;
20881         }
20882     },
20883     
20884     parseEscapes: function (c)
20885     {
20886         if (c === '\\' || c === '{' || c === '}') {
20887             this.text += c;
20888             this.parserState = this.parseText;
20889         } else {
20890             this.parserState = this.parseControlSymbol;
20891             this.parseControlSymbol(c);
20892         }
20893     },
20894     parseControlSymbol: function(c)
20895     {
20896         if (c === '~') {
20897             this.text += '\u00a0'; // nbsp
20898             this.parserState = this.parseText
20899         } else if (c === '-') {
20900              this.text += '\u00ad'; // soft hyphen
20901         } else if (c === '_') {
20902             this.text += '\u2011'; // non-breaking hyphen
20903         } else if (c === '*') {
20904             this.emitIgnorable();
20905             this.parserState = this.parseText;
20906         } else if (c === "'") {
20907             this.parserState = this.parseHexChar;
20908         } else if (c === '|') { // formula cacter
20909             this.emitFormula();
20910             this.parserState = this.parseText;
20911         } else if (c === ':') { // subentry in an index entry
20912             this.emitIndexSubEntry();
20913             this.parserState = this.parseText;
20914         } else if (c === '\x0a') {
20915             this.emitEndParagraph();
20916             this.parserState = this.parseText;
20917         } else if (c === '\x0d') {
20918             this.emitEndParagraph();
20919             this.parserState = this.parseText;
20920         } else {
20921             this.parserState = this.parseControlWord;
20922             this.parseControlWord(c);
20923         }
20924     },
20925     parseHexChar: function (c)
20926     {
20927         if (/^[A-Fa-f0-9]$/.test(c)) {
20928             this.hexChar += c;
20929             if (this.hexChar.length >= 2) {
20930               this.emitHexChar();
20931               this.parserState = this.parseText;
20932             }
20933             return;
20934         }
20935         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20936         this.parserState = this.parseText;
20937         
20938     },
20939     parseControlWord : function(c)
20940     {
20941         if (c === ' ') {
20942             this.emitControlWord();
20943             this.parserState = this.parseText;
20944         } else if (/^[-\d]$/.test(c)) {
20945             this.parserState = this.parseControlWordParam;
20946             this.controlWordParam += c;
20947         } else if (/^[A-Za-z]$/.test(c)) {
20948           this.controlWord += c;
20949         } else {
20950           this.emitControlWord();
20951           this.parserState = this.parseText;
20952           this.parseText(c);
20953         }
20954     },
20955     parseControlWordParam : function (c) {
20956         if (/^\d$/.test(c)) {
20957           this.controlWordParam += c;
20958         } else if (c === ' ') {
20959           this.emitControlWord();
20960           this.parserState = this.parseText;
20961         } else {
20962           this.emitControlWord();
20963           this.parserState = this.parseText;
20964           this.parseText(c);
20965         }
20966     },
20967     
20968     
20969     
20970     
20971     emitText : function () {
20972         if (this.text === '') {
20973             return;
20974         }
20975         this.push({
20976             type: 'text',
20977             value: this.text,
20978             pos: this.cpos,
20979             row: this.row,
20980             col: this.col
20981         });
20982         this.text = ''
20983     },
20984     emitControlWord : function ()
20985     {
20986         this.emitText();
20987         if (this.controlWord === '') {
20988             this.emitError('empty control word');
20989         } else {
20990             this.push({
20991                   type: 'controlword',
20992                   value: this.controlWord,
20993                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
20994                   pos: this.cpos,
20995                   row: this.row,
20996                   col: this.col
20997             });
20998         }
20999         this.controlWord = '';
21000         this.controlWordParam = '';
21001     },
21002     emitStartGroup : function ()
21003     {
21004         this.emitText();
21005         this.push({
21006             type: 'groupstart',
21007             pos: this.cpos,
21008             row: this.row,
21009             col: this.col
21010         });
21011     },
21012     emitEndGroup : function ()
21013     {
21014         this.emitText();
21015         this.push({
21016             type: 'groupend',
21017             pos: this.cpos,
21018             row: this.row,
21019             col: this.col
21020         });
21021     },
21022     emitIgnorable : function ()
21023     {
21024         this.emitText();
21025         this.push({
21026             type: 'ignorable',
21027             pos: this.cpos,
21028             row: this.row,
21029             col: this.col
21030         });
21031     },
21032     emitHexChar : function ()
21033     {
21034         this.emitText();
21035         this.push({
21036             type: 'hexchar',
21037             value: this.hexChar,
21038             pos: this.cpos,
21039             row: this.row,
21040             col: this.col
21041         });
21042         this.hexChar = ''
21043     },
21044     emitError : function (message)
21045     {
21046       this.emitText();
21047       this.push({
21048             type: 'error',
21049             value: message,
21050             row: this.row,
21051             col: this.col,
21052             char: this.cpos //,
21053             //stack: new Error().stack
21054         });
21055     },
21056     emitEndParagraph : function () {
21057         this.emitText();
21058         this.push({
21059             type: 'endparagraph',
21060             pos: this.cpos,
21061             row: this.row,
21062             col: this.col
21063         });
21064     }
21065      
21066 } ;
21067 Roo.htmleditor = {};
21068  
21069 /**
21070  * @class Roo.htmleditor.Filter
21071  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21072  * @cfg {DomElement} node The node to iterate and filter
21073  * @cfg {boolean|String|Array} tag Tags to replace 
21074  * @constructor
21075  * Create a new Filter.
21076  * @param {Object} config Configuration options
21077  */
21078
21079
21080
21081 Roo.htmleditor.Filter = function(cfg) {
21082     Roo.apply(this.cfg);
21083     // this does not actually call walk as it's really just a abstract class
21084 }
21085
21086
21087 Roo.htmleditor.Filter.prototype = {
21088     
21089     node: false,
21090     
21091     tag: false,
21092
21093     // overrride to do replace comments.
21094     replaceComment : false,
21095     
21096     // overrride to do replace or do stuff with tags..
21097     replaceTag : false,
21098     
21099     walk : function(dom)
21100     {
21101         Roo.each( Array.from(dom.childNodes), function( e ) {
21102             switch(true) {
21103                 
21104                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
21105                     this.replaceComment(e);
21106                     return;
21107                 
21108                 case e.nodeType != 1: //not a node.
21109                     return;
21110                 
21111                 case this.tag === true: // everything
21112                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21113                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21114                     if (this.replaceTag && false === this.replaceTag(e)) {
21115                         return;
21116                     }
21117                     if (e.hasChildNodes()) {
21118                         this.walk(e);
21119                     }
21120                     return;
21121                 
21122                 default:    // tags .. that do not match.
21123                     if (e.hasChildNodes()) {
21124                         this.walk(e);
21125                     }
21126             }
21127             
21128         }, this);
21129         
21130     }
21131 }; 
21132
21133 /**
21134  * @class Roo.htmleditor.FilterAttributes
21135  * clean attributes and  styles including http:// etc.. in attribute
21136  * @constructor
21137 * Run a new Attribute Filter
21138 * @param {Object} config Configuration options
21139  */
21140 Roo.htmleditor.FilterAttributes = function(cfg)
21141 {
21142     Roo.apply(this, cfg);
21143     this.attrib_black = this.attrib_black || [];
21144     this.attrib_white = this.attrib_white || [];
21145
21146     this.attrib_clean = this.attrib_clean || [];
21147     this.style_white = this.style_white || [];
21148     this.style_black = this.style_black || [];
21149     this.walk(cfg.node);
21150 }
21151
21152 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21153 {
21154     tag: true, // all tags
21155     
21156     attrib_black : false, // array
21157     attrib_clean : false,
21158     attrib_white : false,
21159
21160     style_white : false,
21161     style_black : false,
21162      
21163      
21164     replaceTag : function(node)
21165     {
21166         if (!node.attributes || !node.attributes.length) {
21167             return true;
21168         }
21169         
21170         for (var i = node.attributes.length-1; i > -1 ; i--) {
21171             var a = node.attributes[i];
21172             //console.log(a);
21173             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21174                 node.removeAttribute(a.name);
21175                 continue;
21176             }
21177             
21178             
21179             
21180             if (a.name.toLowerCase().substr(0,2)=='on')  {
21181                 node.removeAttribute(a.name);
21182                 continue;
21183             }
21184             
21185             
21186             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21187                 node.removeAttribute(a.name);
21188                 continue;
21189             }
21190             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21191                 this.cleanAttr(node,a.name,a.value); // fixme..
21192                 continue;
21193             }
21194             if (a.name == 'style') {
21195                 this.cleanStyle(node,a.name,a.value);
21196                 continue;
21197             }
21198             /// clean up MS crap..
21199             // tecnically this should be a list of valid class'es..
21200             
21201             
21202             if (a.name == 'class') {
21203                 if (a.value.match(/^Mso/)) {
21204                     node.removeAttribute('class');
21205                 }
21206                 
21207                 if (a.value.match(/^body$/)) {
21208                     node.removeAttribute('class');
21209                 }
21210                 continue;
21211             }
21212             
21213             
21214             // style cleanup!?
21215             // class cleanup?
21216             
21217         }
21218         return true; // clean children
21219     },
21220         
21221     cleanAttr: function(node, n,v)
21222     {
21223         
21224         if (v.match(/^\./) || v.match(/^\//)) {
21225             return;
21226         }
21227         if (v.match(/^(http|https):\/\//)
21228             || v.match(/^mailto:/) 
21229             || v.match(/^ftp:/)
21230             || v.match(/^data:/)
21231             ) {
21232             return;
21233         }
21234         if (v.match(/^#/)) {
21235             return;
21236         }
21237         if (v.match(/^\{/)) { // allow template editing.
21238             return;
21239         }
21240 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21241         node.removeAttribute(n);
21242         
21243     },
21244     cleanStyle : function(node,  n,v)
21245     {
21246         if (v.match(/expression/)) { //XSS?? should we even bother..
21247             node.removeAttribute(n);
21248             return;
21249         }
21250         
21251         var parts = v.split(/;/);
21252         var clean = [];
21253         
21254         Roo.each(parts, function(p) {
21255             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21256             if (!p.length) {
21257                 return true;
21258             }
21259             var l = p.split(':').shift().replace(/\s+/g,'');
21260             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21261             
21262             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21263                 return true;
21264             }
21265             //Roo.log()
21266             // only allow 'c whitelisted system attributes'
21267             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21268                 return true;
21269             }
21270             
21271             
21272             clean.push(p);
21273             return true;
21274         },this);
21275         if (clean.length) { 
21276             node.setAttribute(n, clean.join(';'));
21277         } else {
21278             node.removeAttribute(n);
21279         }
21280         
21281     }
21282         
21283         
21284         
21285     
21286 });/**
21287  * @class Roo.htmleditor.FilterBlack
21288  * remove blacklisted elements.
21289  * @constructor
21290  * Run a new Blacklisted Filter
21291  * @param {Object} config Configuration options
21292  */
21293
21294 Roo.htmleditor.FilterBlack = function(cfg)
21295 {
21296     Roo.apply(this, cfg);
21297     this.walk(cfg.node);
21298 }
21299
21300 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21301 {
21302     tag : true, // all elements.
21303    
21304     replace : function(n)
21305     {
21306         n.parentNode.removeChild(n);
21307     }
21308 });
21309 /**
21310  * @class Roo.htmleditor.FilterComment
21311  * remove comments.
21312  * @constructor
21313 * Run a new Comments Filter
21314 * @param {Object} config Configuration options
21315  */
21316 Roo.htmleditor.FilterComment = function(cfg)
21317 {
21318     this.walk(cfg.node);
21319 }
21320
21321 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21322 {
21323   
21324     replaceComment : function(n)
21325     {
21326         n.parentNode.removeChild(n);
21327     }
21328 });/**
21329  * @class Roo.htmleditor.FilterKeepChildren
21330  * remove tags but keep children
21331  * @constructor
21332  * Run a new Keep Children Filter
21333  * @param {Object} config Configuration options
21334  */
21335
21336 Roo.htmleditor.FilterKeepChildren = function(cfg)
21337 {
21338     Roo.apply(this, cfg);
21339     if (this.tag === false) {
21340         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21341     }
21342     this.walk(cfg.node);
21343 }
21344
21345 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21346 {
21347     
21348   
21349     replaceTag : function(node)
21350     {
21351         // walk children...
21352         //Roo.log(node);
21353         var ar = Array.from(node.childNodes);
21354         //remove first..
21355         for (var i = 0; i < ar.length; i++) {
21356             if (ar[i].nodeType == 1) {
21357                 if (
21358                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21359                     || // array and it matches
21360                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21361                 ) {
21362                     this.replaceTag(ar[i]); // child is blacklisted as well...
21363                     continue;
21364                 }
21365             }
21366         }  
21367         ar = Array.from(node.childNodes);
21368         for (var i = 0; i < ar.length; i++) {
21369          
21370             node.removeChild(ar[i]);
21371             // what if we need to walk these???
21372             node.parentNode.insertBefore(ar[i], node);
21373             if (this.tag !== false) {
21374                 this.walk(ar[i]);
21375                 
21376             }
21377         }
21378         node.parentNode.removeChild(node);
21379         return false; // don't walk children
21380         
21381         
21382     }
21383 });/**
21384  * @class Roo.htmleditor.FilterParagraph
21385  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21386  * like on 'push' to remove the <p> tags and replace them with line breaks.
21387  * @constructor
21388  * Run a new Paragraph Filter
21389  * @param {Object} config Configuration options
21390  */
21391
21392 Roo.htmleditor.FilterParagraph = function(cfg)
21393 {
21394     // no need to apply config.
21395     this.walk(cfg.node);
21396 }
21397
21398 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21399 {
21400     
21401      
21402     tag : 'P',
21403     
21404      
21405     replaceTag : function(node)
21406     {
21407         
21408         if (node.childNodes.length == 1 &&
21409             node.childNodes[0].nodeType == 3 &&
21410             node.childNodes[0].textContent.trim().length < 1
21411             ) {
21412             // remove and replace with '<BR>';
21413             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21414             return false; // no need to walk..
21415         }
21416         var ar = Array.from(node.childNodes);
21417         for (var i = 0; i < ar.length; i++) {
21418             node.removeChild(ar[i]);
21419             // what if we need to walk these???
21420             node.parentNode.insertBefore(ar[i], node);
21421         }
21422         // now what about this?
21423         // <p> &nbsp; </p>
21424         
21425         // double BR.
21426         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21427         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21428         node.parentNode.removeChild(node);
21429         
21430         return false;
21431
21432     }
21433     
21434 });/**
21435  * @class Roo.htmleditor.FilterSpan
21436  * filter span's with no attributes out..
21437  * @constructor
21438  * Run a new Span Filter
21439  * @param {Object} config Configuration options
21440  */
21441
21442 Roo.htmleditor.FilterSpan = function(cfg)
21443 {
21444     // no need to apply config.
21445     this.walk(cfg.node);
21446 }
21447
21448 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21449 {
21450      
21451     tag : 'SPAN',
21452      
21453  
21454     replaceTag : function(node)
21455     {
21456         if (node.attributes && node.attributes.length > 0) {
21457             return true; // walk if there are any.
21458         }
21459         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21460         return false;
21461      
21462     }
21463     
21464 });/**
21465  * @class Roo.htmleditor.FilterTableWidth
21466   try and remove table width data - as that frequently messes up other stuff.
21467  * 
21468  *      was cleanTableWidths.
21469  *
21470  * Quite often pasting from word etc.. results in tables with column and widths.
21471  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21472  *
21473  * @constructor
21474  * Run a new Table Filter
21475  * @param {Object} config Configuration options
21476  */
21477
21478 Roo.htmleditor.FilterTableWidth = function(cfg)
21479 {
21480     // no need to apply config.
21481     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21482     this.walk(cfg.node);
21483 }
21484
21485 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21486 {
21487      
21488      
21489     
21490     replaceTag: function(node) {
21491         
21492         
21493       
21494         if (node.hasAttribute('width')) {
21495             node.removeAttribute('width');
21496         }
21497         
21498          
21499         if (node.hasAttribute("style")) {
21500             // pretty basic...
21501             
21502             var styles = node.getAttribute("style").split(";");
21503             var nstyle = [];
21504             Roo.each(styles, function(s) {
21505                 if (!s.match(/:/)) {
21506                     return;
21507                 }
21508                 var kv = s.split(":");
21509                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21510                     return;
21511                 }
21512                 // what ever is left... we allow.
21513                 nstyle.push(s);
21514             });
21515             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21516             if (!nstyle.length) {
21517                 node.removeAttribute('style');
21518             }
21519         }
21520         
21521         return true; // continue doing children..
21522     }
21523 });/**
21524  * @class Roo.htmleditor.FilterWord
21525  * try and clean up all the mess that Word generates.
21526  * 
21527  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21528  
21529  * @constructor
21530  * Run a new Span Filter
21531  * @param {Object} config Configuration options
21532  */
21533
21534 Roo.htmleditor.FilterWord = function(cfg)
21535 {
21536     // no need to apply config.
21537     this.walk(cfg.node);
21538 }
21539
21540 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21541 {
21542     tag: true,
21543      
21544     
21545     /**
21546      * Clean up MS wordisms...
21547      */
21548     replaceTag : function(node)
21549     {
21550          
21551         // no idea what this does - span with text, replaceds with just text.
21552         if(
21553                 node.nodeName == 'SPAN' &&
21554                 !node.hasAttributes() &&
21555                 node.childNodes.length == 1 &&
21556                 node.firstChild.nodeName == "#text"  
21557         ) {
21558             var textNode = node.firstChild;
21559             node.removeChild(textNode);
21560             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21561                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21562             }
21563             node.parentNode.insertBefore(textNode, node);
21564             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21565                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21566             }
21567             
21568             node.parentNode.removeChild(node);
21569             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21570         }
21571         
21572    
21573         
21574         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21575             node.parentNode.removeChild(node);
21576             return false; // dont do chidlren
21577         }
21578         //Roo.log(node.tagName);
21579         // remove - but keep children..
21580         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21581             //Roo.log('-- removed');
21582             while (node.childNodes.length) {
21583                 var cn = node.childNodes[0];
21584                 node.removeChild(cn);
21585                 node.parentNode.insertBefore(cn, node);
21586                 // move node to parent - and clean it..
21587                 this.replaceTag(cn);
21588             }
21589             node.parentNode.removeChild(node);
21590             /// no need to iterate chidlren = it's got none..
21591             //this.iterateChildren(node, this.cleanWord);
21592             return false; // no need to iterate children.
21593         }
21594         // clean styles
21595         if (node.className.length) {
21596             
21597             var cn = node.className.split(/\W+/);
21598             var cna = [];
21599             Roo.each(cn, function(cls) {
21600                 if (cls.match(/Mso[a-zA-Z]+/)) {
21601                     return;
21602                 }
21603                 cna.push(cls);
21604             });
21605             node.className = cna.length ? cna.join(' ') : '';
21606             if (!cna.length) {
21607                 node.removeAttribute("class");
21608             }
21609         }
21610         
21611         if (node.hasAttribute("lang")) {
21612             node.removeAttribute("lang");
21613         }
21614         
21615         if (node.hasAttribute("style")) {
21616             
21617             var styles = node.getAttribute("style").split(";");
21618             var nstyle = [];
21619             Roo.each(styles, function(s) {
21620                 if (!s.match(/:/)) {
21621                     return;
21622                 }
21623                 var kv = s.split(":");
21624                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21625                     return;
21626                 }
21627                 // what ever is left... we allow.
21628                 nstyle.push(s);
21629             });
21630             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21631             if (!nstyle.length) {
21632                 node.removeAttribute('style');
21633             }
21634         }
21635         return true; // do children
21636         
21637         
21638         
21639     }
21640 });
21641 /**
21642  * @class Roo.htmleditor.FilterStyleToTag
21643  * part of the word stuff... - certain 'styles' should be converted to tags.
21644  * eg.
21645  *   font-weight: bold -> bold
21646  *   ?? super / subscrit etc..
21647  * 
21648  * @constructor
21649 * Run a new style to tag filter.
21650 * @param {Object} config Configuration options
21651  */
21652 Roo.htmleditor.FilterStyleToTag = function(cfg)
21653 {
21654     
21655     this.tags = {
21656         B  : [ 'fontWeight' , 'bold'],
21657         I :  [ 'fontStyle' , 'italic'],
21658         //pre :  [ 'font-style' , 'italic'],
21659         // h1.. h6 ?? font-size?
21660         SUP : [ 'verticalAlign' , 'super' ],
21661         SUB : [ 'verticalAlign' , 'sub' ]
21662         
21663         
21664     };
21665     
21666     Roo.apply(this, cfg);
21667      
21668     
21669     this.walk(cfg.node);
21670     
21671     
21672     
21673 }
21674
21675
21676 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21677 {
21678     tag: true, // all tags
21679     
21680     tags : false,
21681     
21682     
21683     replaceTag : function(node)
21684     {
21685         
21686         
21687         if (node.getAttribute("style") === null) {
21688             return true;
21689         }
21690         var inject = [];
21691         for (var k in this.tags) {
21692             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21693                 inject.push(k);
21694                 node.style.removeProperty(this.tags[k][0]);
21695             }
21696         }
21697         if (!inject.length) {
21698             return true; 
21699         }
21700         var cn = Array.from(node.childNodes);
21701         var nn = node;
21702         Roo.each(inject, function(t) {
21703             var nc = node.ownerDocument.createelement(t);
21704             nn.appendChild(nc);
21705             nn = nc;
21706         });
21707         for(var i = 0;i < cn.length;cn++) {
21708             node.removeChild(cn[i]);
21709             nn.appendChild(cn[i]);
21710         }
21711         return true /// iterate thru
21712     }
21713     
21714 })/**
21715  * @class Roo.htmleditor.FilterLongBr
21716  * BR/BR/BR - keep a maximum of 2...
21717  * @constructor
21718  * Run a new Long BR Filter
21719  * @param {Object} config Configuration options
21720  */
21721
21722 Roo.htmleditor.FilterLongBr = function(cfg)
21723 {
21724     // no need to apply config.
21725     this.walk(cfg.node);
21726 }
21727
21728 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21729 {
21730     
21731      
21732     tag : 'BR',
21733     
21734      
21735     replaceTag : function(node)
21736     {
21737         
21738         var ps = node.nextSibling;
21739         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21740             ps = ps.nextSibling;
21741         }
21742         
21743         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21744             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21745             return false;
21746         }
21747         
21748         if (!ps || ps.nodeType != 1) {
21749             return false;
21750         }
21751         
21752         if (!ps || ps.tagName != 'BR') {
21753            
21754             return false;
21755         }
21756         
21757         
21758         
21759         
21760         
21761         if (!node.previousSibling) {
21762             return false;
21763         }
21764         var ps = node.previousSibling;
21765         
21766         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21767             ps = ps.previousSibling;
21768         }
21769         if (!ps || ps.nodeType != 1) {
21770             return false;
21771         }
21772         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21773         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21774             return false;
21775         }
21776         
21777         node.parentNode.removeChild(node); // remove me...
21778         
21779         return false; // no need to do children
21780
21781     }
21782     
21783 });
21784 /**
21785  * @class Roo.htmleditor.Tidy
21786  * Tidy HTML 
21787  * @cfg {Roo.HtmlEditorCore} core the editor.
21788  * @constructor
21789  * Create a new Filter.
21790  * @param {Object} config Configuration options
21791  */
21792
21793
21794 Roo.htmleditor.Tidy = function(cfg) {
21795     Roo.apply(this, cfg);
21796     
21797     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
21798      
21799 }
21800
21801 Roo.htmleditor.Tidy.toString = function(node)
21802 {
21803     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
21804 }
21805
21806 Roo.htmleditor.Tidy.prototype = {
21807     
21808     
21809     wrap : function(s) {
21810         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
21811     },
21812
21813     
21814     tidy : function(node, indent) {
21815      
21816         if  (node.nodeType == 3) {
21817             // text.
21818             
21819             
21820             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
21821                 
21822             
21823         }
21824         
21825         if  (node.nodeType != 1) {
21826             return '';
21827         }
21828         
21829         
21830         
21831         if (node.tagName == 'BODY') {
21832             
21833             return this.cn(node, '');
21834         }
21835              
21836              // Prints the node tagName, such as <A>, <IMG>, etc
21837         var ret = "<" + node.tagName +  this.attr(node) ;
21838         
21839         // elements with no children..
21840         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
21841                 return ret + '/>';
21842         }
21843         ret += '>';
21844         
21845         
21846         var cindent = indent === false ? '' : (indent + '  ');
21847         // tags where we will not pad the children.. (inline text tags etc..)
21848         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
21849             cindent = false;
21850             
21851             
21852         }
21853         
21854         var cn = this.cn(node, cindent );
21855         
21856         return ret + cn  + '</' + node.tagName + '>';
21857         
21858     },
21859     cn: function(node, indent)
21860     {
21861         var ret = [];
21862         
21863         var ar = Array.from(node.childNodes);
21864         for (var i = 0 ; i < ar.length ; i++) {
21865             
21866             
21867             
21868             if (indent !== false   // indent==false preservies everything
21869                 && i > 0
21870                 && ar[i].nodeType == 3 
21871                 && ar[i].nodeValue.length > 0
21872                 && ar[i].nodeValue.match(/^\s+/)
21873             ) {
21874                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
21875                     ret.pop(); // remove line break from last?
21876                 }
21877                 
21878                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
21879             }
21880             if (indent !== false
21881                 && ar[i].nodeType == 1 // element - and indent is not set... 
21882             ) {
21883                 ret.push("\n" + indent); 
21884             }
21885             
21886             ret.push(this.tidy(ar[i], indent));
21887             // text + trailing indent 
21888             if (indent !== false
21889                 && ar[i].nodeType == 3
21890                 && ar[i].nodeValue.length > 0
21891                 && ar[i].nodeValue.match(/\s+$/)
21892             ){
21893                 ret.push("\n" + indent); 
21894             }
21895             
21896             
21897             
21898             
21899         }
21900         // what if all text?
21901         
21902         
21903         return ret.join('');
21904     },
21905     
21906          
21907         
21908     attr : function(node)
21909     {
21910         var attr = [];
21911         for(i = 0; i < node.attributes.length;i++) {
21912             
21913             // skip empty values?
21914             if (!node.attributes.item(i).value.length) {
21915                 continue;
21916             }
21917             attr.push(  node.attributes.item(i).name + '="' +
21918                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
21919             );
21920         }
21921         return attr.length ? (' ' + attr.join(' ') ) : '';
21922         
21923     }
21924     
21925     
21926     
21927 }
21928 /**
21929  * @class Roo.htmleditor.KeyEnter
21930  * Handle Enter press..
21931  * @cfg {Roo.HtmlEditorCore} core the editor.
21932  * @constructor
21933  * Create a new Filter.
21934  * @param {Object} config Configuration options
21935  */
21936
21937
21938
21939 Roo.htmleditor.KeyEnter = function(cfg) {
21940     Roo.apply(this, cfg);
21941     // this does not actually call walk as it's really just a abstract class
21942  
21943     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
21944 }
21945
21946
21947 Roo.htmleditor.KeyEnter.prototype = {
21948     
21949     core : false,
21950     
21951     keypress : function(e) {
21952         if (e.charCode != 13) {
21953             return true;
21954         }
21955         e.preventDefault();
21956         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
21957         var doc = this.core.doc;
21958         
21959         var docFragment = doc.createDocumentFragment();
21960     
21961         //add a new line
21962         var newEle = doc.createTextNode('\n');
21963         docFragment.appendChild(newEle);
21964     
21965     
21966         var range = this.core.win.getSelection().getRangeAt(0);
21967         var n = range.commonAncestorContainer ;
21968         while (n && n.nodeType != 1) {
21969             n  = n.parentNode;
21970         }
21971         var li = false;
21972         if (n && n.tagName == 'UL') {
21973             li = doc.createElement('LI');
21974             n.appendChild(li);
21975             
21976         }
21977         if (n && n.tagName == 'LI') {
21978             li = doc.createElement('LI');
21979             if (n.nextSibling) {
21980                 n.parentNode.insertBefore(li, n.firstSibling);
21981                 
21982             } else {
21983                 n.parentNode.appendChild(li);
21984             }
21985         }
21986         if (li) {   
21987             range = doc.createRange();
21988             range.setStartAfter(li);
21989             range.collapse(true);
21990         
21991             //make the cursor there
21992             var sel = this.core.win.getSelection();
21993             sel.removeAllRanges();
21994             sel.addRange(range);
21995             return false;
21996             
21997             
21998         }
21999         //add the br, or p, or something else
22000         newEle = doc.createElement('br');
22001         docFragment.appendChild(newEle);
22002     
22003         //make the br replace selection
22004         
22005         range.deleteContents();
22006         
22007         range.insertNode(docFragment);
22008     
22009         //create a new range
22010         range = doc.createRange();
22011         range.setStartAfter(newEle);
22012         range.collapse(true);
22013     
22014         //make the cursor there
22015         var sel = this.core.win.getSelection();
22016         sel.removeAllRanges();
22017         sel.addRange(range);
22018     
22019         return false;
22020          
22021     }
22022 };
22023      
22024 /**
22025  * @class Roo.htmleditor.Block
22026  * Base class for html editor blocks - do not use it directly .. extend it..
22027  * @cfg {DomElement} node The node to apply stuff to.
22028  * @cfg {String} friendly_name the name that appears in the context bar about this block
22029  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
22030  
22031  * @constructor
22032  * Create a new Filter.
22033  * @param {Object} config Configuration options
22034  */
22035
22036 Roo.htmleditor.Block  = function(cfg)
22037 {
22038     // do nothing .. should not be called really.
22039 }
22040
22041 Roo.htmleditor.Block.factory = function(node)
22042 {
22043     
22044     var id = Roo.get(node).id;
22045     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
22046         Roo.htmleditor.Block.cache[id].readElement();
22047         return Roo.htmleditor.Block.cache[id];
22048     }
22049     
22050     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
22051     if (typeof(cls) == 'undefined') {
22052         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
22053         return false;
22054     }
22055     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
22056     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
22057 };
22058 // question goes here... do we need to clear out this cache sometimes?
22059 // or show we make it relivant to the htmleditor.
22060 Roo.htmleditor.Block.cache = {};
22061
22062 Roo.htmleditor.Block.prototype = {
22063     
22064     node : false,
22065     
22066      // used by context menu
22067     friendly_name : 'Image with caption',
22068     
22069     context : false,
22070     /**
22071      * Update a node with values from this object
22072      * @param {DomElement} node
22073      */
22074     updateElement : function(node)
22075     {
22076         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
22077     },
22078      /**
22079      * convert to plain HTML for calling insertAtCursor..
22080      */
22081     toHTML : function()
22082     {
22083         return Roo.DomHelper.markup(this.toObject());
22084     },
22085     /**
22086      * used by readEleemnt to extract data from a node
22087      * may need improving as it's pretty basic
22088      
22089      * @param {DomElement} node
22090      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
22091      * @param {String} attribute (use html - for contents, or style for using next param as style)
22092      * @param {String} style the style property - eg. text-align
22093      */
22094     getVal : function(node, tag, attr, style)
22095     {
22096         var n = node;
22097         if (tag !== true && n.tagName != tag.toUpperCase()) {
22098             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
22099             // but kiss for now.
22100             n = node.getElementsByTagName(tag).item(0);
22101         }
22102         if (attr == 'html') {
22103             return n.innerHTML;
22104         }
22105         if (attr == 'style') {
22106             return Roo.get(n).getStyle(style);
22107         }
22108         
22109         return Roo.get(n).attr(attr);
22110             
22111     },
22112     /**
22113      * create a DomHelper friendly object - for use with 
22114      * Roo.DomHelper.markup / overwrite / etc..
22115      * (override this)
22116      */
22117     toObject : function()
22118     {
22119         return {};
22120     },
22121       /**
22122      * Read a node that has a 'data-block' property - and extract the values from it.
22123      * @param {DomElement} node - the node
22124      */
22125     readElement : function(node)
22126     {
22127         
22128     } 
22129     
22130     
22131 };
22132
22133  
22134
22135 /**
22136  * @class Roo.htmleditor.BlockFigure
22137  * Block that has an image and a figcaption
22138  * @cfg {String} image_src the url for the image
22139  * @cfg {String} align (left|right) alignment for the block default left
22140  * @cfg {String} text_align (left|right) alignment for the text caption default left.
22141  * @cfg {String} caption the text to appear below  (and in the alt tag)
22142  * @cfg {String|number} image_width the width of the image number or %?
22143  * @cfg {String|number} image_height the height of the image number or %?
22144  * 
22145  * @constructor
22146  * Create a new Filter.
22147  * @param {Object} config Configuration options
22148  */
22149
22150 Roo.htmleditor.BlockFigure = function(cfg)
22151 {
22152     if (cfg.node) {
22153         this.readElement(cfg.node);
22154         this.updateElement(cfg.node);
22155     }
22156     Roo.apply(this, cfg);
22157 }
22158 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
22159  
22160     
22161     // setable values.
22162     image_src: '',
22163     
22164     align: 'left',
22165     caption : '',
22166     text_align: 'left',
22167     
22168     width : '46%',
22169     margin: '2%',
22170     
22171     // used by context menu
22172     friendly_name : 'Image with caption',
22173     
22174     context : { // ?? static really
22175         width : {
22176             title: "Width",
22177             width: 40
22178             // ?? number
22179         },
22180         margin : {
22181             title: "Margin",
22182             width: 40
22183             // ?? number
22184         },
22185         align: {
22186             title: "Align",
22187             opts : [[ "left"],[ "right"]],
22188             width : 80
22189             
22190         },
22191         text_align: {
22192             title: "Caption Align",
22193             opts : [ [ "left"],[ "right"],[ "center"]],
22194             width : 80
22195         },
22196         
22197        
22198         image_src : {
22199             title: "Src",
22200             width: 220
22201         }
22202     },
22203     /**
22204      * create a DomHelper friendly object - for use with
22205      * Roo.DomHelper.markup / overwrite / etc..
22206      */
22207     toObject : function()
22208     {
22209         var d = document.createElement('div');
22210         d.innerHTML = this.caption;
22211         
22212         return {
22213             tag: 'figure',
22214             'data-block' : 'Figure',
22215             contenteditable : 'false',
22216             style : {
22217                 display: 'table',
22218                 float :  this.align ,
22219                 width :  this.width,
22220                 margin:  this.margin
22221             },
22222             cn : [
22223                 {
22224                     tag : 'img',
22225                     src : this.image_src,
22226                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
22227                     style: {
22228                         width: '100%'
22229                     }
22230                 },
22231                 {
22232                     tag: 'figcaption',
22233                     contenteditable : true,
22234                     style : {
22235                         'text-align': this.text_align
22236                     },
22237                     html : this.caption
22238                     
22239                 }
22240             ]
22241         };
22242     },
22243     
22244     readElement : function(node)
22245     {
22246         this.image_src = this.getVal(node, 'img', 'src');
22247         this.align = this.getVal(node, 'figure', 'style', 'float');
22248         this.caption = this.getVal(node, 'figcaption', 'html');
22249         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
22250         this.width = this.getVal(node, 'figure', 'style', 'width');
22251         this.margin = this.getVal(node, 'figure', 'style', 'margin');
22252         
22253     } 
22254     
22255   
22256    
22257      
22258     
22259     
22260     
22261     
22262 })
22263
22264 //<script type="text/javascript">
22265
22266 /*
22267  * Based  Ext JS Library 1.1.1
22268  * Copyright(c) 2006-2007, Ext JS, LLC.
22269  * LGPL
22270  *
22271  */
22272  
22273 /**
22274  * @class Roo.HtmlEditorCore
22275  * @extends Roo.Component
22276  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22277  *
22278  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22279  */
22280
22281 Roo.HtmlEditorCore = function(config){
22282     
22283     
22284     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22285     
22286     
22287     this.addEvents({
22288         /**
22289          * @event initialize
22290          * Fires when the editor is fully initialized (including the iframe)
22291          * @param {Roo.HtmlEditorCore} this
22292          */
22293         initialize: true,
22294         /**
22295          * @event activate
22296          * Fires when the editor is first receives the focus. Any insertion must wait
22297          * until after this event.
22298          * @param {Roo.HtmlEditorCore} this
22299          */
22300         activate: true,
22301          /**
22302          * @event beforesync
22303          * Fires before the textarea is updated with content from the editor iframe. Return false
22304          * to cancel the sync.
22305          * @param {Roo.HtmlEditorCore} this
22306          * @param {String} html
22307          */
22308         beforesync: true,
22309          /**
22310          * @event beforepush
22311          * Fires before the iframe editor is updated with content from the textarea. Return false
22312          * to cancel the push.
22313          * @param {Roo.HtmlEditorCore} this
22314          * @param {String} html
22315          */
22316         beforepush: true,
22317          /**
22318          * @event sync
22319          * Fires when the textarea is updated with content from the editor iframe.
22320          * @param {Roo.HtmlEditorCore} this
22321          * @param {String} html
22322          */
22323         sync: true,
22324          /**
22325          * @event push
22326          * Fires when the iframe editor is updated with content from the textarea.
22327          * @param {Roo.HtmlEditorCore} this
22328          * @param {String} html
22329          */
22330         push: true,
22331         
22332         /**
22333          * @event editorevent
22334          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22335          * @param {Roo.HtmlEditorCore} this
22336          */
22337         editorevent: true
22338         
22339     });
22340     
22341     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22342     
22343     // defaults : white / black...
22344     this.applyBlacklists();
22345     
22346     
22347     
22348 };
22349
22350
22351 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22352
22353
22354      /**
22355      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22356      */
22357     
22358     owner : false,
22359     
22360      /**
22361      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22362      *                        Roo.resizable.
22363      */
22364     resizable : false,
22365      /**
22366      * @cfg {Number} height (in pixels)
22367      */   
22368     height: 300,
22369    /**
22370      * @cfg {Number} width (in pixels)
22371      */   
22372     width: 500,
22373     
22374     /**
22375      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22376      * 
22377      */
22378     stylesheets: false,
22379     
22380     /**
22381      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22382      */
22383     allowComments: false,
22384     // id of frame..
22385     frameId: false,
22386     
22387     // private properties
22388     validationEvent : false,
22389     deferHeight: true,
22390     initialized : false,
22391     activated : false,
22392     sourceEditMode : false,
22393     onFocus : Roo.emptyFn,
22394     iframePad:3,
22395     hideMode:'offsets',
22396     
22397     clearUp: true,
22398     
22399     // blacklist + whitelisted elements..
22400     black: false,
22401     white: false,
22402      
22403     bodyCls : '',
22404
22405     /**
22406      * Protected method that will not generally be called directly. It
22407      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22408      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22409      */
22410     getDocMarkup : function(){
22411         // body styles..
22412         var st = '';
22413         
22414         // inherit styels from page...?? 
22415         if (this.stylesheets === false) {
22416             
22417             Roo.get(document.head).select('style').each(function(node) {
22418                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22419             });
22420             
22421             Roo.get(document.head).select('link').each(function(node) { 
22422                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22423             });
22424             
22425         } else if (!this.stylesheets.length) {
22426                 // simple..
22427                 st = '<style type="text/css">' +
22428                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22429                    '</style>';
22430         } else {
22431             for (var i in this.stylesheets) {
22432                 if (typeof(this.stylesheets[i]) != 'string') {
22433                     continue;
22434                 }
22435                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
22436             }
22437             
22438         }
22439         
22440         st +=  '<style type="text/css">' +
22441             'IMG { cursor: pointer } ' +
22442         '</style>';
22443
22444         var cls = 'roo-htmleditor-body';
22445         
22446         if(this.bodyCls.length){
22447             cls += ' ' + this.bodyCls;
22448         }
22449         
22450         return '<html><head>' + st  +
22451             //<style type="text/css">' +
22452             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22453             //'</style>' +
22454             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22455     },
22456
22457     // private
22458     onRender : function(ct, position)
22459     {
22460         var _t = this;
22461         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22462         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22463         
22464         
22465         this.el.dom.style.border = '0 none';
22466         this.el.dom.setAttribute('tabIndex', -1);
22467         this.el.addClass('x-hidden hide');
22468         
22469         
22470         
22471         if(Roo.isIE){ // fix IE 1px bogus margin
22472             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22473         }
22474        
22475         
22476         this.frameId = Roo.id();
22477         
22478          
22479         
22480         var iframe = this.owner.wrap.createChild({
22481             tag: 'iframe',
22482             cls: 'form-control', // bootstrap..
22483             id: this.frameId,
22484             name: this.frameId,
22485             frameBorder : 'no',
22486             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22487         }, this.el
22488         );
22489         
22490         
22491         this.iframe = iframe.dom;
22492
22493         this.assignDocWin();
22494         
22495         this.doc.designMode = 'on';
22496        
22497         this.doc.open();
22498         this.doc.write(this.getDocMarkup());
22499         this.doc.close();
22500
22501         
22502         var task = { // must defer to wait for browser to be ready
22503             run : function(){
22504                 //console.log("run task?" + this.doc.readyState);
22505                 this.assignDocWin();
22506                 if(this.doc.body || this.doc.readyState == 'complete'){
22507                     try {
22508                         this.doc.designMode="on";
22509                     } catch (e) {
22510                         return;
22511                     }
22512                     Roo.TaskMgr.stop(task);
22513                     this.initEditor.defer(10, this);
22514                 }
22515             },
22516             interval : 10,
22517             duration: 10000,
22518             scope: this
22519         };
22520         Roo.TaskMgr.start(task);
22521
22522     },
22523
22524     // private
22525     onResize : function(w, h)
22526     {
22527          Roo.log('resize: ' +w + ',' + h );
22528         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22529         if(!this.iframe){
22530             return;
22531         }
22532         if(typeof w == 'number'){
22533             
22534             this.iframe.style.width = w + 'px';
22535         }
22536         if(typeof h == 'number'){
22537             
22538             this.iframe.style.height = h + 'px';
22539             if(this.doc){
22540                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22541             }
22542         }
22543         
22544     },
22545
22546     /**
22547      * Toggles the editor between standard and source edit mode.
22548      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22549      */
22550     toggleSourceEdit : function(sourceEditMode){
22551         
22552         this.sourceEditMode = sourceEditMode === true;
22553         
22554         if(this.sourceEditMode){
22555  
22556             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
22557             
22558         }else{
22559             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
22560             //this.iframe.className = '';
22561             this.deferFocus();
22562         }
22563         //this.setSize(this.owner.wrap.getSize());
22564         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22565     },
22566
22567     
22568   
22569
22570     /**
22571      * Protected method that will not generally be called directly. If you need/want
22572      * custom HTML cleanup, this is the method you should override.
22573      * @param {String} html The HTML to be cleaned
22574      * return {String} The cleaned HTML
22575      */
22576     cleanHtml : function(html){
22577         html = String(html);
22578         if(html.length > 5){
22579             if(Roo.isSafari){ // strip safari nonsense
22580                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22581             }
22582         }
22583         if(html == '&nbsp;'){
22584             html = '';
22585         }
22586         return html;
22587     },
22588
22589     /**
22590      * HTML Editor -> Textarea
22591      * Protected method that will not generally be called directly. Syncs the contents
22592      * of the editor iframe with the textarea.
22593      */
22594     syncValue : function()
22595     {
22596         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
22597         if(this.initialized){
22598             var bd = (this.doc.body || this.doc.documentElement);
22599             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22600             
22601             // not sure if this is really the place for this
22602             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
22603             // this has to update attributes that get duped.. like alt and caption..
22604             
22605             
22606             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22607             //     Roo.htmleditor.Block.factory(e);
22608             //},this);
22609             
22610             
22611             var div = document.createElement('div');
22612             div.innerHTML = bd.innerHTML;
22613             // remove content editable. (blocks)
22614             
22615            
22616             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
22617             //?? tidy?
22618             var html = div.innerHTML;
22619             if(Roo.isSafari){
22620                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22621                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22622                 if(m && m[1]){
22623                     html = '<div style="'+m[0]+'">' + html + '</div>';
22624                 }
22625             }
22626             html = this.cleanHtml(html);
22627             // fix up the special chars.. normaly like back quotes in word...
22628             // however we do not want to do this with chinese..
22629             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22630                 
22631                 var cc = match.charCodeAt();
22632
22633                 // Get the character value, handling surrogate pairs
22634                 if (match.length == 2) {
22635                     // It's a surrogate pair, calculate the Unicode code point
22636                     var high = match.charCodeAt(0) - 0xD800;
22637                     var low  = match.charCodeAt(1) - 0xDC00;
22638                     cc = (high * 0x400) + low + 0x10000;
22639                 }  else if (
22640                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22641                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22642                     (cc >= 0xf900 && cc < 0xfb00 )
22643                 ) {
22644                         return match;
22645                 }  
22646          
22647                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22648                 return "&#" + cc + ";";
22649                 
22650                 
22651             });
22652             
22653             
22654              
22655             if(this.owner.fireEvent('beforesync', this, html) !== false){
22656                 this.el.dom.value = html;
22657                 this.owner.fireEvent('sync', this, html);
22658             }
22659         }
22660     },
22661
22662     /**
22663      * TEXTAREA -> EDITABLE
22664      * Protected method that will not generally be called directly. Pushes the value of the textarea
22665      * into the iframe editor.
22666      */
22667     pushValue : function()
22668     {
22669         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
22670         if(this.initialized){
22671             var v = this.el.dom.value.trim();
22672             
22673             
22674             if(this.owner.fireEvent('beforepush', this, v) !== false){
22675                 var d = (this.doc.body || this.doc.documentElement);
22676                 d.innerHTML = v;
22677                  
22678                 this.el.dom.value = d.innerHTML;
22679                 this.owner.fireEvent('push', this, v);
22680             }
22681             
22682             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22683                 
22684                 Roo.htmleditor.Block.factory(e);
22685                 
22686             },this);
22687             var lc = this.doc.body.lastChild;
22688             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
22689                 // add an extra line at the end.
22690                 this.doc.body.appendChild(this.doc.createElement('br'));
22691             }
22692             
22693             
22694         }
22695     },
22696
22697     // private
22698     deferFocus : function(){
22699         this.focus.defer(10, this);
22700     },
22701
22702     // doc'ed in Field
22703     focus : function(){
22704         if(this.win && !this.sourceEditMode){
22705             this.win.focus();
22706         }else{
22707             this.el.focus();
22708         }
22709     },
22710     
22711     assignDocWin: function()
22712     {
22713         var iframe = this.iframe;
22714         
22715          if(Roo.isIE){
22716             this.doc = iframe.contentWindow.document;
22717             this.win = iframe.contentWindow;
22718         } else {
22719 //            if (!Roo.get(this.frameId)) {
22720 //                return;
22721 //            }
22722 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22723 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22724             
22725             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22726                 return;
22727             }
22728             
22729             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22730             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22731         }
22732     },
22733     
22734     // private
22735     initEditor : function(){
22736         //console.log("INIT EDITOR");
22737         this.assignDocWin();
22738         
22739         
22740         
22741         this.doc.designMode="on";
22742         this.doc.open();
22743         this.doc.write(this.getDocMarkup());
22744         this.doc.close();
22745         
22746         var dbody = (this.doc.body || this.doc.documentElement);
22747         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22748         // this copies styles from the containing element into thsi one..
22749         // not sure why we need all of this..
22750         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22751         
22752         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22753         //ss['background-attachment'] = 'fixed'; // w3c
22754         dbody.bgProperties = 'fixed'; // ie
22755         //Roo.DomHelper.applyStyles(dbody, ss);
22756         Roo.EventManager.on(this.doc, {
22757             //'mousedown': this.onEditorEvent,
22758             'mouseup': this.onEditorEvent,
22759             'dblclick': this.onEditorEvent,
22760             'click': this.onEditorEvent,
22761             'keyup': this.onEditorEvent,
22762             
22763             buffer:100,
22764             scope: this
22765         });
22766         Roo.EventManager.on(this.doc, {
22767             'paste': this.onPasteEvent,
22768             scope : this
22769         });
22770         if(Roo.isGecko){
22771             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22772         }
22773         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22774             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22775         }
22776         this.initialized = true;
22777
22778         
22779         // initialize special key events - enter
22780         new Roo.htmleditor.KeyEnter({core : this});
22781         
22782          
22783         
22784         this.owner.fireEvent('initialize', this);
22785         this.pushValue();
22786     },
22787     
22788     onPasteEvent : function(e,v)
22789     {
22790         // I think we better assume paste is going to be a dirty load of rubish from word..
22791         
22792         // even pasting into a 'email version' of this widget will have to clean up that mess.
22793         var cd = (e.browserEvent.clipboardData || window.clipboardData);
22794         
22795         // check what type of paste - if it's an image, then handle it differently.
22796         if (cd.files.length > 0) {
22797             // pasting images?
22798             var urlAPI = (window.createObjectURL && window) || 
22799                 (window.URL && URL.revokeObjectURL && URL) || 
22800                 (window.webkitURL && webkitURL);
22801     
22802             var url = urlAPI.createObjectURL( cd.files[0]);
22803             this.insertAtCursor('<img src=" + url + ">');
22804             return false;
22805         }
22806         
22807         var html = cd.getData('text/html'); // clipboard event
22808         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
22809         var images = parser.doc.getElementsByType('pict');
22810         Roo.log(images);
22811         //Roo.log(imgs);
22812         // fixme..
22813         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
22814                        .map(function(g) { return g.toDataURL(); });
22815         
22816         
22817         html = this.cleanWordChars(html);
22818         
22819         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
22820         
22821         if (images.length > 0) {
22822             Roo.each(d.getElementsByTagName('img'), function(img, i) {
22823                 img.setAttribute('src', images[i]);
22824             });
22825         }
22826         
22827       
22828         new Roo.htmleditor.FilterStyleToTag({ node : d });
22829         new Roo.htmleditor.FilterAttributes({
22830             node : d,
22831             attrib_white : ['href', 'src', 'name', 'align'],
22832             attrib_clean : ['href', 'src' ] 
22833         });
22834         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
22835         // should be fonts..
22836         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
22837         new Roo.htmleditor.FilterParagraph({ node : d });
22838         new Roo.htmleditor.FilterSpan({ node : d });
22839         new Roo.htmleditor.FilterLongBr({ node : d });
22840         
22841         
22842         
22843         this.insertAtCursor(d.innerHTML);
22844         
22845         e.preventDefault();
22846         return false;
22847         // default behaveiour should be our local cleanup paste? (optional?)
22848         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
22849         //this.owner.fireEvent('paste', e, v);
22850     },
22851     // private
22852     onDestroy : function(){
22853         
22854         
22855         
22856         if(this.rendered){
22857             
22858             //for (var i =0; i < this.toolbars.length;i++) {
22859             //    // fixme - ask toolbars for heights?
22860             //    this.toolbars[i].onDestroy();
22861            // }
22862             
22863             //this.wrap.dom.innerHTML = '';
22864             //this.wrap.remove();
22865         }
22866     },
22867
22868     // private
22869     onFirstFocus : function(){
22870         
22871         this.assignDocWin();
22872         
22873         
22874         this.activated = true;
22875          
22876     
22877         if(Roo.isGecko){ // prevent silly gecko errors
22878             this.win.focus();
22879             var s = this.win.getSelection();
22880             if(!s.focusNode || s.focusNode.nodeType != 3){
22881                 var r = s.getRangeAt(0);
22882                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22883                 r.collapse(true);
22884                 this.deferFocus();
22885             }
22886             try{
22887                 this.execCmd('useCSS', true);
22888                 this.execCmd('styleWithCSS', false);
22889             }catch(e){}
22890         }
22891         this.owner.fireEvent('activate', this);
22892     },
22893
22894     // private
22895     adjustFont: function(btn){
22896         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22897         //if(Roo.isSafari){ // safari
22898         //    adjust *= 2;
22899        // }
22900         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22901         if(Roo.isSafari){ // safari
22902             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22903             v =  (v < 10) ? 10 : v;
22904             v =  (v > 48) ? 48 : v;
22905             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22906             
22907         }
22908         
22909         
22910         v = Math.max(1, v+adjust);
22911         
22912         this.execCmd('FontSize', v  );
22913     },
22914
22915     onEditorEvent : function(e)
22916     {
22917         this.owner.fireEvent('editorevent', this, e);
22918       //  this.updateToolbar();
22919         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22920     },
22921
22922     insertTag : function(tg)
22923     {
22924         // could be a bit smarter... -> wrap the current selected tRoo..
22925         if (tg.toLowerCase() == 'span' ||
22926             tg.toLowerCase() == 'code' ||
22927             tg.toLowerCase() == 'sup' ||
22928             tg.toLowerCase() == 'sub' 
22929             ) {
22930             
22931             range = this.createRange(this.getSelection());
22932             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22933             wrappingNode.appendChild(range.extractContents());
22934             range.insertNode(wrappingNode);
22935
22936             return;
22937             
22938             
22939             
22940         }
22941         this.execCmd("formatblock",   tg);
22942         
22943     },
22944     
22945     insertText : function(txt)
22946     {
22947         
22948         
22949         var range = this.createRange();
22950         range.deleteContents();
22951                //alert(Sender.getAttribute('label'));
22952                
22953         range.insertNode(this.doc.createTextNode(txt));
22954     } ,
22955     
22956      
22957
22958     /**
22959      * Executes a Midas editor command on the editor document and performs necessary focus and
22960      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22961      * @param {String} cmd The Midas command
22962      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22963      */
22964     relayCmd : function(cmd, value){
22965         this.win.focus();
22966         this.execCmd(cmd, value);
22967         this.owner.fireEvent('editorevent', this);
22968         //this.updateToolbar();
22969         this.owner.deferFocus();
22970     },
22971
22972     /**
22973      * Executes a Midas editor command directly on the editor document.
22974      * For visual commands, you should use {@link #relayCmd} instead.
22975      * <b>This should only be called after the editor is initialized.</b>
22976      * @param {String} cmd The Midas command
22977      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22978      */
22979     execCmd : function(cmd, value){
22980         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22981         this.syncValue();
22982     },
22983  
22984  
22985    
22986     /**
22987      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22988      * to insert tRoo.
22989      * @param {String} text | dom node.. 
22990      */
22991     insertAtCursor : function(text)
22992     {
22993         
22994         if(!this.activated){
22995             return;
22996         }
22997          
22998         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22999             this.win.focus();
23000             
23001             
23002             // from jquery ui (MIT licenced)
23003             var range, node;
23004             var win = this.win;
23005             
23006             if (win.getSelection && win.getSelection().getRangeAt) {
23007                 
23008                 // delete the existing?
23009                 
23010                 this.createRange(this.getSelection()).deleteContents();
23011                 range = win.getSelection().getRangeAt(0);
23012                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23013                 range.insertNode(node);
23014             } else if (win.document.selection && win.document.selection.createRange) {
23015                 // no firefox support
23016                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23017                 win.document.selection.createRange().pasteHTML(txt);
23018             } else {
23019                 // no firefox support
23020                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23021                 this.execCmd('InsertHTML', txt);
23022             } 
23023             
23024             this.syncValue();
23025             
23026             this.deferFocus();
23027         }
23028     },
23029  // private
23030     mozKeyPress : function(e){
23031         if(e.ctrlKey){
23032             var c = e.getCharCode(), cmd;
23033           
23034             if(c > 0){
23035                 c = String.fromCharCode(c).toLowerCase();
23036                 switch(c){
23037                     case 'b':
23038                         cmd = 'bold';
23039                         break;
23040                     case 'i':
23041                         cmd = 'italic';
23042                         break;
23043                     
23044                     case 'u':
23045                         cmd = 'underline';
23046                         break;
23047                     
23048                     //case 'v':
23049                       //  this.cleanUpPaste.defer(100, this);
23050                       //  return;
23051                         
23052                 }
23053                 if(cmd){
23054                     this.win.focus();
23055                     this.execCmd(cmd);
23056                     this.deferFocus();
23057                     e.preventDefault();
23058                 }
23059                 
23060             }
23061         }
23062     },
23063
23064     // private
23065     fixKeys : function(){ // load time branching for fastest keydown performance
23066         if(Roo.isIE){
23067             return function(e){
23068                 var k = e.getKey(), r;
23069                 if(k == e.TAB){
23070                     e.stopEvent();
23071                     r = this.doc.selection.createRange();
23072                     if(r){
23073                         r.collapse(true);
23074                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23075                         this.deferFocus();
23076                     }
23077                     return;
23078                 }
23079                 
23080                 if(k == e.ENTER){
23081                     r = this.doc.selection.createRange();
23082                     if(r){
23083                         var target = r.parentElement();
23084                         if(!target || target.tagName.toLowerCase() != 'li'){
23085                             e.stopEvent();
23086                             r.pasteHTML('<br/>');
23087                             r.collapse(false);
23088                             r.select();
23089                         }
23090                     }
23091                 }
23092                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23093                 //    this.cleanUpPaste.defer(100, this);
23094                 //    return;
23095                 //}
23096                 
23097                 
23098             };
23099         }else if(Roo.isOpera){
23100             return function(e){
23101                 var k = e.getKey();
23102                 if(k == e.TAB){
23103                     e.stopEvent();
23104                     this.win.focus();
23105                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23106                     this.deferFocus();
23107                 }
23108                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23109                 //    this.cleanUpPaste.defer(100, this);
23110                  //   return;
23111                 //}
23112                 
23113             };
23114         }else if(Roo.isSafari){
23115             return function(e){
23116                 var k = e.getKey();
23117                 
23118                 if(k == e.TAB){
23119                     e.stopEvent();
23120                     this.execCmd('InsertText','\t');
23121                     this.deferFocus();
23122                     return;
23123                 }
23124                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23125                  //   this.cleanUpPaste.defer(100, this);
23126                  //   return;
23127                // }
23128                 
23129              };
23130         }
23131     }(),
23132     
23133     getAllAncestors: function()
23134     {
23135         var p = this.getSelectedNode();
23136         var a = [];
23137         if (!p) {
23138             a.push(p); // push blank onto stack..
23139             p = this.getParentElement();
23140         }
23141         
23142         
23143         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23144             a.push(p);
23145             p = p.parentNode;
23146         }
23147         a.push(this.doc.body);
23148         return a;
23149     },
23150     lastSel : false,
23151     lastSelNode : false,
23152     
23153     
23154     getSelection : function() 
23155     {
23156         this.assignDocWin();
23157         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23158     },
23159     /**
23160      * Select a dom node
23161      * @param {DomElement} node the node to select
23162      */
23163     selectNode : function(node)
23164     {
23165         
23166             var nodeRange = node.ownerDocument.createRange();
23167             try {
23168                 nodeRange.selectNode(node);
23169             } catch (e) {
23170                 nodeRange.selectNodeContents(node);
23171             }
23172             //nodeRange.collapse(true);
23173             var s = this.win.getSelection();
23174             s.removeAllRanges();
23175             s.addRange(nodeRange);
23176     },
23177     
23178     getSelectedNode: function() 
23179     {
23180         // this may only work on Gecko!!!
23181         
23182         // should we cache this!!!!
23183         
23184         
23185         
23186          
23187         var range = this.createRange(this.getSelection()).cloneRange();
23188         
23189         if (Roo.isIE) {
23190             var parent = range.parentElement();
23191             while (true) {
23192                 var testRange = range.duplicate();
23193                 testRange.moveToElementText(parent);
23194                 if (testRange.inRange(range)) {
23195                     break;
23196                 }
23197                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23198                     break;
23199                 }
23200                 parent = parent.parentElement;
23201             }
23202             return parent;
23203         }
23204         
23205         // is ancestor a text element.
23206         var ac =  range.commonAncestorContainer;
23207         if (ac.nodeType == 3) {
23208             ac = ac.parentNode;
23209         }
23210         
23211         var ar = ac.childNodes;
23212          
23213         var nodes = [];
23214         var other_nodes = [];
23215         var has_other_nodes = false;
23216         for (var i=0;i<ar.length;i++) {
23217             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23218                 continue;
23219             }
23220             // fullly contained node.
23221             
23222             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23223                 nodes.push(ar[i]);
23224                 continue;
23225             }
23226             
23227             // probably selected..
23228             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23229                 other_nodes.push(ar[i]);
23230                 continue;
23231             }
23232             // outer..
23233             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23234                 continue;
23235             }
23236             
23237             
23238             has_other_nodes = true;
23239         }
23240         if (!nodes.length && other_nodes.length) {
23241             nodes= other_nodes;
23242         }
23243         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23244             return false;
23245         }
23246         
23247         return nodes[0];
23248     },
23249     createRange: function(sel)
23250     {
23251         // this has strange effects when using with 
23252         // top toolbar - not sure if it's a great idea.
23253         //this.editor.contentWindow.focus();
23254         if (typeof sel != "undefined") {
23255             try {
23256                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23257             } catch(e) {
23258                 return this.doc.createRange();
23259             }
23260         } else {
23261             return this.doc.createRange();
23262         }
23263     },
23264     getParentElement: function()
23265     {
23266         
23267         this.assignDocWin();
23268         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23269         
23270         var range = this.createRange(sel);
23271          
23272         try {
23273             var p = range.commonAncestorContainer;
23274             while (p.nodeType == 3) { // text node
23275                 p = p.parentNode;
23276             }
23277             return p;
23278         } catch (e) {
23279             return null;
23280         }
23281     
23282     },
23283     /***
23284      *
23285      * Range intersection.. the hard stuff...
23286      *  '-1' = before
23287      *  '0' = hits..
23288      *  '1' = after.
23289      *         [ -- selected range --- ]
23290      *   [fail]                        [fail]
23291      *
23292      *    basically..
23293      *      if end is before start or  hits it. fail.
23294      *      if start is after end or hits it fail.
23295      *
23296      *   if either hits (but other is outside. - then it's not 
23297      *   
23298      *    
23299      **/
23300     
23301     
23302     // @see http://www.thismuchiknow.co.uk/?p=64.
23303     rangeIntersectsNode : function(range, node)
23304     {
23305         var nodeRange = node.ownerDocument.createRange();
23306         try {
23307             nodeRange.selectNode(node);
23308         } catch (e) {
23309             nodeRange.selectNodeContents(node);
23310         }
23311     
23312         var rangeStartRange = range.cloneRange();
23313         rangeStartRange.collapse(true);
23314     
23315         var rangeEndRange = range.cloneRange();
23316         rangeEndRange.collapse(false);
23317     
23318         var nodeStartRange = nodeRange.cloneRange();
23319         nodeStartRange.collapse(true);
23320     
23321         var nodeEndRange = nodeRange.cloneRange();
23322         nodeEndRange.collapse(false);
23323     
23324         return rangeStartRange.compareBoundaryPoints(
23325                  Range.START_TO_START, nodeEndRange) == -1 &&
23326                rangeEndRange.compareBoundaryPoints(
23327                  Range.START_TO_START, nodeStartRange) == 1;
23328         
23329          
23330     },
23331     rangeCompareNode : function(range, node)
23332     {
23333         var nodeRange = node.ownerDocument.createRange();
23334         try {
23335             nodeRange.selectNode(node);
23336         } catch (e) {
23337             nodeRange.selectNodeContents(node);
23338         }
23339         
23340         
23341         range.collapse(true);
23342     
23343         nodeRange.collapse(true);
23344      
23345         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23346         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23347          
23348         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23349         
23350         var nodeIsBefore   =  ss == 1;
23351         var nodeIsAfter    = ee == -1;
23352         
23353         if (nodeIsBefore && nodeIsAfter) {
23354             return 0; // outer
23355         }
23356         if (!nodeIsBefore && nodeIsAfter) {
23357             return 1; //right trailed.
23358         }
23359         
23360         if (nodeIsBefore && !nodeIsAfter) {
23361             return 2;  // left trailed.
23362         }
23363         // fully contined.
23364         return 3;
23365     },
23366  
23367     cleanWordChars : function(input) {// change the chars to hex code
23368         
23369        var swapCodes  = [ 
23370             [    8211, "&#8211;" ], 
23371             [    8212, "&#8212;" ], 
23372             [    8216,  "'" ],  
23373             [    8217, "'" ],  
23374             [    8220, '"' ],  
23375             [    8221, '"' ],  
23376             [    8226, "*" ],  
23377             [    8230, "..." ]
23378         ]; 
23379         var output = input;
23380         Roo.each(swapCodes, function(sw) { 
23381             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23382             
23383             output = output.replace(swapper, sw[1]);
23384         });
23385         
23386         return output;
23387     },
23388     
23389      
23390     
23391         
23392     
23393     cleanUpChild : function (node)
23394     {
23395         
23396         new Roo.htmleditor.FilterComment({node : node});
23397         new Roo.htmleditor.FilterAttributes({
23398                 node : node,
23399                 attrib_black : this.ablack,
23400                 attrib_clean : this.aclean,
23401                 style_white : this.cwhite,
23402                 style_black : this.cblack
23403         });
23404         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
23405         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
23406          
23407         
23408     },
23409     
23410     /**
23411      * Clean up MS wordisms...
23412      * @deprecated - use filter directly
23413      */
23414     cleanWord : function(node)
23415     {
23416         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
23417         
23418     },
23419    
23420     
23421     /**
23422
23423      * @deprecated - use filters
23424      */
23425     cleanTableWidths : function(node)
23426     {
23427         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
23428         
23429  
23430     },
23431     
23432      
23433         
23434     applyBlacklists : function()
23435     {
23436         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23437         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23438         
23439         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
23440         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
23441         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
23442         
23443         this.white = [];
23444         this.black = [];
23445         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23446             if (b.indexOf(tag) > -1) {
23447                 return;
23448             }
23449             this.white.push(tag);
23450             
23451         }, this);
23452         
23453         Roo.each(w, function(tag) {
23454             if (b.indexOf(tag) > -1) {
23455                 return;
23456             }
23457             if (this.white.indexOf(tag) > -1) {
23458                 return;
23459             }
23460             this.white.push(tag);
23461             
23462         }, this);
23463         
23464         
23465         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23466             if (w.indexOf(tag) > -1) {
23467                 return;
23468             }
23469             this.black.push(tag);
23470             
23471         }, this);
23472         
23473         Roo.each(b, function(tag) {
23474             if (w.indexOf(tag) > -1) {
23475                 return;
23476             }
23477             if (this.black.indexOf(tag) > -1) {
23478                 return;
23479             }
23480             this.black.push(tag);
23481             
23482         }, this);
23483         
23484         
23485         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23486         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23487         
23488         this.cwhite = [];
23489         this.cblack = [];
23490         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23491             if (b.indexOf(tag) > -1) {
23492                 return;
23493             }
23494             this.cwhite.push(tag);
23495             
23496         }, this);
23497         
23498         Roo.each(w, function(tag) {
23499             if (b.indexOf(tag) > -1) {
23500                 return;
23501             }
23502             if (this.cwhite.indexOf(tag) > -1) {
23503                 return;
23504             }
23505             this.cwhite.push(tag);
23506             
23507         }, this);
23508         
23509         
23510         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23511             if (w.indexOf(tag) > -1) {
23512                 return;
23513             }
23514             this.cblack.push(tag);
23515             
23516         }, this);
23517         
23518         Roo.each(b, function(tag) {
23519             if (w.indexOf(tag) > -1) {
23520                 return;
23521             }
23522             if (this.cblack.indexOf(tag) > -1) {
23523                 return;
23524             }
23525             this.cblack.push(tag);
23526             
23527         }, this);
23528     },
23529     
23530     setStylesheets : function(stylesheets)
23531     {
23532         if(typeof(stylesheets) == 'string'){
23533             Roo.get(this.iframe.contentDocument.head).createChild({
23534                 tag : 'link',
23535                 rel : 'stylesheet',
23536                 type : 'text/css',
23537                 href : stylesheets
23538             });
23539             
23540             return;
23541         }
23542         var _this = this;
23543      
23544         Roo.each(stylesheets, function(s) {
23545             if(!s.length){
23546                 return;
23547             }
23548             
23549             Roo.get(_this.iframe.contentDocument.head).createChild({
23550                 tag : 'link',
23551                 rel : 'stylesheet',
23552                 type : 'text/css',
23553                 href : s
23554             });
23555         });
23556
23557         
23558     },
23559     
23560     removeStylesheets : function()
23561     {
23562         var _this = this;
23563         
23564         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23565             s.remove();
23566         });
23567     },
23568     
23569     setStyle : function(style)
23570     {
23571         Roo.get(this.iframe.contentDocument.head).createChild({
23572             tag : 'style',
23573             type : 'text/css',
23574             html : style
23575         });
23576
23577         return;
23578     }
23579     
23580     // hide stuff that is not compatible
23581     /**
23582      * @event blur
23583      * @hide
23584      */
23585     /**
23586      * @event change
23587      * @hide
23588      */
23589     /**
23590      * @event focus
23591      * @hide
23592      */
23593     /**
23594      * @event specialkey
23595      * @hide
23596      */
23597     /**
23598      * @cfg {String} fieldClass @hide
23599      */
23600     /**
23601      * @cfg {String} focusClass @hide
23602      */
23603     /**
23604      * @cfg {String} autoCreate @hide
23605      */
23606     /**
23607      * @cfg {String} inputType @hide
23608      */
23609     /**
23610      * @cfg {String} invalidClass @hide
23611      */
23612     /**
23613      * @cfg {String} invalidText @hide
23614      */
23615     /**
23616      * @cfg {String} msgFx @hide
23617      */
23618     /**
23619      * @cfg {String} validateOnBlur @hide
23620      */
23621 });
23622
23623 Roo.HtmlEditorCore.white = [
23624         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
23625         
23626        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
23627        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
23628        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
23629        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
23630        'TABLE',   'UL',         'XMP', 
23631        
23632        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
23633       'THEAD',   'TR', 
23634      
23635       'DIR', 'MENU', 'OL', 'UL', 'DL',
23636        
23637       'EMBED',  'OBJECT'
23638 ];
23639
23640
23641 Roo.HtmlEditorCore.black = [
23642     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23643         'APPLET', // 
23644         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
23645         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
23646         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
23647         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
23648         //'FONT' // CLEAN LATER..
23649         'COLGROUP', 'COL'  // messy tables.
23650         
23651 ];
23652 Roo.HtmlEditorCore.clean = [ // ?? needed???
23653      'SCRIPT', 'STYLE', 'TITLE', 'XML'
23654 ];
23655 Roo.HtmlEditorCore.tag_remove = [
23656     'FONT', 'TBODY'  
23657 ];
23658 // attributes..
23659
23660 Roo.HtmlEditorCore.ablack = [
23661     'on'
23662 ];
23663     
23664 Roo.HtmlEditorCore.aclean = [ 
23665     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23666 ];
23667
23668 // protocols..
23669 Roo.HtmlEditorCore.pwhite= [
23670         'http',  'https',  'mailto'
23671 ];
23672
23673 // white listed style attributes.
23674 Roo.HtmlEditorCore.cwhite= [
23675       //  'text-align', /// default is to allow most things..
23676       
23677          
23678 //        'font-size'//??
23679 ];
23680
23681 // black listed style attributes.
23682 Roo.HtmlEditorCore.cblack= [
23683       //  'font-size' -- this can be set by the project 
23684 ];
23685
23686
23687
23688
23689     //<script type="text/javascript">
23690
23691 /*
23692  * Ext JS Library 1.1.1
23693  * Copyright(c) 2006-2007, Ext JS, LLC.
23694  * Licence LGPL
23695  * 
23696  */
23697  
23698  
23699 Roo.form.HtmlEditor = function(config){
23700     
23701     
23702     
23703     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
23704     
23705     if (!this.toolbars) {
23706         this.toolbars = [];
23707     }
23708     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23709     
23710     
23711 };
23712
23713 /**
23714  * @class Roo.form.HtmlEditor
23715  * @extends Roo.form.Field
23716  * Provides a lightweight HTML Editor component.
23717  *
23718  * This has been tested on Fireforx / Chrome.. IE may not be so great..
23719  * 
23720  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23721  * supported by this editor.</b><br/><br/>
23722  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23723  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23724  */
23725 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
23726     /**
23727      * @cfg {Boolean} clearUp
23728      */
23729     clearUp : true,
23730       /**
23731      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23732      */
23733     toolbars : false,
23734    
23735      /**
23736      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23737      *                        Roo.resizable.
23738      */
23739     resizable : false,
23740      /**
23741      * @cfg {Number} height (in pixels)
23742      */   
23743     height: 300,
23744    /**
23745      * @cfg {Number} width (in pixels)
23746      */   
23747     width: 500,
23748     
23749     /**
23750      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
23751      * 
23752      */
23753     stylesheets: false,
23754     
23755     
23756      /**
23757      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
23758      * 
23759      */
23760     cblack: false,
23761     /**
23762      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
23763      * 
23764      */
23765     cwhite: false,
23766     
23767      /**
23768      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
23769      * 
23770      */
23771     black: false,
23772     /**
23773      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
23774      * 
23775      */
23776     white: false,
23777     /**
23778      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
23779      */
23780     allowComments: false,
23781     /**
23782      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
23783      */
23784     
23785     
23786      bodyCls : '',
23787     
23788     // id of frame..
23789     frameId: false,
23790     
23791     // private properties
23792     validationEvent : false,
23793     deferHeight: true,
23794     initialized : false,
23795     activated : false,
23796     
23797     onFocus : Roo.emptyFn,
23798     iframePad:3,
23799     hideMode:'offsets',
23800     
23801     actionMode : 'container', // defaults to hiding it...
23802     
23803     defaultAutoCreate : { // modified by initCompnoent..
23804         tag: "textarea",
23805         style:"width:500px;height:300px;",
23806         autocomplete: "new-password"
23807     },
23808
23809     // private
23810     initComponent : function(){
23811         this.addEvents({
23812             /**
23813              * @event initialize
23814              * Fires when the editor is fully initialized (including the iframe)
23815              * @param {HtmlEditor} this
23816              */
23817             initialize: true,
23818             /**
23819              * @event activate
23820              * Fires when the editor is first receives the focus. Any insertion must wait
23821              * until after this event.
23822              * @param {HtmlEditor} this
23823              */
23824             activate: true,
23825              /**
23826              * @event beforesync
23827              * Fires before the textarea is updated with content from the editor iframe. Return false
23828              * to cancel the sync.
23829              * @param {HtmlEditor} this
23830              * @param {String} html
23831              */
23832             beforesync: true,
23833              /**
23834              * @event beforepush
23835              * Fires before the iframe editor is updated with content from the textarea. Return false
23836              * to cancel the push.
23837              * @param {HtmlEditor} this
23838              * @param {String} html
23839              */
23840             beforepush: true,
23841              /**
23842              * @event sync
23843              * Fires when the textarea is updated with content from the editor iframe.
23844              * @param {HtmlEditor} this
23845              * @param {String} html
23846              */
23847             sync: true,
23848              /**
23849              * @event push
23850              * Fires when the iframe editor is updated with content from the textarea.
23851              * @param {HtmlEditor} this
23852              * @param {String} html
23853              */
23854             push: true,
23855              /**
23856              * @event editmodechange
23857              * Fires when the editor switches edit modes
23858              * @param {HtmlEditor} this
23859              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23860              */
23861             editmodechange: true,
23862             /**
23863              * @event editorevent
23864              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23865              * @param {HtmlEditor} this
23866              */
23867             editorevent: true,
23868             /**
23869              * @event firstfocus
23870              * Fires when on first focus - needed by toolbars..
23871              * @param {HtmlEditor} this
23872              */
23873             firstfocus: true,
23874             /**
23875              * @event autosave
23876              * Auto save the htmlEditor value as a file into Events
23877              * @param {HtmlEditor} this
23878              */
23879             autosave: true,
23880             /**
23881              * @event savedpreview
23882              * preview the saved version of htmlEditor
23883              * @param {HtmlEditor} this
23884              */
23885             savedpreview: true,
23886             
23887             /**
23888             * @event stylesheetsclick
23889             * Fires when press the Sytlesheets button
23890             * @param {Roo.HtmlEditorCore} this
23891             */
23892             stylesheetsclick: true,
23893             /**
23894             * @event paste
23895             * Fires when press user pastes into the editor
23896             * @param {Roo.HtmlEditorCore} this
23897             */
23898             paste: true 
23899         });
23900         this.defaultAutoCreate =  {
23901             tag: "textarea",
23902             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
23903             autocomplete: "new-password"
23904         };
23905     },
23906
23907     /**
23908      * Protected method that will not generally be called directly. It
23909      * is called when the editor creates its toolbar. Override this method if you need to
23910      * add custom toolbar buttons.
23911      * @param {HtmlEditor} editor
23912      */
23913     createToolbar : function(editor){
23914         Roo.log("create toolbars");
23915         if (!editor.toolbars || !editor.toolbars.length) {
23916             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23917         }
23918         
23919         for (var i =0 ; i < editor.toolbars.length;i++) {
23920             editor.toolbars[i] = Roo.factory(
23921                     typeof(editor.toolbars[i]) == 'string' ?
23922                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
23923                 Roo.form.HtmlEditor);
23924             editor.toolbars[i].init(editor);
23925         }
23926          
23927         
23928     },
23929
23930      
23931     // private
23932     onRender : function(ct, position)
23933     {
23934         var _t = this;
23935         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23936         
23937         this.wrap = this.el.wrap({
23938             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23939         });
23940         
23941         this.editorcore.onRender(ct, position);
23942          
23943         if (this.resizable) {
23944             this.resizeEl = new Roo.Resizable(this.wrap, {
23945                 pinned : true,
23946                 wrap: true,
23947                 dynamic : true,
23948                 minHeight : this.height,
23949                 height: this.height,
23950                 handles : this.resizable,
23951                 width: this.width,
23952                 listeners : {
23953                     resize : function(r, w, h) {
23954                         _t.onResize(w,h); // -something
23955                     }
23956                 }
23957             });
23958             
23959         }
23960         this.createToolbar(this);
23961        
23962         
23963         if(!this.width){
23964             this.setSize(this.wrap.getSize());
23965         }
23966         if (this.resizeEl) {
23967             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23968             // should trigger onReize..
23969         }
23970         
23971         this.keyNav = new Roo.KeyNav(this.el, {
23972             
23973             "tab" : function(e){
23974                 e.preventDefault();
23975                 
23976                 var value = this.getValue();
23977                 
23978                 var start = this.el.dom.selectionStart;
23979                 var end = this.el.dom.selectionEnd;
23980                 
23981                 if(!e.shiftKey){
23982                     
23983                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
23984                     this.el.dom.setSelectionRange(end + 1, end + 1);
23985                     return;
23986                 }
23987                 
23988                 var f = value.substring(0, start).split("\t");
23989                 
23990                 if(f.pop().length != 0){
23991                     return;
23992                 }
23993                 
23994                 this.setValue(f.join("\t") + value.substring(end));
23995                 this.el.dom.setSelectionRange(start - 1, start - 1);
23996                 
23997             },
23998             
23999             "home" : function(e){
24000                 e.preventDefault();
24001                 
24002                 var curr = this.el.dom.selectionStart;
24003                 var lines = this.getValue().split("\n");
24004                 
24005                 if(!lines.length){
24006                     return;
24007                 }
24008                 
24009                 if(e.ctrlKey){
24010                     this.el.dom.setSelectionRange(0, 0);
24011                     return;
24012                 }
24013                 
24014                 var pos = 0;
24015                 
24016                 for (var i = 0; i < lines.length;i++) {
24017                     pos += lines[i].length;
24018                     
24019                     if(i != 0){
24020                         pos += 1;
24021                     }
24022                     
24023                     if(pos < curr){
24024                         continue;
24025                     }
24026                     
24027                     pos -= lines[i].length;
24028                     
24029                     break;
24030                 }
24031                 
24032                 if(!e.shiftKey){
24033                     this.el.dom.setSelectionRange(pos, pos);
24034                     return;
24035                 }
24036                 
24037                 this.el.dom.selectionStart = pos;
24038                 this.el.dom.selectionEnd = curr;
24039             },
24040             
24041             "end" : function(e){
24042                 e.preventDefault();
24043                 
24044                 var curr = this.el.dom.selectionStart;
24045                 var lines = this.getValue().split("\n");
24046                 
24047                 if(!lines.length){
24048                     return;
24049                 }
24050                 
24051                 if(e.ctrlKey){
24052                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
24053                     return;
24054                 }
24055                 
24056                 var pos = 0;
24057                 
24058                 for (var i = 0; i < lines.length;i++) {
24059                     
24060                     pos += lines[i].length;
24061                     
24062                     if(i != 0){
24063                         pos += 1;
24064                     }
24065                     
24066                     if(pos < curr){
24067                         continue;
24068                     }
24069                     
24070                     break;
24071                 }
24072                 
24073                 if(!e.shiftKey){
24074                     this.el.dom.setSelectionRange(pos, pos);
24075                     return;
24076                 }
24077                 
24078                 this.el.dom.selectionStart = curr;
24079                 this.el.dom.selectionEnd = pos;
24080             },
24081
24082             scope : this,
24083
24084             doRelay : function(foo, bar, hname){
24085                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
24086             },
24087
24088             forceKeyDown: true
24089         });
24090         
24091 //        if(this.autosave && this.w){
24092 //            this.autoSaveFn = setInterval(this.autosave, 1000);
24093 //        }
24094     },
24095
24096     // private
24097     onResize : function(w, h)
24098     {
24099         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24100         var ew = false;
24101         var eh = false;
24102         
24103         if(this.el ){
24104             if(typeof w == 'number'){
24105                 var aw = w - this.wrap.getFrameWidth('lr');
24106                 this.el.setWidth(this.adjustWidth('textarea', aw));
24107                 ew = aw;
24108             }
24109             if(typeof h == 'number'){
24110                 var tbh = 0;
24111                 for (var i =0; i < this.toolbars.length;i++) {
24112                     // fixme - ask toolbars for heights?
24113                     tbh += this.toolbars[i].tb.el.getHeight();
24114                     if (this.toolbars[i].footer) {
24115                         tbh += this.toolbars[i].footer.el.getHeight();
24116                     }
24117                 }
24118                 
24119                 
24120                 
24121                 
24122                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24123                 ah -= 5; // knock a few pixes off for look..
24124 //                Roo.log(ah);
24125                 this.el.setHeight(this.adjustWidth('textarea', ah));
24126                 var eh = ah;
24127             }
24128         }
24129         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24130         this.editorcore.onResize(ew,eh);
24131         
24132     },
24133
24134     /**
24135      * Toggles the editor between standard and source edit mode.
24136      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24137      */
24138     toggleSourceEdit : function(sourceEditMode)
24139     {
24140         this.editorcore.toggleSourceEdit(sourceEditMode);
24141         
24142         if(this.editorcore.sourceEditMode){
24143             Roo.log('editor - showing textarea');
24144             
24145 //            Roo.log('in');
24146 //            Roo.log(this.syncValue());
24147             this.editorcore.syncValue();
24148             this.el.removeClass('x-hidden');
24149             this.el.dom.removeAttribute('tabIndex');
24150             this.el.focus();
24151             this.el.dom.scrollTop = 0;
24152             
24153             
24154             for (var i = 0; i < this.toolbars.length; i++) {
24155                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24156                     this.toolbars[i].tb.hide();
24157                     this.toolbars[i].footer.hide();
24158                 }
24159             }
24160             
24161         }else{
24162             Roo.log('editor - hiding textarea');
24163 //            Roo.log('out')
24164 //            Roo.log(this.pushValue()); 
24165             this.editorcore.pushValue();
24166             
24167             this.el.addClass('x-hidden');
24168             this.el.dom.setAttribute('tabIndex', -1);
24169             
24170             for (var i = 0; i < this.toolbars.length; i++) {
24171                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24172                     this.toolbars[i].tb.show();
24173                     this.toolbars[i].footer.show();
24174                 }
24175             }
24176             
24177             //this.deferFocus();
24178         }
24179         
24180         this.setSize(this.wrap.getSize());
24181         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
24182         
24183         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24184     },
24185  
24186     // private (for BoxComponent)
24187     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24188
24189     // private (for BoxComponent)
24190     getResizeEl : function(){
24191         return this.wrap;
24192     },
24193
24194     // private (for BoxComponent)
24195     getPositionEl : function(){
24196         return this.wrap;
24197     },
24198
24199     // private
24200     initEvents : function(){
24201         this.originalValue = this.getValue();
24202     },
24203
24204     /**
24205      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24206      * @method
24207      */
24208     markInvalid : Roo.emptyFn,
24209     /**
24210      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24211      * @method
24212      */
24213     clearInvalid : Roo.emptyFn,
24214
24215     setValue : function(v){
24216         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24217         this.editorcore.pushValue();
24218     },
24219
24220      
24221     // private
24222     deferFocus : function(){
24223         this.focus.defer(10, this);
24224     },
24225
24226     // doc'ed in Field
24227     focus : function(){
24228         this.editorcore.focus();
24229         
24230     },
24231       
24232
24233     // private
24234     onDestroy : function(){
24235         
24236         
24237         
24238         if(this.rendered){
24239             
24240             for (var i =0; i < this.toolbars.length;i++) {
24241                 // fixme - ask toolbars for heights?
24242                 this.toolbars[i].onDestroy();
24243             }
24244             
24245             this.wrap.dom.innerHTML = '';
24246             this.wrap.remove();
24247         }
24248     },
24249
24250     // private
24251     onFirstFocus : function(){
24252         //Roo.log("onFirstFocus");
24253         this.editorcore.onFirstFocus();
24254          for (var i =0; i < this.toolbars.length;i++) {
24255             this.toolbars[i].onFirstFocus();
24256         }
24257         
24258     },
24259     
24260     // private
24261     syncValue : function()
24262     {
24263         this.editorcore.syncValue();
24264     },
24265     
24266     pushValue : function()
24267     {
24268         this.editorcore.pushValue();
24269     },
24270     
24271     setStylesheets : function(stylesheets)
24272     {
24273         this.editorcore.setStylesheets(stylesheets);
24274     },
24275     
24276     removeStylesheets : function()
24277     {
24278         this.editorcore.removeStylesheets();
24279     }
24280      
24281     
24282     // hide stuff that is not compatible
24283     /**
24284      * @event blur
24285      * @hide
24286      */
24287     /**
24288      * @event change
24289      * @hide
24290      */
24291     /**
24292      * @event focus
24293      * @hide
24294      */
24295     /**
24296      * @event specialkey
24297      * @hide
24298      */
24299     /**
24300      * @cfg {String} fieldClass @hide
24301      */
24302     /**
24303      * @cfg {String} focusClass @hide
24304      */
24305     /**
24306      * @cfg {String} autoCreate @hide
24307      */
24308     /**
24309      * @cfg {String} inputType @hide
24310      */
24311     /**
24312      * @cfg {String} invalidClass @hide
24313      */
24314     /**
24315      * @cfg {String} invalidText @hide
24316      */
24317     /**
24318      * @cfg {String} msgFx @hide
24319      */
24320     /**
24321      * @cfg {String} validateOnBlur @hide
24322      */
24323 });
24324  
24325     // <script type="text/javascript">
24326 /*
24327  * Based on
24328  * Ext JS Library 1.1.1
24329  * Copyright(c) 2006-2007, Ext JS, LLC.
24330  *  
24331  
24332  */
24333
24334 /**
24335  * @class Roo.form.HtmlEditorToolbar1
24336  * Basic Toolbar
24337  * 
24338  * Usage:
24339  *
24340  new Roo.form.HtmlEditor({
24341     ....
24342     toolbars : [
24343         new Roo.form.HtmlEditorToolbar1({
24344             disable : { fonts: 1 , format: 1, ..., ... , ...],
24345             btns : [ .... ]
24346         })
24347     }
24348      
24349  * 
24350  * @cfg {Object} disable List of elements to disable..
24351  * @cfg {Array} btns List of additional buttons.
24352  * 
24353  * 
24354  * NEEDS Extra CSS? 
24355  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24356  */
24357  
24358 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24359 {
24360     
24361     Roo.apply(this, config);
24362     
24363     // default disabled, based on 'good practice'..
24364     this.disable = this.disable || {};
24365     Roo.applyIf(this.disable, {
24366         fontSize : true,
24367         colors : true,
24368         specialElements : true
24369     });
24370     
24371     
24372     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24373     // dont call parent... till later.
24374 }
24375
24376 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24377     
24378     tb: false,
24379     
24380     rendered: false,
24381     
24382     editor : false,
24383     editorcore : false,
24384     /**
24385      * @cfg {Object} disable  List of toolbar elements to disable
24386          
24387      */
24388     disable : false,
24389     
24390     
24391      /**
24392      * @cfg {String} createLinkText The default text for the create link prompt
24393      */
24394     createLinkText : 'Please enter the URL for the link:',
24395     /**
24396      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24397      */
24398     defaultLinkValue : 'http:/'+'/',
24399    
24400     
24401       /**
24402      * @cfg {Array} fontFamilies An array of available font families
24403      */
24404     fontFamilies : [
24405         'Arial',
24406         'Courier New',
24407         'Tahoma',
24408         'Times New Roman',
24409         'Verdana'
24410     ],
24411     
24412     specialChars : [
24413            "&#169;",
24414           "&#174;",     
24415           "&#8482;",    
24416           "&#163;" ,    
24417          // "&#8212;",    
24418           "&#8230;",    
24419           "&#247;" ,    
24420         //  "&#225;" ,     ?? a acute?
24421            "&#8364;"    , //Euro
24422        //   "&#8220;"    ,
24423         //  "&#8221;"    ,
24424         //  "&#8226;"    ,
24425           "&#176;"  //   , // degrees
24426
24427          // "&#233;"     , // e ecute
24428          // "&#250;"     , // u ecute?
24429     ],
24430     
24431     specialElements : [
24432         {
24433             text: "Insert Table",
24434             xtype: 'MenuItem',
24435             xns : Roo.Menu,
24436             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
24437                 
24438         },
24439         {    
24440             text: "Insert Image",
24441             xtype: 'MenuItem',
24442             xns : Roo.Menu,
24443             ihtml : '<img src="about:blank"/>'
24444             
24445         }
24446         
24447          
24448     ],
24449     
24450     
24451     inputElements : [ 
24452             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24453             "input:submit", "input:button", "select", "textarea", "label" ],
24454     formats : [
24455         ["p"] ,  
24456         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24457         ["pre"],[ "code"], 
24458         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
24459         ['div'],['span'],
24460         ['sup'],['sub']
24461     ],
24462     
24463     cleanStyles : [
24464         "font-size"
24465     ],
24466      /**
24467      * @cfg {String} defaultFont default font to use.
24468      */
24469     defaultFont: 'tahoma',
24470    
24471     fontSelect : false,
24472     
24473     
24474     formatCombo : false,
24475     
24476     init : function(editor)
24477     {
24478         this.editor = editor;
24479         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24480         var editorcore = this.editorcore;
24481         
24482         var _t = this;
24483         
24484         var fid = editorcore.frameId;
24485         var etb = this;
24486         function btn(id, toggle, handler){
24487             var xid = fid + '-'+ id ;
24488             return {
24489                 id : xid,
24490                 cmd : id,
24491                 cls : 'x-btn-icon x-edit-'+id,
24492                 enableToggle:toggle !== false,
24493                 scope: _t, // was editor...
24494                 handler:handler||_t.relayBtnCmd,
24495                 clickEvent:'mousedown',
24496                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24497                 tabIndex:-1
24498             };
24499         }
24500         
24501         
24502         
24503         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24504         this.tb = tb;
24505          // stop form submits
24506         tb.el.on('click', function(e){
24507             e.preventDefault(); // what does this do?
24508         });
24509
24510         if(!this.disable.font) { // && !Roo.isSafari){
24511             /* why no safari for fonts 
24512             editor.fontSelect = tb.el.createChild({
24513                 tag:'select',
24514                 tabIndex: -1,
24515                 cls:'x-font-select',
24516                 html: this.createFontOptions()
24517             });
24518             
24519             editor.fontSelect.on('change', function(){
24520                 var font = editor.fontSelect.dom.value;
24521                 editor.relayCmd('fontname', font);
24522                 editor.deferFocus();
24523             }, editor);
24524             
24525             tb.add(
24526                 editor.fontSelect.dom,
24527                 '-'
24528             );
24529             */
24530             
24531         };
24532         if(!this.disable.formats){
24533             this.formatCombo = new Roo.form.ComboBox({
24534                 store: new Roo.data.SimpleStore({
24535                     id : 'tag',
24536                     fields: ['tag'],
24537                     data : this.formats // from states.js
24538                 }),
24539                 blockFocus : true,
24540                 name : '',
24541                 //autoCreate : {tag: "div",  size: "20"},
24542                 displayField:'tag',
24543                 typeAhead: false,
24544                 mode: 'local',
24545                 editable : false,
24546                 triggerAction: 'all',
24547                 emptyText:'Add tag',
24548                 selectOnFocus:true,
24549                 width:135,
24550                 listeners : {
24551                     'select': function(c, r, i) {
24552                         editorcore.insertTag(r.get('tag'));
24553                         editor.focus();
24554                     }
24555                 }
24556
24557             });
24558             tb.addField(this.formatCombo);
24559             
24560         }
24561         
24562         if(!this.disable.format){
24563             tb.add(
24564                 btn('bold'),
24565                 btn('italic'),
24566                 btn('underline'),
24567                 btn('strikethrough')
24568             );
24569         };
24570         if(!this.disable.fontSize){
24571             tb.add(
24572                 '-',
24573                 
24574                 
24575                 btn('increasefontsize', false, editorcore.adjustFont),
24576                 btn('decreasefontsize', false, editorcore.adjustFont)
24577             );
24578         };
24579         
24580         
24581         if(!this.disable.colors){
24582             tb.add(
24583                 '-', {
24584                     id:editorcore.frameId +'-forecolor',
24585                     cls:'x-btn-icon x-edit-forecolor',
24586                     clickEvent:'mousedown',
24587                     tooltip: this.buttonTips['forecolor'] || undefined,
24588                     tabIndex:-1,
24589                     menu : new Roo.menu.ColorMenu({
24590                         allowReselect: true,
24591                         focus: Roo.emptyFn,
24592                         value:'000000',
24593                         plain:true,
24594                         selectHandler: function(cp, color){
24595                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24596                             editor.deferFocus();
24597                         },
24598                         scope: editorcore,
24599                         clickEvent:'mousedown'
24600                     })
24601                 }, {
24602                     id:editorcore.frameId +'backcolor',
24603                     cls:'x-btn-icon x-edit-backcolor',
24604                     clickEvent:'mousedown',
24605                     tooltip: this.buttonTips['backcolor'] || undefined,
24606                     tabIndex:-1,
24607                     menu : new Roo.menu.ColorMenu({
24608                         focus: Roo.emptyFn,
24609                         value:'FFFFFF',
24610                         plain:true,
24611                         allowReselect: true,
24612                         selectHandler: function(cp, color){
24613                             if(Roo.isGecko){
24614                                 editorcore.execCmd('useCSS', false);
24615                                 editorcore.execCmd('hilitecolor', color);
24616                                 editorcore.execCmd('useCSS', true);
24617                                 editor.deferFocus();
24618                             }else{
24619                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24620                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24621                                 editor.deferFocus();
24622                             }
24623                         },
24624                         scope:editorcore,
24625                         clickEvent:'mousedown'
24626                     })
24627                 }
24628             );
24629         };
24630         // now add all the items...
24631         
24632
24633         if(!this.disable.alignments){
24634             tb.add(
24635                 '-',
24636                 btn('justifyleft'),
24637                 btn('justifycenter'),
24638                 btn('justifyright')
24639             );
24640         };
24641
24642         //if(!Roo.isSafari){
24643             if(!this.disable.links){
24644                 tb.add(
24645                     '-',
24646                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
24647                 );
24648             };
24649
24650             if(!this.disable.lists){
24651                 tb.add(
24652                     '-',
24653                     btn('insertorderedlist'),
24654                     btn('insertunorderedlist')
24655                 );
24656             }
24657             if(!this.disable.sourceEdit){
24658                 tb.add(
24659                     '-',
24660                     btn('sourceedit', true, function(btn){
24661                         this.toggleSourceEdit(btn.pressed);
24662                     })
24663                 );
24664             }
24665         //}
24666         
24667         var smenu = { };
24668         // special menu.. - needs to be tidied up..
24669         if (!this.disable.special) {
24670             smenu = {
24671                 text: "&#169;",
24672                 cls: 'x-edit-none',
24673                 
24674                 menu : {
24675                     items : []
24676                 }
24677             };
24678             for (var i =0; i < this.specialChars.length; i++) {
24679                 smenu.menu.items.push({
24680                     
24681                     html: this.specialChars[i],
24682                     handler: function(a,b) {
24683                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
24684                         //editor.insertAtCursor(a.html);
24685                         
24686                     },
24687                     tabIndex:-1
24688                 });
24689             }
24690             
24691             
24692             tb.add(smenu);
24693             
24694             
24695         }
24696         
24697         var cmenu = { };
24698         if (!this.disable.cleanStyles) {
24699             cmenu = {
24700                 cls: 'x-btn-icon x-btn-clear',
24701                 
24702                 menu : {
24703                     items : []
24704                 }
24705             };
24706             for (var i =0; i < this.cleanStyles.length; i++) {
24707                 cmenu.menu.items.push({
24708                     actiontype : this.cleanStyles[i],
24709                     html: 'Remove ' + this.cleanStyles[i],
24710                     handler: function(a,b) {
24711 //                        Roo.log(a);
24712 //                        Roo.log(b);
24713                         var c = Roo.get(editorcore.doc.body);
24714                         c.select('[style]').each(function(s) {
24715                             s.dom.style.removeProperty(a.actiontype);
24716                         });
24717                         editorcore.syncValue();
24718                     },
24719                     tabIndex:-1
24720                 });
24721             }
24722             cmenu.menu.items.push({
24723                 actiontype : 'tablewidths',
24724                 html: 'Remove Table Widths',
24725                 handler: function(a,b) {
24726                     editorcore.cleanTableWidths();
24727                     editorcore.syncValue();
24728                 },
24729                 tabIndex:-1
24730             });
24731             cmenu.menu.items.push({
24732                 actiontype : 'word',
24733                 html: 'Remove MS Word Formating',
24734                 handler: function(a,b) {
24735                     editorcore.cleanWord();
24736                     editorcore.syncValue();
24737                 },
24738                 tabIndex:-1
24739             });
24740             
24741             cmenu.menu.items.push({
24742                 actiontype : 'all',
24743                 html: 'Remove All Styles',
24744                 handler: function(a,b) {
24745                     
24746                     var c = Roo.get(editorcore.doc.body);
24747                     c.select('[style]').each(function(s) {
24748                         s.dom.removeAttribute('style');
24749                     });
24750                     editorcore.syncValue();
24751                 },
24752                 tabIndex:-1
24753             });
24754             
24755             cmenu.menu.items.push({
24756                 actiontype : 'all',
24757                 html: 'Remove All CSS Classes',
24758                 handler: function(a,b) {
24759                     
24760                     var c = Roo.get(editorcore.doc.body);
24761                     c.select('[class]').each(function(s) {
24762                         s.dom.removeAttribute('class');
24763                     });
24764                     editorcore.cleanWord();
24765                     editorcore.syncValue();
24766                 },
24767                 tabIndex:-1
24768             });
24769             
24770              cmenu.menu.items.push({
24771                 actiontype : 'tidy',
24772                 html: 'Tidy HTML Source',
24773                 handler: function(a,b) {
24774                     new Roo.htmleditor.Tidy(editorcore.doc.body);
24775                     editorcore.syncValue();
24776                 },
24777                 tabIndex:-1
24778             });
24779             
24780             
24781             tb.add(cmenu);
24782         }
24783          
24784         if (!this.disable.specialElements) {
24785             var semenu = {
24786                 text: "Other;",
24787                 cls: 'x-edit-none',
24788                 menu : {
24789                     items : []
24790                 }
24791             };
24792             for (var i =0; i < this.specialElements.length; i++) {
24793                 semenu.menu.items.push(
24794                     Roo.apply({ 
24795                         handler: function(a,b) {
24796                             editor.insertAtCursor(this.ihtml);
24797                         }
24798                     }, this.specialElements[i])
24799                 );
24800                     
24801             }
24802             
24803             tb.add(semenu);
24804             
24805             
24806         }
24807          
24808         
24809         if (this.btns) {
24810             for(var i =0; i< this.btns.length;i++) {
24811                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
24812                 b.cls =  'x-edit-none';
24813                 
24814                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
24815                     b.cls += ' x-init-enable';
24816                 }
24817                 
24818                 b.scope = editorcore;
24819                 tb.add(b);
24820             }
24821         
24822         }
24823         
24824         
24825         
24826         // disable everything...
24827         
24828         this.tb.items.each(function(item){
24829             
24830            if(
24831                 item.id != editorcore.frameId+ '-sourceedit' && 
24832                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
24833             ){
24834                 
24835                 item.disable();
24836             }
24837         });
24838         this.rendered = true;
24839         
24840         // the all the btns;
24841         editor.on('editorevent', this.updateToolbar, this);
24842         // other toolbars need to implement this..
24843         //editor.on('editmodechange', this.updateToolbar, this);
24844     },
24845     
24846     
24847     relayBtnCmd : function(btn) {
24848         this.editorcore.relayCmd(btn.cmd);
24849     },
24850     // private used internally
24851     createLink : function(){
24852         Roo.log("create link?");
24853         var url = prompt(this.createLinkText, this.defaultLinkValue);
24854         if(url && url != 'http:/'+'/'){
24855             this.editorcore.relayCmd('createlink', url);
24856         }
24857     },
24858
24859     
24860     /**
24861      * Protected method that will not generally be called directly. It triggers
24862      * a toolbar update by reading the markup state of the current selection in the editor.
24863      */
24864     updateToolbar: function(){
24865
24866         if(!this.editorcore.activated){
24867             this.editor.onFirstFocus();
24868             return;
24869         }
24870
24871         var btns = this.tb.items.map, 
24872             doc = this.editorcore.doc,
24873             frameId = this.editorcore.frameId;
24874
24875         if(!this.disable.font && !Roo.isSafari){
24876             /*
24877             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24878             if(name != this.fontSelect.dom.value){
24879                 this.fontSelect.dom.value = name;
24880             }
24881             */
24882         }
24883         if(!this.disable.format){
24884             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24885             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24886             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24887             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
24888         }
24889         if(!this.disable.alignments){
24890             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24891             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24892             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24893         }
24894         if(!Roo.isSafari && !this.disable.lists){
24895             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24896             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24897         }
24898         
24899         var ans = this.editorcore.getAllAncestors();
24900         if (this.formatCombo) {
24901             
24902             
24903             var store = this.formatCombo.store;
24904             this.formatCombo.setValue("");
24905             for (var i =0; i < ans.length;i++) {
24906                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24907                     // select it..
24908                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24909                     break;
24910                 }
24911             }
24912         }
24913         
24914         
24915         
24916         // hides menus... - so this cant be on a menu...
24917         Roo.menu.MenuMgr.hideAll();
24918
24919         //this.editorsyncValue();
24920     },
24921    
24922     
24923     createFontOptions : function(){
24924         var buf = [], fs = this.fontFamilies, ff, lc;
24925         
24926         
24927         
24928         for(var i = 0, len = fs.length; i< len; i++){
24929             ff = fs[i];
24930             lc = ff.toLowerCase();
24931             buf.push(
24932                 '<option value="',lc,'" style="font-family:',ff,';"',
24933                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24934                     ff,
24935                 '</option>'
24936             );
24937         }
24938         return buf.join('');
24939     },
24940     
24941     toggleSourceEdit : function(sourceEditMode){
24942         
24943         Roo.log("toolbar toogle");
24944         if(sourceEditMode === undefined){
24945             sourceEditMode = !this.sourceEditMode;
24946         }
24947         this.sourceEditMode = sourceEditMode === true;
24948         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
24949         // just toggle the button?
24950         if(btn.pressed !== this.sourceEditMode){
24951             btn.toggle(this.sourceEditMode);
24952             return;
24953         }
24954         
24955         if(sourceEditMode){
24956             Roo.log("disabling buttons");
24957             this.tb.items.each(function(item){
24958                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
24959                     item.disable();
24960                 }
24961             });
24962           
24963         }else{
24964             Roo.log("enabling buttons");
24965             if(this.editorcore.initialized){
24966                 this.tb.items.each(function(item){
24967                     item.enable();
24968                 });
24969             }
24970             
24971         }
24972         Roo.log("calling toggole on editor");
24973         // tell the editor that it's been pressed..
24974         this.editor.toggleSourceEdit(sourceEditMode);
24975        
24976     },
24977      /**
24978      * Object collection of toolbar tooltips for the buttons in the editor. The key
24979      * is the command id associated with that button and the value is a valid QuickTips object.
24980      * For example:
24981 <pre><code>
24982 {
24983     bold : {
24984         title: 'Bold (Ctrl+B)',
24985         text: 'Make the selected text bold.',
24986         cls: 'x-html-editor-tip'
24987     },
24988     italic : {
24989         title: 'Italic (Ctrl+I)',
24990         text: 'Make the selected text italic.',
24991         cls: 'x-html-editor-tip'
24992     },
24993     ...
24994 </code></pre>
24995     * @type Object
24996      */
24997     buttonTips : {
24998         bold : {
24999             title: 'Bold (Ctrl+B)',
25000             text: 'Make the selected text bold.',
25001             cls: 'x-html-editor-tip'
25002         },
25003         italic : {
25004             title: 'Italic (Ctrl+I)',
25005             text: 'Make the selected text italic.',
25006             cls: 'x-html-editor-tip'
25007         },
25008         underline : {
25009             title: 'Underline (Ctrl+U)',
25010             text: 'Underline the selected text.',
25011             cls: 'x-html-editor-tip'
25012         },
25013         strikethrough : {
25014             title: 'Strikethrough',
25015             text: 'Strikethrough the selected text.',
25016             cls: 'x-html-editor-tip'
25017         },
25018         increasefontsize : {
25019             title: 'Grow Text',
25020             text: 'Increase the font size.',
25021             cls: 'x-html-editor-tip'
25022         },
25023         decreasefontsize : {
25024             title: 'Shrink Text',
25025             text: 'Decrease the font size.',
25026             cls: 'x-html-editor-tip'
25027         },
25028         backcolor : {
25029             title: 'Text Highlight Color',
25030             text: 'Change the background color of the selected text.',
25031             cls: 'x-html-editor-tip'
25032         },
25033         forecolor : {
25034             title: 'Font Color',
25035             text: 'Change the color of the selected text.',
25036             cls: 'x-html-editor-tip'
25037         },
25038         justifyleft : {
25039             title: 'Align Text Left',
25040             text: 'Align text to the left.',
25041             cls: 'x-html-editor-tip'
25042         },
25043         justifycenter : {
25044             title: 'Center Text',
25045             text: 'Center text in the editor.',
25046             cls: 'x-html-editor-tip'
25047         },
25048         justifyright : {
25049             title: 'Align Text Right',
25050             text: 'Align text to the right.',
25051             cls: 'x-html-editor-tip'
25052         },
25053         insertunorderedlist : {
25054             title: 'Bullet List',
25055             text: 'Start a bulleted list.',
25056             cls: 'x-html-editor-tip'
25057         },
25058         insertorderedlist : {
25059             title: 'Numbered List',
25060             text: 'Start a numbered list.',
25061             cls: 'x-html-editor-tip'
25062         },
25063         createlink : {
25064             title: 'Hyperlink',
25065             text: 'Make the selected text a hyperlink.',
25066             cls: 'x-html-editor-tip'
25067         },
25068         sourceedit : {
25069             title: 'Source Edit',
25070             text: 'Switch to source editing mode.',
25071             cls: 'x-html-editor-tip'
25072         }
25073     },
25074     // private
25075     onDestroy : function(){
25076         if(this.rendered){
25077             
25078             this.tb.items.each(function(item){
25079                 if(item.menu){
25080                     item.menu.removeAll();
25081                     if(item.menu.el){
25082                         item.menu.el.destroy();
25083                     }
25084                 }
25085                 item.destroy();
25086             });
25087              
25088         }
25089     },
25090     onFirstFocus: function() {
25091         this.tb.items.each(function(item){
25092            item.enable();
25093         });
25094     }
25095 });
25096
25097
25098
25099
25100 // <script type="text/javascript">
25101 /*
25102  * Based on
25103  * Ext JS Library 1.1.1
25104  * Copyright(c) 2006-2007, Ext JS, LLC.
25105  *  
25106  
25107  */
25108
25109  
25110 /**
25111  * @class Roo.form.HtmlEditor.ToolbarContext
25112  * Context Toolbar
25113  * 
25114  * Usage:
25115  *
25116  new Roo.form.HtmlEditor({
25117     ....
25118     toolbars : [
25119         { xtype: 'ToolbarStandard', styles : {} }
25120         { xtype: 'ToolbarContext', disable : {} }
25121     ]
25122 })
25123
25124      
25125  * 
25126  * @config : {Object} disable List of elements to disable.. (not done yet.)
25127  * @config : {Object} styles  Map of styles available.
25128  * 
25129  */
25130
25131 Roo.form.HtmlEditor.ToolbarContext = function(config)
25132 {
25133     
25134     Roo.apply(this, config);
25135     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25136     // dont call parent... till later.
25137     this.styles = this.styles || {};
25138 }
25139
25140  
25141
25142 Roo.form.HtmlEditor.ToolbarContext.types = {
25143     'IMG' : [
25144         {
25145             name : 'width',
25146             title: "Width",
25147             width: 40
25148         },
25149         {
25150             name : 'height',
25151             title: "Height",
25152             width: 40
25153         },
25154         {
25155             name : 'align',
25156             title: "Align",
25157             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25158             width : 80
25159             
25160         },
25161         {
25162             name : 'border',
25163             title: "Border",
25164             width: 40
25165         },
25166         {
25167             name : 'alt',
25168             title: "Alt",
25169             width: 120
25170         },
25171         {
25172             name : 'src',
25173             title: "Src",
25174             width: 220
25175         }
25176         
25177     ],
25178     
25179     'FIGURE' : [
25180         {
25181             name : 'align',
25182             title: "Align",
25183             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25184             width : 80  
25185         }
25186     ],
25187     'A' : [
25188         {
25189             name : 'name',
25190             title: "Name",
25191             width: 50
25192         },
25193         {
25194             name : 'target',
25195             title: "Target",
25196             width: 120
25197         },
25198         {
25199             name : 'href',
25200             title: "Href",
25201             width: 220
25202         } // border?
25203         
25204     ],
25205     
25206     'INPUT' : [
25207         {
25208             name : 'name',
25209             title: "name",
25210             width: 120
25211         },
25212         {
25213             name : 'value',
25214             title: "Value",
25215             width: 120
25216         },
25217         {
25218             name : 'width',
25219             title: "Width",
25220             width: 40
25221         }
25222     ],
25223     'LABEL' : [
25224          {
25225             name : 'for',
25226             title: "For",
25227             width: 120
25228         }
25229     ],
25230     'TEXTAREA' : [
25231         {
25232             name : 'name',
25233             title: "name",
25234             width: 120
25235         },
25236         {
25237             name : 'rows',
25238             title: "Rows",
25239             width: 20
25240         },
25241         {
25242             name : 'cols',
25243             title: "Cols",
25244             width: 20
25245         }
25246     ],
25247     'SELECT' : [
25248         {
25249             name : 'name',
25250             title: "name",
25251             width: 120
25252         },
25253         {
25254             name : 'selectoptions',
25255             title: "Options",
25256             width: 200
25257         }
25258     ],
25259     
25260     // should we really allow this??
25261     // should this just be 
25262     'BODY' : [
25263         
25264         {
25265             name : 'title',
25266             title: "Title",
25267             width: 200,
25268             disabled : true
25269         }
25270     ],
25271  
25272     '*' : [
25273         // empty.
25274     ]
25275
25276 };
25277
25278 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
25279 Roo.form.HtmlEditor.ToolbarContext.stores = false;
25280
25281 Roo.form.HtmlEditor.ToolbarContext.options = {
25282         'font-family'  : [ 
25283                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
25284                 [ 'Courier New', 'Courier New'],
25285                 [ 'Tahoma', 'Tahoma'],
25286                 [ 'Times New Roman,serif', 'Times'],
25287                 [ 'Verdana','Verdana' ]
25288         ]
25289 };
25290
25291 // fixme - these need to be configurable..
25292  
25293
25294 //Roo.form.HtmlEditor.ToolbarContext.types
25295
25296
25297 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25298     
25299     tb: false,
25300     
25301     rendered: false,
25302     
25303     editor : false,
25304     editorcore : false,
25305     /**
25306      * @cfg {Object} disable  List of toolbar elements to disable
25307          
25308      */
25309     disable : false,
25310     /**
25311      * @cfg {Object} styles List of styles 
25312      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
25313      *
25314      * These must be defined in the page, so they get rendered correctly..
25315      * .headline { }
25316      * TD.underline { }
25317      * 
25318      */
25319     styles : false,
25320     
25321     options: false,
25322     
25323     toolbars : false,
25324     
25325     init : function(editor)
25326     {
25327         this.editor = editor;
25328         this.editorcore = editor.editorcore ? editor.editorcore : editor;
25329         var editorcore = this.editorcore;
25330         
25331         var fid = editorcore.frameId;
25332         var etb = this;
25333         function btn(id, toggle, handler){
25334             var xid = fid + '-'+ id ;
25335             return {
25336                 id : xid,
25337                 cmd : id,
25338                 cls : 'x-btn-icon x-edit-'+id,
25339                 enableToggle:toggle !== false,
25340                 scope: editorcore, // was editor...
25341                 handler:handler||editorcore.relayBtnCmd,
25342                 clickEvent:'mousedown',
25343                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25344                 tabIndex:-1
25345             };
25346         }
25347         // create a new element.
25348         var wdiv = editor.wrap.createChild({
25349                 tag: 'div'
25350             }, editor.wrap.dom.firstChild.nextSibling, true);
25351         
25352         // can we do this more than once??
25353         
25354          // stop form submits
25355       
25356  
25357         // disable everything...
25358         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25359         this.toolbars = {};
25360            
25361         for (var i in  ty) {
25362           
25363             this.toolbars[i] = this.buildToolbar(ty[i],i);
25364         }
25365         this.tb = this.toolbars.BODY;
25366         this.tb.el.show();
25367         this.buildFooter();
25368         this.footer.show();
25369         editor.on('hide', function( ) { this.footer.hide() }, this);
25370         editor.on('show', function( ) { this.footer.show() }, this);
25371         
25372          
25373         this.rendered = true;
25374         
25375         // the all the btns;
25376         editor.on('editorevent', this.updateToolbar, this);
25377         // other toolbars need to implement this..
25378         //editor.on('editmodechange', this.updateToolbar, this);
25379     },
25380     
25381     
25382     
25383     /**
25384      * Protected method that will not generally be called directly. It triggers
25385      * a toolbar update by reading the markup state of the current selection in the editor.
25386      *
25387      * Note you can force an update by calling on('editorevent', scope, false)
25388      */
25389     updateToolbar: function(editor ,ev, sel)
25390     {
25391         
25392         if (ev) {
25393             ev.stopEvent(); // se if we can stop this looping with mutiple events.
25394         }
25395         
25396         //Roo.log(ev);
25397         // capture mouse up - this is handy for selecting images..
25398         // perhaps should go somewhere else...
25399         if(!this.editorcore.activated){
25400              this.editor.onFirstFocus();
25401             return;
25402         }
25403         Roo.log(ev ? ev.target : 'NOTARGET');
25404         
25405         
25406         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
25407         // selectNode - might want to handle IE?
25408         
25409         
25410         
25411         if (ev &&
25412             (ev.type == 'mouseup' || ev.type == 'click' ) &&
25413             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
25414             // they have click on an image...
25415             // let's see if we can change the selection...
25416             sel = ev.target;
25417             
25418             // this triggers looping?
25419             //this.editorcore.selectNode(sel);
25420              
25421         }  
25422         
25423       
25424         //var updateFooter = sel ? false : true; 
25425         
25426         
25427         var ans = this.editorcore.getAllAncestors();
25428         
25429         // pick
25430         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
25431         
25432         if (!sel) { 
25433             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
25434             sel = sel ? sel : this.editorcore.doc.body;
25435             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
25436             
25437         }
25438         
25439         var tn = sel.tagName.toUpperCase();
25440         var lastSel = this.tb.selectedNode;
25441         this.tb.selectedNode = sel;
25442         var left_label = tn;
25443         
25444         // ok see if we are editing a block?
25445         var sel_el = Roo.get(sel);
25446         var db = false;
25447         // you are not actually selecting the block.
25448         if (sel && sel.hasAttribute('data-block')) {
25449             db = sel;
25450         } else if (sel && !sel.hasAttribute('contenteditable')) {
25451             db = sel_el.findParent('[data-block]');
25452             var cepar = sel_el.findParent('[contenteditable=true]');
25453             if (db && cepar && cepar.tagName != 'BODY') {
25454                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
25455             }   
25456         }
25457         
25458         
25459         var block = false;
25460         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
25461         if (db) {
25462             block = Roo.htmleditor.Block.factory(db);
25463             if (block) {
25464                 tn = 'BLOCK.' + db.getAttribute('data-block');
25465                 
25466                 //this.editorcore.selectNode(db);
25467                 if (typeof(this.toolbars[tn]) == 'undefined') {
25468                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
25469                 }
25470                 this.toolbars[tn].selectedNode = db;
25471                 left_label = block.friendly_name;
25472                 ans = this.editorcore.getAllAncestors();
25473             }
25474             
25475                 
25476             
25477         }
25478         
25479         
25480         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
25481             return; // no change?
25482         }
25483         
25484         
25485           
25486         this.tb.el.hide();
25487         ///console.log("show: " + tn);
25488         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
25489         
25490         this.tb.el.show();
25491         // update name
25492         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
25493         
25494         
25495         // update attributes
25496         if (block) {
25497              
25498             this.tb.fields.each(function(e) {
25499                 e.setValue(block[e.name]);
25500             });
25501             
25502             
25503         } else  if (this.tb.fields && this.tb.selectedNode) {
25504             this.tb.fields.each( function(e) {
25505                 if (e.stylename) {
25506                     e.setValue(this.tb.selectedNode.style[e.stylename]);
25507                     return;
25508                 } 
25509                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
25510             }, this);
25511             this.updateToolbarStyles(this.tb.selectedNode);  
25512         }
25513         
25514         
25515        
25516         Roo.menu.MenuMgr.hideAll();
25517
25518         
25519         
25520     
25521         // update the footer
25522         //
25523         this.updateFooter(ans);
25524              
25525     },
25526     
25527     updateToolbarStyles : function(sel)
25528     {
25529         var hasStyles = false;
25530         for(var i in this.styles) {
25531             hasStyles = true;
25532             break;
25533         }
25534         
25535         // update styles
25536         if (hasStyles && this.tb.hasStyles) { 
25537             var st = this.tb.fields.item(0);
25538             
25539             st.store.removeAll();
25540             var cn = sel.className.split(/\s+/);
25541             
25542             var avs = [];
25543             if (this.styles['*']) {
25544                 
25545                 Roo.each(this.styles['*'], function(v) {
25546                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25547                 });
25548             }
25549             if (this.styles[tn]) { 
25550                 Roo.each(this.styles[tn], function(v) {
25551                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25552                 });
25553             }
25554             
25555             st.store.loadData(avs);
25556             st.collapse();
25557             st.setValue(cn);
25558         }
25559     },
25560     
25561      
25562     updateFooter : function(ans)
25563     {
25564         var html = '';
25565         if (ans === false) {
25566             this.footDisp.dom.innerHTML = '';
25567             return;
25568         }
25569         
25570         this.footerEls = ans.reverse();
25571         Roo.each(this.footerEls, function(a,i) {
25572             if (!a) { return; }
25573             html += html.length ? ' &gt; '  :  '';
25574             
25575             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
25576             
25577         });
25578        
25579         // 
25580         var sz = this.footDisp.up('td').getSize();
25581         this.footDisp.dom.style.width = (sz.width -10) + 'px';
25582         this.footDisp.dom.style.marginLeft = '5px';
25583         
25584         this.footDisp.dom.style.overflow = 'hidden';
25585         
25586         this.footDisp.dom.innerHTML = html;
25587             
25588         
25589     },
25590    
25591        
25592     // private
25593     onDestroy : function(){
25594         if(this.rendered){
25595             
25596             this.tb.items.each(function(item){
25597                 if(item.menu){
25598                     item.menu.removeAll();
25599                     if(item.menu.el){
25600                         item.menu.el.destroy();
25601                     }
25602                 }
25603                 item.destroy();
25604             });
25605              
25606         }
25607     },
25608     onFirstFocus: function() {
25609         // need to do this for all the toolbars..
25610         this.tb.items.each(function(item){
25611            item.enable();
25612         });
25613     },
25614     buildToolbar: function(tlist, nm, friendly_name, block)
25615     {
25616         var editor = this.editor;
25617         var editorcore = this.editorcore;
25618          // create a new element.
25619         var wdiv = editor.wrap.createChild({
25620                 tag: 'div'
25621             }, editor.wrap.dom.firstChild.nextSibling, true);
25622         
25623        
25624         var tb = new Roo.Toolbar(wdiv);
25625         this.tb = tb;
25626         if (tlist === false && block) {
25627             tlist = block.contextMenu(this);
25628         }
25629         
25630         tb.hasStyles = false;
25631         tb.name = nm;
25632         
25633         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
25634         
25635         var styles = Array.from(this.styles);
25636         
25637         
25638         // styles...
25639         if (styles && styles.length) {
25640             tb.hasStyles = true;
25641             // this needs a multi-select checkbox...
25642             tb.addField( new Roo.form.ComboBox({
25643                 store: new Roo.data.SimpleStore({
25644                     id : 'val',
25645                     fields: ['val', 'selected'],
25646                     data : [] 
25647                 }),
25648                 name : '-roo-edit-className',
25649                 attrname : 'className',
25650                 displayField: 'val',
25651                 typeAhead: false,
25652                 mode: 'local',
25653                 editable : false,
25654                 triggerAction: 'all',
25655                 emptyText:'Select Style',
25656                 selectOnFocus:true,
25657                 width: 130,
25658                 listeners : {
25659                     'select': function(c, r, i) {
25660                         // initial support only for on class per el..
25661                         tb.selectedNode.className =  r ? r.get('val') : '';
25662                         editorcore.syncValue();
25663                     }
25664                 }
25665     
25666             }));
25667         }
25668         
25669         var tbc = Roo.form.HtmlEditor.ToolbarContext;
25670         
25671         
25672         for (var i = 0; i < tlist.length; i++) {
25673             
25674             // newer versions will use xtype cfg to create menus.
25675             if (typeof(tlist[i].xtype) != 'undefined') {
25676                 
25677                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
25678                 
25679                 
25680                 continue;
25681             }
25682             
25683             var item = tlist[i];
25684             tb.add(item.title + ":&nbsp;");
25685             
25686             
25687             //optname == used so you can configure the options available..
25688             var opts = item.opts ? item.opts : false;
25689             if (item.optname) { // use the b
25690                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
25691            
25692             }
25693             
25694             if (opts) {
25695                 // opts == pulldown..
25696                 tb.addField( new Roo.form.ComboBox({
25697                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
25698                         id : 'val',
25699                         fields: ['val', 'display'],
25700                         data : opts  
25701                     }),
25702                     name : '-roo-edit-' + tlist[i].name,
25703                     
25704                     attrname : tlist[i].name,
25705                     stylename : item.style ? item.style : false,
25706                     
25707                     displayField: item.displayField ? item.displayField : 'val',
25708                     valueField :  'val',
25709                     typeAhead: false,
25710                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
25711                     editable : false,
25712                     triggerAction: 'all',
25713                     emptyText:'Select',
25714                     selectOnFocus:true,
25715                     width: item.width ? item.width  : 130,
25716                     listeners : {
25717                         'select': function(c, r, i) {
25718                             if (tb.selectedNode.hasAttribute('data-block')) {
25719                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25720                                 b[c.attrname] = r.get('val');
25721                                 b.updateElement(tb.selectedNode);
25722                                 editorcore.syncValue();
25723                                 return;
25724                             }
25725                             
25726                             if (c.stylename) {
25727                                 tb.selectedNode.style[c.stylename] =  r.get('val');
25728                                 editorcore.syncValue();
25729                                 return;
25730                             }
25731                             if (r === false) {
25732                                 tb.selectedNode.removeAttribute(c.attrname);
25733                                 editorcore.syncValue();
25734                                 return;
25735                             }
25736                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
25737                             editorcore.syncValue();
25738                         }
25739                     }
25740
25741                 }));
25742                 continue;
25743                     
25744                  
25745                 /*
25746                 tb.addField( new Roo.form.TextField({
25747                     name: i,
25748                     width: 100,
25749                     //allowBlank:false,
25750                     value: ''
25751                 }));
25752                 continue;
25753                 */
25754             }
25755             tb.addField( new Roo.form.TextField({
25756                 name: '-roo-edit-' + tlist[i].name,
25757                 attrname : tlist[i].name,
25758                 
25759                 width: item.width,
25760                 //allowBlank:true,
25761                 value: '',
25762                 listeners: {
25763                     'change' : function(f, nv, ov) {
25764                         
25765                         if (tb.selectedNode.hasAttribute('data-block')) {
25766                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25767                             b[f.attrname] = nv;
25768                             b.updateElement(tb.selectedNode);
25769                             editorcore.syncValue();
25770                             return;
25771                         }
25772                         
25773                         tb.selectedNode.setAttribute(f.attrname, nv);
25774                         editorcore.syncValue();
25775                     }
25776                 }
25777             }));
25778              
25779         }
25780         
25781         var _this = this;
25782         
25783         if(nm == 'BODY'){
25784             tb.addSeparator();
25785         
25786             tb.addButton( {
25787                 text: 'Stylesheets',
25788
25789                 listeners : {
25790                     click : function ()
25791                     {
25792                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
25793                     }
25794                 }
25795             });
25796         }
25797         
25798         tb.addFill();
25799         tb.addButton({
25800             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
25801     
25802             listeners : {
25803                 click : function ()
25804                 {
25805                     var sn = tb.selectedNode;
25806                     if (block) {
25807                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removalNode();
25808                         
25809                     }
25810                     if (!sn) {
25811                         return;
25812                     }
25813                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
25814                     if (sn.hasAttribute('data-block')) {
25815                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
25816                         sn.parentNode.removeChild(sn);
25817                         
25818                     } else if (sn && sn.tagName != 'BODY') {
25819                         // remove and keep parents.
25820                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
25821                         a.removeTag(sn);
25822                     }
25823                     
25824                     
25825                     var range = editorcore.createRange();
25826         
25827                     range.setStart(stn,0);
25828                     range.setEnd(stn,0); 
25829                     var selection = editorcore.getSelection();
25830                     selection.removeAllRanges();
25831                     selection.addRange(range);
25832                     
25833                     
25834                     //_this.updateToolbar(null, null, pn);
25835                     _this.updateToolbar(null, null, null);
25836                     _this.updateFooter(false);
25837                     
25838                 }
25839             }
25840             
25841                     
25842                 
25843             
25844         });
25845         
25846         
25847         tb.el.on('click', function(e){
25848             e.preventDefault(); // what does this do?
25849         });
25850         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25851         tb.el.hide();
25852         
25853         // dont need to disable them... as they will get hidden
25854         return tb;
25855          
25856         
25857     },
25858     buildFooter : function()
25859     {
25860         
25861         var fel = this.editor.wrap.createChild();
25862         this.footer = new Roo.Toolbar(fel);
25863         // toolbar has scrolly on left / right?
25864         var footDisp= new Roo.Toolbar.Fill();
25865         var _t = this;
25866         this.footer.add(
25867             {
25868                 text : '&lt;',
25869                 xtype: 'Button',
25870                 handler : function() {
25871                     _t.footDisp.scrollTo('left',0,true)
25872                 }
25873             }
25874         );
25875         this.footer.add( footDisp );
25876         this.footer.add( 
25877             {
25878                 text : '&gt;',
25879                 xtype: 'Button',
25880                 handler : function() {
25881                     // no animation..
25882                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
25883                 }
25884             }
25885         );
25886         var fel = Roo.get(footDisp.el);
25887         fel.addClass('x-editor-context');
25888         this.footDispWrap = fel; 
25889         this.footDispWrap.overflow  = 'hidden';
25890         
25891         this.footDisp = fel.createChild();
25892         this.footDispWrap.on('click', this.onContextClick, this)
25893         
25894         
25895     },
25896     // when the footer contect changes
25897     onContextClick : function (ev,dom)
25898     {
25899         ev.preventDefault();
25900         var  cn = dom.className;
25901         //Roo.log(cn);
25902         if (!cn.match(/x-ed-loc-/)) {
25903             return;
25904         }
25905         var n = cn.split('-').pop();
25906         var ans = this.footerEls;
25907         var sel = ans[n];
25908         
25909          // pick
25910         var range = this.editorcore.createRange();
25911         
25912         range.selectNodeContents(sel);
25913         //range.selectNode(sel);
25914         
25915         
25916         var selection = this.editorcore.getSelection();
25917         selection.removeAllRanges();
25918         selection.addRange(range);
25919         
25920         
25921         
25922         this.updateToolbar(null, null, sel);
25923         
25924         
25925     }
25926     
25927     
25928     
25929     
25930     
25931 });
25932
25933
25934
25935
25936
25937 /*
25938  * Based on:
25939  * Ext JS Library 1.1.1
25940  * Copyright(c) 2006-2007, Ext JS, LLC.
25941  *
25942  * Originally Released Under LGPL - original licence link has changed is not relivant.
25943  *
25944  * Fork - LGPL
25945  * <script type="text/javascript">
25946  */
25947  
25948 /**
25949  * @class Roo.form.BasicForm
25950  * @extends Roo.util.Observable
25951  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25952  * @constructor
25953  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25954  * @param {Object} config Configuration options
25955  */
25956 Roo.form.BasicForm = function(el, config){
25957     this.allItems = [];
25958     this.childForms = [];
25959     Roo.apply(this, config);
25960     /*
25961      * The Roo.form.Field items in this form.
25962      * @type MixedCollection
25963      */
25964      
25965      
25966     this.items = new Roo.util.MixedCollection(false, function(o){
25967         return o.id || (o.id = Roo.id());
25968     });
25969     this.addEvents({
25970         /**
25971          * @event beforeaction
25972          * Fires before any action is performed. Return false to cancel the action.
25973          * @param {Form} this
25974          * @param {Action} action The action to be performed
25975          */
25976         beforeaction: true,
25977         /**
25978          * @event actionfailed
25979          * Fires when an action fails.
25980          * @param {Form} this
25981          * @param {Action} action The action that failed
25982          */
25983         actionfailed : true,
25984         /**
25985          * @event actioncomplete
25986          * Fires when an action is completed.
25987          * @param {Form} this
25988          * @param {Action} action The action that completed
25989          */
25990         actioncomplete : true
25991     });
25992     if(el){
25993         this.initEl(el);
25994     }
25995     Roo.form.BasicForm.superclass.constructor.call(this);
25996     
25997     Roo.form.BasicForm.popover.apply();
25998 };
25999
26000 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26001     /**
26002      * @cfg {String} method
26003      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26004      */
26005     /**
26006      * @cfg {DataReader} reader
26007      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26008      * This is optional as there is built-in support for processing JSON.
26009      */
26010     /**
26011      * @cfg {DataReader} errorReader
26012      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26013      * This is completely optional as there is built-in support for processing JSON.
26014      */
26015     /**
26016      * @cfg {String} url
26017      * The URL to use for form actions if one isn't supplied in the action options.
26018      */
26019     /**
26020      * @cfg {Boolean} fileUpload
26021      * Set to true if this form is a file upload.
26022      */
26023      
26024     /**
26025      * @cfg {Object} baseParams
26026      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26027      */
26028      /**
26029      
26030     /**
26031      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26032      */
26033     timeout: 30,
26034
26035     // private
26036     activeAction : null,
26037
26038     /**
26039      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26040      * or setValues() data instead of when the form was first created.
26041      */
26042     trackResetOnLoad : false,
26043     
26044     
26045     /**
26046      * childForms - used for multi-tab forms
26047      * @type {Array}
26048      */
26049     childForms : false,
26050     
26051     /**
26052      * allItems - full list of fields.
26053      * @type {Array}
26054      */
26055     allItems : false,
26056     
26057     /**
26058      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26059      * element by passing it or its id or mask the form itself by passing in true.
26060      * @type Mixed
26061      */
26062     waitMsgTarget : false,
26063     
26064     /**
26065      * @type Boolean
26066      */
26067     disableMask : false,
26068     
26069     /**
26070      * @cfg {Boolean} errorMask (true|false) default false
26071      */
26072     errorMask : false,
26073     
26074     /**
26075      * @cfg {Number} maskOffset Default 100
26076      */
26077     maskOffset : 100,
26078
26079     // private
26080     initEl : function(el){
26081         this.el = Roo.get(el);
26082         this.id = this.el.id || Roo.id();
26083         this.el.on('submit', this.onSubmit, this);
26084         this.el.addClass('x-form');
26085     },
26086
26087     // private
26088     onSubmit : function(e){
26089         e.stopEvent();
26090     },
26091
26092     /**
26093      * Returns true if client-side validation on the form is successful.
26094      * @return Boolean
26095      */
26096     isValid : function(){
26097         var valid = true;
26098         var target = false;
26099         this.items.each(function(f){
26100             if(f.validate()){
26101                 return;
26102             }
26103             
26104             valid = false;
26105                 
26106             if(!target && f.el.isVisible(true)){
26107                 target = f;
26108             }
26109         });
26110         
26111         if(this.errorMask && !valid){
26112             Roo.form.BasicForm.popover.mask(this, target);
26113         }
26114         
26115         return valid;
26116     },
26117     /**
26118      * Returns array of invalid form fields.
26119      * @return Array
26120      */
26121     
26122     invalidFields : function()
26123     {
26124         var ret = [];
26125         this.items.each(function(f){
26126             if(f.validate()){
26127                 return;
26128             }
26129             ret.push(f);
26130             
26131         });
26132         
26133         return ret;
26134     },
26135     
26136     
26137     /**
26138      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
26139      * @return Boolean
26140      */
26141     isDirty : function(){
26142         var dirty = false;
26143         this.items.each(function(f){
26144            if(f.isDirty()){
26145                dirty = true;
26146                return false;
26147            }
26148         });
26149         return dirty;
26150     },
26151     
26152     /**
26153      * Returns true if any fields in this form have changed since their original load. (New version)
26154      * @return Boolean
26155      */
26156     
26157     hasChanged : function()
26158     {
26159         var dirty = false;
26160         this.items.each(function(f){
26161            if(f.hasChanged()){
26162                dirty = true;
26163                return false;
26164            }
26165         });
26166         return dirty;
26167         
26168     },
26169     /**
26170      * Resets all hasChanged to 'false' -
26171      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
26172      * So hasChanged storage is only to be used for this purpose
26173      * @return Boolean
26174      */
26175     resetHasChanged : function()
26176     {
26177         this.items.each(function(f){
26178            f.resetHasChanged();
26179         });
26180         
26181     },
26182     
26183     
26184     /**
26185      * Performs a predefined action (submit or load) or custom actions you define on this form.
26186      * @param {String} actionName The name of the action type
26187      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26188      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26189      * accept other config options):
26190      * <pre>
26191 Property          Type             Description
26192 ----------------  ---------------  ----------------------------------------------------------------------------------
26193 url               String           The url for the action (defaults to the form's url)
26194 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26195 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26196 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26197                                    validate the form on the client (defaults to false)
26198      * </pre>
26199      * @return {BasicForm} this
26200      */
26201     doAction : function(action, options){
26202         if(typeof action == 'string'){
26203             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26204         }
26205         if(this.fireEvent('beforeaction', this, action) !== false){
26206             this.beforeAction(action);
26207             action.run.defer(100, action);
26208         }
26209         return this;
26210     },
26211
26212     /**
26213      * Shortcut to do a submit action.
26214      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26215      * @return {BasicForm} this
26216      */
26217     submit : function(options){
26218         this.doAction('submit', options);
26219         return this;
26220     },
26221
26222     /**
26223      * Shortcut to do a load action.
26224      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26225      * @return {BasicForm} this
26226      */
26227     load : function(options){
26228         this.doAction('load', options);
26229         return this;
26230     },
26231
26232     /**
26233      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26234      * @param {Record} record The record to edit
26235      * @return {BasicForm} this
26236      */
26237     updateRecord : function(record){
26238         record.beginEdit();
26239         var fs = record.fields;
26240         fs.each(function(f){
26241             var field = this.findField(f.name);
26242             if(field){
26243                 record.set(f.name, field.getValue());
26244             }
26245         }, this);
26246         record.endEdit();
26247         return this;
26248     },
26249
26250     /**
26251      * Loads an Roo.data.Record into this form.
26252      * @param {Record} record The record to load
26253      * @return {BasicForm} this
26254      */
26255     loadRecord : function(record){
26256         this.setValues(record.data);
26257         return this;
26258     },
26259
26260     // private
26261     beforeAction : function(action){
26262         var o = action.options;
26263         
26264         if(!this.disableMask) {
26265             if(this.waitMsgTarget === true){
26266                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26267             }else if(this.waitMsgTarget){
26268                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26269                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26270             }else {
26271                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26272             }
26273         }
26274         
26275          
26276     },
26277
26278     // private
26279     afterAction : function(action, success){
26280         this.activeAction = null;
26281         var o = action.options;
26282         
26283         if(!this.disableMask) {
26284             if(this.waitMsgTarget === true){
26285                 this.el.unmask();
26286             }else if(this.waitMsgTarget){
26287                 this.waitMsgTarget.unmask();
26288             }else{
26289                 Roo.MessageBox.updateProgress(1);
26290                 Roo.MessageBox.hide();
26291             }
26292         }
26293         
26294         if(success){
26295             if(o.reset){
26296                 this.reset();
26297             }
26298             Roo.callback(o.success, o.scope, [this, action]);
26299             this.fireEvent('actioncomplete', this, action);
26300             
26301         }else{
26302             
26303             // failure condition..
26304             // we have a scenario where updates need confirming.
26305             // eg. if a locking scenario exists..
26306             // we look for { errors : { needs_confirm : true }} in the response.
26307             if (
26308                 (typeof(action.result) != 'undefined')  &&
26309                 (typeof(action.result.errors) != 'undefined')  &&
26310                 (typeof(action.result.errors.needs_confirm) != 'undefined')
26311            ){
26312                 var _t = this;
26313                 Roo.MessageBox.confirm(
26314                     "Change requires confirmation",
26315                     action.result.errorMsg,
26316                     function(r) {
26317                         if (r != 'yes') {
26318                             return;
26319                         }
26320                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
26321                     }
26322                     
26323                 );
26324                 
26325                 
26326                 
26327                 return;
26328             }
26329             
26330             Roo.callback(o.failure, o.scope, [this, action]);
26331             // show an error message if no failed handler is set..
26332             if (!this.hasListener('actionfailed')) {
26333                 Roo.MessageBox.alert("Error",
26334                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26335                         action.result.errorMsg :
26336                         "Saving Failed, please check your entries or try again"
26337                 );
26338             }
26339             
26340             this.fireEvent('actionfailed', this, action);
26341         }
26342         
26343     },
26344
26345     /**
26346      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26347      * @param {String} id The value to search for
26348      * @return Field
26349      */
26350     findField : function(id){
26351         var field = this.items.get(id);
26352         if(!field){
26353             this.items.each(function(f){
26354                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26355                     field = f;
26356                     return false;
26357                 }
26358             });
26359         }
26360         return field || null;
26361     },
26362
26363     /**
26364      * Add a secondary form to this one, 
26365      * Used to provide tabbed forms. One form is primary, with hidden values 
26366      * which mirror the elements from the other forms.
26367      * 
26368      * @param {Roo.form.Form} form to add.
26369      * 
26370      */
26371     addForm : function(form)
26372     {
26373        
26374         if (this.childForms.indexOf(form) > -1) {
26375             // already added..
26376             return;
26377         }
26378         this.childForms.push(form);
26379         var n = '';
26380         Roo.each(form.allItems, function (fe) {
26381             
26382             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26383             if (this.findField(n)) { // already added..
26384                 return;
26385             }
26386             var add = new Roo.form.Hidden({
26387                 name : n
26388             });
26389             add.render(this.el);
26390             
26391             this.add( add );
26392         }, this);
26393         
26394     },
26395     /**
26396      * Mark fields in this form invalid in bulk.
26397      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26398      * @return {BasicForm} this
26399      */
26400     markInvalid : function(errors){
26401         if(errors instanceof Array){
26402             for(var i = 0, len = errors.length; i < len; i++){
26403                 var fieldError = errors[i];
26404                 var f = this.findField(fieldError.id);
26405                 if(f){
26406                     f.markInvalid(fieldError.msg);
26407                 }
26408             }
26409         }else{
26410             var field, id;
26411             for(id in errors){
26412                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26413                     field.markInvalid(errors[id]);
26414                 }
26415             }
26416         }
26417         Roo.each(this.childForms || [], function (f) {
26418             f.markInvalid(errors);
26419         });
26420         
26421         return this;
26422     },
26423
26424     /**
26425      * Set values for fields in this form in bulk.
26426      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26427      * @return {BasicForm} this
26428      */
26429     setValues : function(values){
26430         if(values instanceof Array){ // array of objects
26431             for(var i = 0, len = values.length; i < len; i++){
26432                 var v = values[i];
26433                 var f = this.findField(v.id);
26434                 if(f){
26435                     f.setValue(v.value);
26436                     if(this.trackResetOnLoad){
26437                         f.originalValue = f.getValue();
26438                     }
26439                 }
26440             }
26441         }else{ // object hash
26442             var field, id;
26443             for(id in values){
26444                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26445                     
26446                     if (field.setFromData && 
26447                         field.valueField && 
26448                         field.displayField &&
26449                         // combos' with local stores can 
26450                         // be queried via setValue()
26451                         // to set their value..
26452                         (field.store && !field.store.isLocal)
26453                         ) {
26454                         // it's a combo
26455                         var sd = { };
26456                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26457                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26458                         field.setFromData(sd);
26459                         
26460                     } else {
26461                         field.setValue(values[id]);
26462                     }
26463                     
26464                     
26465                     if(this.trackResetOnLoad){
26466                         field.originalValue = field.getValue();
26467                     }
26468                 }
26469             }
26470         }
26471         this.resetHasChanged();
26472         
26473         
26474         Roo.each(this.childForms || [], function (f) {
26475             f.setValues(values);
26476             f.resetHasChanged();
26477         });
26478                 
26479         return this;
26480     },
26481  
26482     /**
26483      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26484      * they are returned as an array.
26485      * @param {Boolean} asString
26486      * @return {Object}
26487      */
26488     getValues : function(asString)
26489     {
26490         if (this.childForms) {
26491             // copy values from the child forms
26492             Roo.each(this.childForms, function (f) {
26493                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
26494             }, this);
26495         }
26496         
26497         // use formdata
26498         if (typeof(FormData) != 'undefined' && asString !== true) {
26499             // this relies on a 'recent' version of chrome apparently...
26500             try {
26501                 var fd = (new FormData(this.el.dom)).entries();
26502                 var ret = {};
26503                 var ent = fd.next();
26504                 while (!ent.done) {
26505                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
26506                     ent = fd.next();
26507                 };
26508                 return ret;
26509             } catch(e) {
26510                 
26511             }
26512             
26513         }
26514         
26515         
26516         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26517         if(asString === true){
26518             return fs;
26519         }
26520         return Roo.urlDecode(fs);
26521     },
26522     
26523     /**
26524      * Returns the fields in this form as an object with key/value pairs. 
26525      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26526      * @return {Object}
26527      */
26528     getFieldValues : function(with_hidden)
26529     {
26530         if (this.childForms) {
26531             // copy values from the child forms
26532             // should this call getFieldValues - probably not as we do not currently copy
26533             // hidden fields when we generate..
26534             Roo.each(this.childForms, function (f) {
26535                 this.setValues(f.getFieldValues());
26536             }, this);
26537         }
26538         
26539         var ret = {};
26540         this.items.each(function(f){
26541             if (!f.getName()) {
26542                 return;
26543             }
26544             var v = f.getValue();
26545             if (f.inputType =='radio') {
26546                 if (typeof(ret[f.getName()]) == 'undefined') {
26547                     ret[f.getName()] = ''; // empty..
26548                 }
26549                 
26550                 if (!f.el.dom.checked) {
26551                     return;
26552                     
26553                 }
26554                 v = f.el.dom.value;
26555                 
26556             }
26557             
26558             // not sure if this supported any more..
26559             if ((typeof(v) == 'object') && f.getRawValue) {
26560                 v = f.getRawValue() ; // dates..
26561             }
26562             // combo boxes where name != hiddenName...
26563             if (f.name != f.getName()) {
26564                 ret[f.name] = f.getRawValue();
26565             }
26566             ret[f.getName()] = v;
26567         });
26568         
26569         return ret;
26570     },
26571
26572     /**
26573      * Clears all invalid messages in this form.
26574      * @return {BasicForm} this
26575      */
26576     clearInvalid : function(){
26577         this.items.each(function(f){
26578            f.clearInvalid();
26579         });
26580         
26581         Roo.each(this.childForms || [], function (f) {
26582             f.clearInvalid();
26583         });
26584         
26585         
26586         return this;
26587     },
26588
26589     /**
26590      * Resets this form.
26591      * @return {BasicForm} this
26592      */
26593     reset : function(){
26594         this.items.each(function(f){
26595             f.reset();
26596         });
26597         
26598         Roo.each(this.childForms || [], function (f) {
26599             f.reset();
26600         });
26601         this.resetHasChanged();
26602         
26603         return this;
26604     },
26605
26606     /**
26607      * Add Roo.form components to this form.
26608      * @param {Field} field1
26609      * @param {Field} field2 (optional)
26610      * @param {Field} etc (optional)
26611      * @return {BasicForm} this
26612      */
26613     add : function(){
26614         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26615         return this;
26616     },
26617
26618
26619     /**
26620      * Removes a field from the items collection (does NOT remove its markup).
26621      * @param {Field} field
26622      * @return {BasicForm} this
26623      */
26624     remove : function(field){
26625         this.items.remove(field);
26626         return this;
26627     },
26628
26629     /**
26630      * Looks at the fields in this form, checks them for an id attribute,
26631      * and calls applyTo on the existing dom element with that id.
26632      * @return {BasicForm} this
26633      */
26634     render : function(){
26635         this.items.each(function(f){
26636             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26637                 f.applyTo(f.id);
26638             }
26639         });
26640         return this;
26641     },
26642
26643     /**
26644      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26645      * @param {Object} values
26646      * @return {BasicForm} this
26647      */
26648     applyToFields : function(o){
26649         this.items.each(function(f){
26650            Roo.apply(f, o);
26651         });
26652         return this;
26653     },
26654
26655     /**
26656      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26657      * @param {Object} values
26658      * @return {BasicForm} this
26659      */
26660     applyIfToFields : function(o){
26661         this.items.each(function(f){
26662            Roo.applyIf(f, o);
26663         });
26664         return this;
26665     }
26666 });
26667
26668 // back compat
26669 Roo.BasicForm = Roo.form.BasicForm;
26670
26671 Roo.apply(Roo.form.BasicForm, {
26672     
26673     popover : {
26674         
26675         padding : 5,
26676         
26677         isApplied : false,
26678         
26679         isMasked : false,
26680         
26681         form : false,
26682         
26683         target : false,
26684         
26685         intervalID : false,
26686         
26687         maskEl : false,
26688         
26689         apply : function()
26690         {
26691             if(this.isApplied){
26692                 return;
26693             }
26694             
26695             this.maskEl = {
26696                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
26697                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
26698                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
26699                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
26700             };
26701             
26702             this.maskEl.top.enableDisplayMode("block");
26703             this.maskEl.left.enableDisplayMode("block");
26704             this.maskEl.bottom.enableDisplayMode("block");
26705             this.maskEl.right.enableDisplayMode("block");
26706             
26707             Roo.get(document.body).on('click', function(){
26708                 this.unmask();
26709             }, this);
26710             
26711             Roo.get(document.body).on('touchstart', function(){
26712                 this.unmask();
26713             }, this);
26714             
26715             this.isApplied = true
26716         },
26717         
26718         mask : function(form, target)
26719         {
26720             this.form = form;
26721             
26722             this.target = target;
26723             
26724             if(!this.form.errorMask || !target.el){
26725                 return;
26726             }
26727             
26728             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
26729             
26730             var ot = this.target.el.calcOffsetsTo(scrollable);
26731             
26732             var scrollTo = ot[1] - this.form.maskOffset;
26733             
26734             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
26735             
26736             scrollable.scrollTo('top', scrollTo);
26737             
26738             var el = this.target.wrap || this.target.el;
26739             
26740             var box = el.getBox();
26741             
26742             this.maskEl.top.setStyle('position', 'absolute');
26743             this.maskEl.top.setStyle('z-index', 10000);
26744             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
26745             this.maskEl.top.setLeft(0);
26746             this.maskEl.top.setTop(0);
26747             this.maskEl.top.show();
26748             
26749             this.maskEl.left.setStyle('position', 'absolute');
26750             this.maskEl.left.setStyle('z-index', 10000);
26751             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
26752             this.maskEl.left.setLeft(0);
26753             this.maskEl.left.setTop(box.y - this.padding);
26754             this.maskEl.left.show();
26755
26756             this.maskEl.bottom.setStyle('position', 'absolute');
26757             this.maskEl.bottom.setStyle('z-index', 10000);
26758             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
26759             this.maskEl.bottom.setLeft(0);
26760             this.maskEl.bottom.setTop(box.bottom + this.padding);
26761             this.maskEl.bottom.show();
26762
26763             this.maskEl.right.setStyle('position', 'absolute');
26764             this.maskEl.right.setStyle('z-index', 10000);
26765             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
26766             this.maskEl.right.setLeft(box.right + this.padding);
26767             this.maskEl.right.setTop(box.y - this.padding);
26768             this.maskEl.right.show();
26769
26770             this.intervalID = window.setInterval(function() {
26771                 Roo.form.BasicForm.popover.unmask();
26772             }, 10000);
26773
26774             window.onwheel = function(){ return false;};
26775             
26776             (function(){ this.isMasked = true; }).defer(500, this);
26777             
26778         },
26779         
26780         unmask : function()
26781         {
26782             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
26783                 return;
26784             }
26785             
26786             this.maskEl.top.setStyle('position', 'absolute');
26787             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
26788             this.maskEl.top.hide();
26789
26790             this.maskEl.left.setStyle('position', 'absolute');
26791             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
26792             this.maskEl.left.hide();
26793
26794             this.maskEl.bottom.setStyle('position', 'absolute');
26795             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
26796             this.maskEl.bottom.hide();
26797
26798             this.maskEl.right.setStyle('position', 'absolute');
26799             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
26800             this.maskEl.right.hide();
26801             
26802             window.onwheel = function(){ return true;};
26803             
26804             if(this.intervalID){
26805                 window.clearInterval(this.intervalID);
26806                 this.intervalID = false;
26807             }
26808             
26809             this.isMasked = false;
26810             
26811         }
26812         
26813     }
26814     
26815 });/*
26816  * Based on:
26817  * Ext JS Library 1.1.1
26818  * Copyright(c) 2006-2007, Ext JS, LLC.
26819  *
26820  * Originally Released Under LGPL - original licence link has changed is not relivant.
26821  *
26822  * Fork - LGPL
26823  * <script type="text/javascript">
26824  */
26825
26826 /**
26827  * @class Roo.form.Form
26828  * @extends Roo.form.BasicForm
26829  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26830  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26831  * @constructor
26832  * @param {Object} config Configuration options
26833  */
26834 Roo.form.Form = function(config){
26835     var xitems =  [];
26836     if (config.items) {
26837         xitems = config.items;
26838         delete config.items;
26839     }
26840    
26841     
26842     Roo.form.Form.superclass.constructor.call(this, null, config);
26843     this.url = this.url || this.action;
26844     if(!this.root){
26845         this.root = new Roo.form.Layout(Roo.applyIf({
26846             id: Roo.id()
26847         }, config));
26848     }
26849     this.active = this.root;
26850     /**
26851      * Array of all the buttons that have been added to this form via {@link addButton}
26852      * @type Array
26853      */
26854     this.buttons = [];
26855     this.allItems = [];
26856     this.addEvents({
26857         /**
26858          * @event clientvalidation
26859          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26860          * @param {Form} this
26861          * @param {Boolean} valid true if the form has passed client-side validation
26862          */
26863         clientvalidation: true,
26864         /**
26865          * @event rendered
26866          * Fires when the form is rendered
26867          * @param {Roo.form.Form} form
26868          */
26869         rendered : true
26870     });
26871     
26872     if (this.progressUrl) {
26873             // push a hidden field onto the list of fields..
26874             this.addxtype( {
26875                     xns: Roo.form, 
26876                     xtype : 'Hidden', 
26877                     name : 'UPLOAD_IDENTIFIER' 
26878             });
26879         }
26880         
26881     
26882     Roo.each(xitems, this.addxtype, this);
26883     
26884 };
26885
26886 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26887      /**
26888      * @cfg {Roo.Button} buttons[] buttons at bottom of form
26889      */
26890     
26891     /**
26892      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26893      */
26894     /**
26895      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26896      */
26897     /**
26898      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26899      */
26900     buttonAlign:'center',
26901
26902     /**
26903      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26904      */
26905     minButtonWidth:75,
26906
26907     /**
26908      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26909      * This property cascades to child containers if not set.
26910      */
26911     labelAlign:'left',
26912
26913     /**
26914      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26915      * fires a looping event with that state. This is required to bind buttons to the valid
26916      * state using the config value formBind:true on the button.
26917      */
26918     monitorValid : false,
26919
26920     /**
26921      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26922      */
26923     monitorPoll : 200,
26924     
26925     /**
26926      * @cfg {String} progressUrl - Url to return progress data 
26927      */
26928     
26929     progressUrl : false,
26930     /**
26931      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
26932      * sending a formdata with extra parameters - eg uploaded elements.
26933      */
26934     
26935     formData : false,
26936     
26937     /**
26938      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26939      * fields are added and the column is closed. If no fields are passed the column remains open
26940      * until end() is called.
26941      * @param {Object} config The config to pass to the column
26942      * @param {Field} field1 (optional)
26943      * @param {Field} field2 (optional)
26944      * @param {Field} etc (optional)
26945      * @return Column The column container object
26946      */
26947     column : function(c){
26948         var col = new Roo.form.Column(c);
26949         this.start(col);
26950         if(arguments.length > 1){ // duplicate code required because of Opera
26951             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26952             this.end();
26953         }
26954         return col;
26955     },
26956
26957     /**
26958      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26959      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26960      * until end() is called.
26961      * @param {Object} config The config to pass to the fieldset
26962      * @param {Field} field1 (optional)
26963      * @param {Field} field2 (optional)
26964      * @param {Field} etc (optional)
26965      * @return FieldSet The fieldset container object
26966      */
26967     fieldset : function(c){
26968         var fs = new Roo.form.FieldSet(c);
26969         this.start(fs);
26970         if(arguments.length > 1){ // duplicate code required because of Opera
26971             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26972             this.end();
26973         }
26974         return fs;
26975     },
26976
26977     /**
26978      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26979      * fields are added and the container is closed. If no fields are passed the container remains open
26980      * until end() is called.
26981      * @param {Object} config The config to pass to the Layout
26982      * @param {Field} field1 (optional)
26983      * @param {Field} field2 (optional)
26984      * @param {Field} etc (optional)
26985      * @return Layout The container object
26986      */
26987     container : function(c){
26988         var l = new Roo.form.Layout(c);
26989         this.start(l);
26990         if(arguments.length > 1){ // duplicate code required because of Opera
26991             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26992             this.end();
26993         }
26994         return l;
26995     },
26996
26997     /**
26998      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26999      * @param {Object} container A Roo.form.Layout or subclass of Layout
27000      * @return {Form} this
27001      */
27002     start : function(c){
27003         // cascade label info
27004         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27005         this.active.stack.push(c);
27006         c.ownerCt = this.active;
27007         this.active = c;
27008         return this;
27009     },
27010
27011     /**
27012      * Closes the current open container
27013      * @return {Form} this
27014      */
27015     end : function(){
27016         if(this.active == this.root){
27017             return this;
27018         }
27019         this.active = this.active.ownerCt;
27020         return this;
27021     },
27022
27023     /**
27024      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27025      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27026      * as the label of the field.
27027      * @param {Field} field1
27028      * @param {Field} field2 (optional)
27029      * @param {Field} etc. (optional)
27030      * @return {Form} this
27031      */
27032     add : function(){
27033         this.active.stack.push.apply(this.active.stack, arguments);
27034         this.allItems.push.apply(this.allItems,arguments);
27035         var r = [];
27036         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27037             if(a[i].isFormField){
27038                 r.push(a[i]);
27039             }
27040         }
27041         if(r.length > 0){
27042             Roo.form.Form.superclass.add.apply(this, r);
27043         }
27044         return this;
27045     },
27046     
27047
27048     
27049     
27050     
27051      /**
27052      * Find any element that has been added to a form, using it's ID or name
27053      * This can include framesets, columns etc. along with regular fields..
27054      * @param {String} id - id or name to find.
27055      
27056      * @return {Element} e - or false if nothing found.
27057      */
27058     findbyId : function(id)
27059     {
27060         var ret = false;
27061         if (!id) {
27062             return ret;
27063         }
27064         Roo.each(this.allItems, function(f){
27065             if (f.id == id || f.name == id ){
27066                 ret = f;
27067                 return false;
27068             }
27069         });
27070         return ret;
27071     },
27072
27073     
27074     
27075     /**
27076      * Render this form into the passed container. This should only be called once!
27077      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27078      * @return {Form} this
27079      */
27080     render : function(ct)
27081     {
27082         
27083         
27084         
27085         ct = Roo.get(ct);
27086         var o = this.autoCreate || {
27087             tag: 'form',
27088             method : this.method || 'POST',
27089             id : this.id || Roo.id()
27090         };
27091         this.initEl(ct.createChild(o));
27092
27093         this.root.render(this.el);
27094         
27095        
27096              
27097         this.items.each(function(f){
27098             f.render('x-form-el-'+f.id);
27099         });
27100
27101         if(this.buttons.length > 0){
27102             // tables are required to maintain order and for correct IE layout
27103             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27104                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27105                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27106             }}, null, true);
27107             var tr = tb.getElementsByTagName('tr')[0];
27108             for(var i = 0, len = this.buttons.length; i < len; i++) {
27109                 var b = this.buttons[i];
27110                 var td = document.createElement('td');
27111                 td.className = 'x-form-btn-td';
27112                 b.render(tr.appendChild(td));
27113             }
27114         }
27115         if(this.monitorValid){ // initialize after render
27116             this.startMonitoring();
27117         }
27118         this.fireEvent('rendered', this);
27119         return this;
27120     },
27121
27122     /**
27123      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27124      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27125      * object or a valid Roo.DomHelper element config
27126      * @param {Function} handler The function called when the button is clicked
27127      * @param {Object} scope (optional) The scope of the handler function
27128      * @return {Roo.Button}
27129      */
27130     addButton : function(config, handler, scope){
27131         var bc = {
27132             handler: handler,
27133             scope: scope,
27134             minWidth: this.minButtonWidth,
27135             hideParent:true
27136         };
27137         if(typeof config == "string"){
27138             bc.text = config;
27139         }else{
27140             Roo.apply(bc, config);
27141         }
27142         var btn = new Roo.Button(null, bc);
27143         this.buttons.push(btn);
27144         return btn;
27145     },
27146
27147      /**
27148      * Adds a series of form elements (using the xtype property as the factory method.
27149      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27150      * @param {Object} config 
27151      */
27152     
27153     addxtype : function()
27154     {
27155         var ar = Array.prototype.slice.call(arguments, 0);
27156         var ret = false;
27157         for(var i = 0; i < ar.length; i++) {
27158             if (!ar[i]) {
27159                 continue; // skip -- if this happends something invalid got sent, we 
27160                 // should ignore it, as basically that interface element will not show up
27161                 // and that should be pretty obvious!!
27162             }
27163             
27164             if (Roo.form[ar[i].xtype]) {
27165                 ar[i].form = this;
27166                 var fe = Roo.factory(ar[i], Roo.form);
27167                 if (!ret) {
27168                     ret = fe;
27169                 }
27170                 fe.form = this;
27171                 if (fe.store) {
27172                     fe.store.form = this;
27173                 }
27174                 if (fe.isLayout) {  
27175                          
27176                     this.start(fe);
27177                     this.allItems.push(fe);
27178                     if (fe.items && fe.addxtype) {
27179                         fe.addxtype.apply(fe, fe.items);
27180                         delete fe.items;
27181                     }
27182                      this.end();
27183                     continue;
27184                 }
27185                 
27186                 
27187                  
27188                 this.add(fe);
27189               //  console.log('adding ' + ar[i].xtype);
27190             }
27191             if (ar[i].xtype == 'Button') {  
27192                 //console.log('adding button');
27193                 //console.log(ar[i]);
27194                 this.addButton(ar[i]);
27195                 this.allItems.push(fe);
27196                 continue;
27197             }
27198             
27199             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27200                 alert('end is not supported on xtype any more, use items');
27201             //    this.end();
27202             //    //console.log('adding end');
27203             }
27204             
27205         }
27206         return ret;
27207     },
27208     
27209     /**
27210      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27211      * option "monitorValid"
27212      */
27213     startMonitoring : function(){
27214         if(!this.bound){
27215             this.bound = true;
27216             Roo.TaskMgr.start({
27217                 run : this.bindHandler,
27218                 interval : this.monitorPoll || 200,
27219                 scope: this
27220             });
27221         }
27222     },
27223
27224     /**
27225      * Stops monitoring of the valid state of this form
27226      */
27227     stopMonitoring : function(){
27228         this.bound = false;
27229     },
27230
27231     // private
27232     bindHandler : function(){
27233         if(!this.bound){
27234             return false; // stops binding
27235         }
27236         var valid = true;
27237         this.items.each(function(f){
27238             if(!f.isValid(true)){
27239                 valid = false;
27240                 return false;
27241             }
27242         });
27243         for(var i = 0, len = this.buttons.length; i < len; i++){
27244             var btn = this.buttons[i];
27245             if(btn.formBind === true && btn.disabled === valid){
27246                 btn.setDisabled(!valid);
27247             }
27248         }
27249         this.fireEvent('clientvalidation', this, valid);
27250     }
27251     
27252     
27253     
27254     
27255     
27256     
27257     
27258     
27259 });
27260
27261
27262 // back compat
27263 Roo.Form = Roo.form.Form;
27264 /*
27265  * Based on:
27266  * Ext JS Library 1.1.1
27267  * Copyright(c) 2006-2007, Ext JS, LLC.
27268  *
27269  * Originally Released Under LGPL - original licence link has changed is not relivant.
27270  *
27271  * Fork - LGPL
27272  * <script type="text/javascript">
27273  */
27274
27275 // as we use this in bootstrap.
27276 Roo.namespace('Roo.form');
27277  /**
27278  * @class Roo.form.Action
27279  * Internal Class used to handle form actions
27280  * @constructor
27281  * @param {Roo.form.BasicForm} el The form element or its id
27282  * @param {Object} config Configuration options
27283  */
27284
27285  
27286  
27287 // define the action interface
27288 Roo.form.Action = function(form, options){
27289     this.form = form;
27290     this.options = options || {};
27291 };
27292 /**
27293  * Client Validation Failed
27294  * @const 
27295  */
27296 Roo.form.Action.CLIENT_INVALID = 'client';
27297 /**
27298  * Server Validation Failed
27299  * @const 
27300  */
27301 Roo.form.Action.SERVER_INVALID = 'server';
27302  /**
27303  * Connect to Server Failed
27304  * @const 
27305  */
27306 Roo.form.Action.CONNECT_FAILURE = 'connect';
27307 /**
27308  * Reading Data from Server Failed
27309  * @const 
27310  */
27311 Roo.form.Action.LOAD_FAILURE = 'load';
27312
27313 Roo.form.Action.prototype = {
27314     type : 'default',
27315     failureType : undefined,
27316     response : undefined,
27317     result : undefined,
27318
27319     // interface method
27320     run : function(options){
27321
27322     },
27323
27324     // interface method
27325     success : function(response){
27326
27327     },
27328
27329     // interface method
27330     handleResponse : function(response){
27331
27332     },
27333
27334     // default connection failure
27335     failure : function(response){
27336         
27337         this.response = response;
27338         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27339         this.form.afterAction(this, false);
27340     },
27341
27342     processResponse : function(response){
27343         this.response = response;
27344         if(!response.responseText){
27345             return true;
27346         }
27347         this.result = this.handleResponse(response);
27348         return this.result;
27349     },
27350
27351     // utility functions used internally
27352     getUrl : function(appendParams){
27353         var url = this.options.url || this.form.url || this.form.el.dom.action;
27354         if(appendParams){
27355             var p = this.getParams();
27356             if(p){
27357                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27358             }
27359         }
27360         return url;
27361     },
27362
27363     getMethod : function(){
27364         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27365     },
27366
27367     getParams : function(){
27368         var bp = this.form.baseParams;
27369         var p = this.options.params;
27370         if(p){
27371             if(typeof p == "object"){
27372                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27373             }else if(typeof p == 'string' && bp){
27374                 p += '&' + Roo.urlEncode(bp);
27375             }
27376         }else if(bp){
27377             p = Roo.urlEncode(bp);
27378         }
27379         return p;
27380     },
27381
27382     createCallback : function(){
27383         return {
27384             success: this.success,
27385             failure: this.failure,
27386             scope: this,
27387             timeout: (this.form.timeout*1000),
27388             upload: this.form.fileUpload ? this.success : undefined
27389         };
27390     }
27391 };
27392
27393 Roo.form.Action.Submit = function(form, options){
27394     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27395 };
27396
27397 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27398     type : 'submit',
27399
27400     haveProgress : false,
27401     uploadComplete : false,
27402     
27403     // uploadProgress indicator.
27404     uploadProgress : function()
27405     {
27406         if (!this.form.progressUrl) {
27407             return;
27408         }
27409         
27410         if (!this.haveProgress) {
27411             Roo.MessageBox.progress("Uploading", "Uploading");
27412         }
27413         if (this.uploadComplete) {
27414            Roo.MessageBox.hide();
27415            return;
27416         }
27417         
27418         this.haveProgress = true;
27419    
27420         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27421         
27422         var c = new Roo.data.Connection();
27423         c.request({
27424             url : this.form.progressUrl,
27425             params: {
27426                 id : uid
27427             },
27428             method: 'GET',
27429             success : function(req){
27430                //console.log(data);
27431                 var rdata = false;
27432                 var edata;
27433                 try  {
27434                    rdata = Roo.decode(req.responseText)
27435                 } catch (e) {
27436                     Roo.log("Invalid data from server..");
27437                     Roo.log(edata);
27438                     return;
27439                 }
27440                 if (!rdata || !rdata.success) {
27441                     Roo.log(rdata);
27442                     Roo.MessageBox.alert(Roo.encode(rdata));
27443                     return;
27444                 }
27445                 var data = rdata.data;
27446                 
27447                 if (this.uploadComplete) {
27448                    Roo.MessageBox.hide();
27449                    return;
27450                 }
27451                    
27452                 if (data){
27453                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27454                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27455                     );
27456                 }
27457                 this.uploadProgress.defer(2000,this);
27458             },
27459        
27460             failure: function(data) {
27461                 Roo.log('progress url failed ');
27462                 Roo.log(data);
27463             },
27464             scope : this
27465         });
27466            
27467     },
27468     
27469     
27470     run : function()
27471     {
27472         // run get Values on the form, so it syncs any secondary forms.
27473         this.form.getValues();
27474         
27475         var o = this.options;
27476         var method = this.getMethod();
27477         var isPost = method == 'POST';
27478         if(o.clientValidation === false || this.form.isValid()){
27479             
27480             if (this.form.progressUrl) {
27481                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27482                     (new Date() * 1) + '' + Math.random());
27483                     
27484             } 
27485             
27486             
27487             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27488                 form:this.form.el.dom,
27489                 url:this.getUrl(!isPost),
27490                 method: method,
27491                 params:isPost ? this.getParams() : null,
27492                 isUpload: this.form.fileUpload,
27493                 formData : this.form.formData
27494             }));
27495             
27496             this.uploadProgress();
27497
27498         }else if (o.clientValidation !== false){ // client validation failed
27499             this.failureType = Roo.form.Action.CLIENT_INVALID;
27500             this.form.afterAction(this, false);
27501         }
27502     },
27503
27504     success : function(response)
27505     {
27506         this.uploadComplete= true;
27507         if (this.haveProgress) {
27508             Roo.MessageBox.hide();
27509         }
27510         
27511         
27512         var result = this.processResponse(response);
27513         if(result === true || result.success){
27514             this.form.afterAction(this, true);
27515             return;
27516         }
27517         if(result.errors){
27518             this.form.markInvalid(result.errors);
27519             this.failureType = Roo.form.Action.SERVER_INVALID;
27520         }
27521         this.form.afterAction(this, false);
27522     },
27523     failure : function(response)
27524     {
27525         this.uploadComplete= true;
27526         if (this.haveProgress) {
27527             Roo.MessageBox.hide();
27528         }
27529         
27530         this.response = response;
27531         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27532         this.form.afterAction(this, false);
27533     },
27534     
27535     handleResponse : function(response){
27536         if(this.form.errorReader){
27537             var rs = this.form.errorReader.read(response);
27538             var errors = [];
27539             if(rs.records){
27540                 for(var i = 0, len = rs.records.length; i < len; i++) {
27541                     var r = rs.records[i];
27542                     errors[i] = r.data;
27543                 }
27544             }
27545             if(errors.length < 1){
27546                 errors = null;
27547             }
27548             return {
27549                 success : rs.success,
27550                 errors : errors
27551             };
27552         }
27553         var ret = false;
27554         try {
27555             ret = Roo.decode(response.responseText);
27556         } catch (e) {
27557             ret = {
27558                 success: false,
27559                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27560                 errors : []
27561             };
27562         }
27563         return ret;
27564         
27565     }
27566 });
27567
27568
27569 Roo.form.Action.Load = function(form, options){
27570     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27571     this.reader = this.form.reader;
27572 };
27573
27574 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27575     type : 'load',
27576
27577     run : function(){
27578         
27579         Roo.Ajax.request(Roo.apply(
27580                 this.createCallback(), {
27581                     method:this.getMethod(),
27582                     url:this.getUrl(false),
27583                     params:this.getParams()
27584         }));
27585     },
27586
27587     success : function(response){
27588         
27589         var result = this.processResponse(response);
27590         if(result === true || !result.success || !result.data){
27591             this.failureType = Roo.form.Action.LOAD_FAILURE;
27592             this.form.afterAction(this, false);
27593             return;
27594         }
27595         this.form.clearInvalid();
27596         this.form.setValues(result.data);
27597         this.form.afterAction(this, true);
27598     },
27599
27600     handleResponse : function(response){
27601         if(this.form.reader){
27602             var rs = this.form.reader.read(response);
27603             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27604             return {
27605                 success : rs.success,
27606                 data : data
27607             };
27608         }
27609         return Roo.decode(response.responseText);
27610     }
27611 });
27612
27613 Roo.form.Action.ACTION_TYPES = {
27614     'load' : Roo.form.Action.Load,
27615     'submit' : Roo.form.Action.Submit
27616 };/*
27617  * Based on:
27618  * Ext JS Library 1.1.1
27619  * Copyright(c) 2006-2007, Ext JS, LLC.
27620  *
27621  * Originally Released Under LGPL - original licence link has changed is not relivant.
27622  *
27623  * Fork - LGPL
27624  * <script type="text/javascript">
27625  */
27626  
27627 /**
27628  * @class Roo.form.Layout
27629  * @extends Roo.Component
27630  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27631  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27632  * @constructor
27633  * @param {Object} config Configuration options
27634  */
27635 Roo.form.Layout = function(config){
27636     var xitems = [];
27637     if (config.items) {
27638         xitems = config.items;
27639         delete config.items;
27640     }
27641     Roo.form.Layout.superclass.constructor.call(this, config);
27642     this.stack = [];
27643     Roo.each(xitems, this.addxtype, this);
27644      
27645 };
27646
27647 Roo.extend(Roo.form.Layout, Roo.Component, {
27648     /**
27649      * @cfg {String/Object} autoCreate
27650      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27651      */
27652     /**
27653      * @cfg {String/Object/Function} style
27654      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27655      * a function which returns such a specification.
27656      */
27657     /**
27658      * @cfg {String} labelAlign
27659      * Valid values are "left," "top" and "right" (defaults to "left")
27660      */
27661     /**
27662      * @cfg {Number} labelWidth
27663      * Fixed width in pixels of all field labels (defaults to undefined)
27664      */
27665     /**
27666      * @cfg {Boolean} clear
27667      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27668      */
27669     clear : true,
27670     /**
27671      * @cfg {String} labelSeparator
27672      * The separator to use after field labels (defaults to ':')
27673      */
27674     labelSeparator : ':',
27675     /**
27676      * @cfg {Boolean} hideLabels
27677      * True to suppress the display of field labels in this layout (defaults to false)
27678      */
27679     hideLabels : false,
27680
27681     // private
27682     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27683     
27684     isLayout : true,
27685     
27686     // private
27687     onRender : function(ct, position){
27688         if(this.el){ // from markup
27689             this.el = Roo.get(this.el);
27690         }else {  // generate
27691             var cfg = this.getAutoCreate();
27692             this.el = ct.createChild(cfg, position);
27693         }
27694         if(this.style){
27695             this.el.applyStyles(this.style);
27696         }
27697         if(this.labelAlign){
27698             this.el.addClass('x-form-label-'+this.labelAlign);
27699         }
27700         if(this.hideLabels){
27701             this.labelStyle = "display:none";
27702             this.elementStyle = "padding-left:0;";
27703         }else{
27704             if(typeof this.labelWidth == 'number'){
27705                 this.labelStyle = "width:"+this.labelWidth+"px;";
27706                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27707             }
27708             if(this.labelAlign == 'top'){
27709                 this.labelStyle = "width:auto;";
27710                 this.elementStyle = "padding-left:0;";
27711             }
27712         }
27713         var stack = this.stack;
27714         var slen = stack.length;
27715         if(slen > 0){
27716             if(!this.fieldTpl){
27717                 var t = new Roo.Template(
27718                     '<div class="x-form-item {5}">',
27719                         '<label for="{0}" style="{2}">{1}{4}</label>',
27720                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27721                         '</div>',
27722                     '</div><div class="x-form-clear-left"></div>'
27723                 );
27724                 t.disableFormats = true;
27725                 t.compile();
27726                 Roo.form.Layout.prototype.fieldTpl = t;
27727             }
27728             for(var i = 0; i < slen; i++) {
27729                 if(stack[i].isFormField){
27730                     this.renderField(stack[i]);
27731                 }else{
27732                     this.renderComponent(stack[i]);
27733                 }
27734             }
27735         }
27736         if(this.clear){
27737             this.el.createChild({cls:'x-form-clear'});
27738         }
27739     },
27740
27741     // private
27742     renderField : function(f){
27743         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27744                f.id, //0
27745                f.fieldLabel, //1
27746                f.labelStyle||this.labelStyle||'', //2
27747                this.elementStyle||'', //3
27748                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27749                f.itemCls||this.itemCls||''  //5
27750        ], true).getPrevSibling());
27751     },
27752
27753     // private
27754     renderComponent : function(c){
27755         c.render(c.isLayout ? this.el : this.el.createChild());    
27756     },
27757     /**
27758      * Adds a object form elements (using the xtype property as the factory method.)
27759      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27760      * @param {Object} config 
27761      */
27762     addxtype : function(o)
27763     {
27764         // create the lement.
27765         o.form = this.form;
27766         var fe = Roo.factory(o, Roo.form);
27767         this.form.allItems.push(fe);
27768         this.stack.push(fe);
27769         
27770         if (fe.isFormField) {
27771             this.form.items.add(fe);
27772         }
27773          
27774         return fe;
27775     }
27776 });
27777
27778 /**
27779  * @class Roo.form.Column
27780  * @extends Roo.form.Layout
27781  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27782  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27783  * @constructor
27784  * @param {Object} config Configuration options
27785  */
27786 Roo.form.Column = function(config){
27787     Roo.form.Column.superclass.constructor.call(this, config);
27788 };
27789
27790 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27791     /**
27792      * @cfg {Number/String} width
27793      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27794      */
27795     /**
27796      * @cfg {String/Object} autoCreate
27797      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27798      */
27799
27800     // private
27801     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27802
27803     // private
27804     onRender : function(ct, position){
27805         Roo.form.Column.superclass.onRender.call(this, ct, position);
27806         if(this.width){
27807             this.el.setWidth(this.width);
27808         }
27809     }
27810 });
27811
27812
27813 /**
27814  * @class Roo.form.Row
27815  * @extends Roo.form.Layout
27816  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27817  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27818  * @constructor
27819  * @param {Object} config Configuration options
27820  */
27821
27822  
27823 Roo.form.Row = function(config){
27824     Roo.form.Row.superclass.constructor.call(this, config);
27825 };
27826  
27827 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27828       /**
27829      * @cfg {Number/String} width
27830      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27831      */
27832     /**
27833      * @cfg {Number/String} height
27834      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27835      */
27836     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27837     
27838     padWidth : 20,
27839     // private
27840     onRender : function(ct, position){
27841         //console.log('row render');
27842         if(!this.rowTpl){
27843             var t = new Roo.Template(
27844                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27845                     '<label for="{0}" style="{2}">{1}{4}</label>',
27846                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27847                     '</div>',
27848                 '</div>'
27849             );
27850             t.disableFormats = true;
27851             t.compile();
27852             Roo.form.Layout.prototype.rowTpl = t;
27853         }
27854         this.fieldTpl = this.rowTpl;
27855         
27856         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27857         var labelWidth = 100;
27858         
27859         if ((this.labelAlign != 'top')) {
27860             if (typeof this.labelWidth == 'number') {
27861                 labelWidth = this.labelWidth
27862             }
27863             this.padWidth =  20 + labelWidth;
27864             
27865         }
27866         
27867         Roo.form.Column.superclass.onRender.call(this, ct, position);
27868         if(this.width){
27869             this.el.setWidth(this.width);
27870         }
27871         if(this.height){
27872             this.el.setHeight(this.height);
27873         }
27874     },
27875     
27876     // private
27877     renderField : function(f){
27878         f.fieldEl = this.fieldTpl.append(this.el, [
27879                f.id, f.fieldLabel,
27880                f.labelStyle||this.labelStyle||'',
27881                this.elementStyle||'',
27882                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27883                f.itemCls||this.itemCls||'',
27884                f.width ? f.width + this.padWidth : 160 + this.padWidth
27885        ],true);
27886     }
27887 });
27888  
27889
27890 /**
27891  * @class Roo.form.FieldSet
27892  * @extends Roo.form.Layout
27893  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27894  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27895  * @constructor
27896  * @param {Object} config Configuration options
27897  */
27898 Roo.form.FieldSet = function(config){
27899     Roo.form.FieldSet.superclass.constructor.call(this, config);
27900 };
27901
27902 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27903     /**
27904      * @cfg {String} legend
27905      * The text to display as the legend for the FieldSet (defaults to '')
27906      */
27907     /**
27908      * @cfg {String/Object} autoCreate
27909      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27910      */
27911
27912     // private
27913     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27914
27915     // private
27916     onRender : function(ct, position){
27917         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27918         if(this.legend){
27919             this.setLegend(this.legend);
27920         }
27921     },
27922
27923     // private
27924     setLegend : function(text){
27925         if(this.rendered){
27926             this.el.child('legend').update(text);
27927         }
27928     }
27929 });/*
27930  * Based on:
27931  * Ext JS Library 1.1.1
27932  * Copyright(c) 2006-2007, Ext JS, LLC.
27933  *
27934  * Originally Released Under LGPL - original licence link has changed is not relivant.
27935  *
27936  * Fork - LGPL
27937  * <script type="text/javascript">
27938  */
27939 /**
27940  * @class Roo.form.VTypes
27941  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27942  * @static
27943  */
27944 Roo.form.VTypes = function(){
27945     // closure these in so they are only created once.
27946     var alpha = /^[a-zA-Z_]+$/;
27947     var alphanum = /^[a-zA-Z0-9_]+$/;
27948     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
27949     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27950
27951     // All these messages and functions are configurable
27952     return {
27953         /**
27954          * The function used to validate email addresses
27955          * @param {String} value The email address
27956          */
27957         'email' : function(v){
27958             return email.test(v);
27959         },
27960         /**
27961          * The error text to display when the email validation function returns false
27962          * @type String
27963          */
27964         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27965         /**
27966          * The keystroke filter mask to be applied on email input
27967          * @type RegExp
27968          */
27969         'emailMask' : /[a-z0-9_\.\-@]/i,
27970
27971         /**
27972          * The function used to validate URLs
27973          * @param {String} value The URL
27974          */
27975         'url' : function(v){
27976             return url.test(v);
27977         },
27978         /**
27979          * The error text to display when the url validation function returns false
27980          * @type String
27981          */
27982         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27983         
27984         /**
27985          * The function used to validate alpha values
27986          * @param {String} value The value
27987          */
27988         'alpha' : function(v){
27989             return alpha.test(v);
27990         },
27991         /**
27992          * The error text to display when the alpha validation function returns false
27993          * @type String
27994          */
27995         'alphaText' : 'This field should only contain letters and _',
27996         /**
27997          * The keystroke filter mask to be applied on alpha input
27998          * @type RegExp
27999          */
28000         'alphaMask' : /[a-z_]/i,
28001
28002         /**
28003          * The function used to validate alphanumeric values
28004          * @param {String} value The value
28005          */
28006         'alphanum' : function(v){
28007             return alphanum.test(v);
28008         },
28009         /**
28010          * The error text to display when the alphanumeric validation function returns false
28011          * @type String
28012          */
28013         'alphanumText' : 'This field should only contain letters, numbers and _',
28014         /**
28015          * The keystroke filter mask to be applied on alphanumeric input
28016          * @type RegExp
28017          */
28018         'alphanumMask' : /[a-z0-9_]/i
28019     };
28020 }();//<script type="text/javascript">
28021
28022 /**
28023  * @class Roo.form.FCKeditor
28024  * @extends Roo.form.TextArea
28025  * Wrapper around the FCKEditor http://www.fckeditor.net
28026  * @constructor
28027  * Creates a new FCKeditor
28028  * @param {Object} config Configuration options
28029  */
28030 Roo.form.FCKeditor = function(config){
28031     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28032     this.addEvents({
28033          /**
28034          * @event editorinit
28035          * Fired when the editor is initialized - you can add extra handlers here..
28036          * @param {FCKeditor} this
28037          * @param {Object} the FCK object.
28038          */
28039         editorinit : true
28040     });
28041     
28042     
28043 };
28044 Roo.form.FCKeditor.editors = { };
28045 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28046 {
28047     //defaultAutoCreate : {
28048     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28049     //},
28050     // private
28051     /**
28052      * @cfg {Object} fck options - see fck manual for details.
28053      */
28054     fckconfig : false,
28055     
28056     /**
28057      * @cfg {Object} fck toolbar set (Basic or Default)
28058      */
28059     toolbarSet : 'Basic',
28060     /**
28061      * @cfg {Object} fck BasePath
28062      */ 
28063     basePath : '/fckeditor/',
28064     
28065     
28066     frame : false,
28067     
28068     value : '',
28069     
28070    
28071     onRender : function(ct, position)
28072     {
28073         if(!this.el){
28074             this.defaultAutoCreate = {
28075                 tag: "textarea",
28076                 style:"width:300px;height:60px;",
28077                 autocomplete: "new-password"
28078             };
28079         }
28080         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28081         /*
28082         if(this.grow){
28083             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28084             if(this.preventScrollbars){
28085                 this.el.setStyle("overflow", "hidden");
28086             }
28087             this.el.setHeight(this.growMin);
28088         }
28089         */
28090         //console.log('onrender' + this.getId() );
28091         Roo.form.FCKeditor.editors[this.getId()] = this;
28092          
28093
28094         this.replaceTextarea() ;
28095         
28096     },
28097     
28098     getEditor : function() {
28099         return this.fckEditor;
28100     },
28101     /**
28102      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28103      * @param {Mixed} value The value to set
28104      */
28105     
28106     
28107     setValue : function(value)
28108     {
28109         //console.log('setValue: ' + value);
28110         
28111         if(typeof(value) == 'undefined') { // not sure why this is happending...
28112             return;
28113         }
28114         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28115         
28116         //if(!this.el || !this.getEditor()) {
28117         //    this.value = value;
28118             //this.setValue.defer(100,this,[value]);    
28119         //    return;
28120         //} 
28121         
28122         if(!this.getEditor()) {
28123             return;
28124         }
28125         
28126         this.getEditor().SetData(value);
28127         
28128         //
28129
28130     },
28131
28132     /**
28133      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28134      * @return {Mixed} value The field value
28135      */
28136     getValue : function()
28137     {
28138         
28139         if (this.frame && this.frame.dom.style.display == 'none') {
28140             return Roo.form.FCKeditor.superclass.getValue.call(this);
28141         }
28142         
28143         if(!this.el || !this.getEditor()) {
28144            
28145            // this.getValue.defer(100,this); 
28146             return this.value;
28147         }
28148        
28149         
28150         var value=this.getEditor().GetData();
28151         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28152         return Roo.form.FCKeditor.superclass.getValue.call(this);
28153         
28154
28155     },
28156
28157     /**
28158      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28159      * @return {Mixed} value The field value
28160      */
28161     getRawValue : function()
28162     {
28163         if (this.frame && this.frame.dom.style.display == 'none') {
28164             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28165         }
28166         
28167         if(!this.el || !this.getEditor()) {
28168             //this.getRawValue.defer(100,this); 
28169             return this.value;
28170             return;
28171         }
28172         
28173         
28174         
28175         var value=this.getEditor().GetData();
28176         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28177         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28178          
28179     },
28180     
28181     setSize : function(w,h) {
28182         
28183         
28184         
28185         //if (this.frame && this.frame.dom.style.display == 'none') {
28186         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28187         //    return;
28188         //}
28189         //if(!this.el || !this.getEditor()) {
28190         //    this.setSize.defer(100,this, [w,h]); 
28191         //    return;
28192         //}
28193         
28194         
28195         
28196         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28197         
28198         this.frame.dom.setAttribute('width', w);
28199         this.frame.dom.setAttribute('height', h);
28200         this.frame.setSize(w,h);
28201         
28202     },
28203     
28204     toggleSourceEdit : function(value) {
28205         
28206       
28207          
28208         this.el.dom.style.display = value ? '' : 'none';
28209         this.frame.dom.style.display = value ?  'none' : '';
28210         
28211     },
28212     
28213     
28214     focus: function(tag)
28215     {
28216         if (this.frame.dom.style.display == 'none') {
28217             return Roo.form.FCKeditor.superclass.focus.call(this);
28218         }
28219         if(!this.el || !this.getEditor()) {
28220             this.focus.defer(100,this, [tag]); 
28221             return;
28222         }
28223         
28224         
28225         
28226         
28227         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28228         this.getEditor().Focus();
28229         if (tgs.length) {
28230             if (!this.getEditor().Selection.GetSelection()) {
28231                 this.focus.defer(100,this, [tag]); 
28232                 return;
28233             }
28234             
28235             
28236             var r = this.getEditor().EditorDocument.createRange();
28237             r.setStart(tgs[0],0);
28238             r.setEnd(tgs[0],0);
28239             this.getEditor().Selection.GetSelection().removeAllRanges();
28240             this.getEditor().Selection.GetSelection().addRange(r);
28241             this.getEditor().Focus();
28242         }
28243         
28244     },
28245     
28246     
28247     
28248     replaceTextarea : function()
28249     {
28250         if ( document.getElementById( this.getId() + '___Frame' ) ) {
28251             return ;
28252         }
28253         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28254         //{
28255             // We must check the elements firstly using the Id and then the name.
28256         var oTextarea = document.getElementById( this.getId() );
28257         
28258         var colElementsByName = document.getElementsByName( this.getId() ) ;
28259          
28260         oTextarea.style.display = 'none' ;
28261
28262         if ( oTextarea.tabIndex ) {            
28263             this.TabIndex = oTextarea.tabIndex ;
28264         }
28265         
28266         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28267         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28268         this.frame = Roo.get(this.getId() + '___Frame')
28269     },
28270     
28271     _getConfigHtml : function()
28272     {
28273         var sConfig = '' ;
28274
28275         for ( var o in this.fckconfig ) {
28276             sConfig += sConfig.length > 0  ? '&amp;' : '';
28277             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28278         }
28279
28280         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28281     },
28282     
28283     
28284     _getIFrameHtml : function()
28285     {
28286         var sFile = 'fckeditor.html' ;
28287         /* no idea what this is about..
28288         try
28289         {
28290             if ( (/fcksource=true/i).test( window.top.location.search ) )
28291                 sFile = 'fckeditor.original.html' ;
28292         }
28293         catch (e) { 
28294         */
28295
28296         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28297         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28298         
28299         
28300         var html = '<iframe id="' + this.getId() +
28301             '___Frame" src="' + sLink +
28302             '" width="' + this.width +
28303             '" height="' + this.height + '"' +
28304             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28305             ' frameborder="0" scrolling="no"></iframe>' ;
28306
28307         return html ;
28308     },
28309     
28310     _insertHtmlBefore : function( html, element )
28311     {
28312         if ( element.insertAdjacentHTML )       {
28313             // IE
28314             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28315         } else { // Gecko
28316             var oRange = document.createRange() ;
28317             oRange.setStartBefore( element ) ;
28318             var oFragment = oRange.createContextualFragment( html );
28319             element.parentNode.insertBefore( oFragment, element ) ;
28320         }
28321     }
28322     
28323     
28324   
28325     
28326     
28327     
28328     
28329
28330 });
28331
28332 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28333
28334 function FCKeditor_OnComplete(editorInstance){
28335     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28336     f.fckEditor = editorInstance;
28337     //console.log("loaded");
28338     f.fireEvent('editorinit', f, editorInstance);
28339
28340   
28341
28342  
28343
28344
28345
28346
28347
28348
28349
28350
28351
28352
28353
28354
28355
28356
28357
28358 //<script type="text/javascript">
28359 /**
28360  * @class Roo.form.GridField
28361  * @extends Roo.form.Field
28362  * Embed a grid (or editable grid into a form)
28363  * STATUS ALPHA
28364  * 
28365  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28366  * it needs 
28367  * xgrid.store = Roo.data.Store
28368  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28369  * xgrid.store.reader = Roo.data.JsonReader 
28370  * 
28371  * 
28372  * @constructor
28373  * Creates a new GridField
28374  * @param {Object} config Configuration options
28375  */
28376 Roo.form.GridField = function(config){
28377     Roo.form.GridField.superclass.constructor.call(this, config);
28378      
28379 };
28380
28381 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28382     /**
28383      * @cfg {Number} width  - used to restrict width of grid..
28384      */
28385     width : 100,
28386     /**
28387      * @cfg {Number} height - used to restrict height of grid..
28388      */
28389     height : 50,
28390      /**
28391      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28392          * 
28393          *}
28394      */
28395     xgrid : false, 
28396     /**
28397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28398      * {tag: "input", type: "checkbox", autocomplete: "off"})
28399      */
28400    // defaultAutoCreate : { tag: 'div' },
28401     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
28402     /**
28403      * @cfg {String} addTitle Text to include for adding a title.
28404      */
28405     addTitle : false,
28406     //
28407     onResize : function(){
28408         Roo.form.Field.superclass.onResize.apply(this, arguments);
28409     },
28410
28411     initEvents : function(){
28412         // Roo.form.Checkbox.superclass.initEvents.call(this);
28413         // has no events...
28414        
28415     },
28416
28417
28418     getResizeEl : function(){
28419         return this.wrap;
28420     },
28421
28422     getPositionEl : function(){
28423         return this.wrap;
28424     },
28425
28426     // private
28427     onRender : function(ct, position){
28428         
28429         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28430         var style = this.style;
28431         delete this.style;
28432         
28433         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28434         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28435         this.viewEl = this.wrap.createChild({ tag: 'div' });
28436         if (style) {
28437             this.viewEl.applyStyles(style);
28438         }
28439         if (this.width) {
28440             this.viewEl.setWidth(this.width);
28441         }
28442         if (this.height) {
28443             this.viewEl.setHeight(this.height);
28444         }
28445         //if(this.inputValue !== undefined){
28446         //this.setValue(this.value);
28447         
28448         
28449         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28450         
28451         
28452         this.grid.render();
28453         this.grid.getDataSource().on('remove', this.refreshValue, this);
28454         this.grid.getDataSource().on('update', this.refreshValue, this);
28455         this.grid.on('afteredit', this.refreshValue, this);
28456  
28457     },
28458      
28459     
28460     /**
28461      * Sets the value of the item. 
28462      * @param {String} either an object  or a string..
28463      */
28464     setValue : function(v){
28465         //this.value = v;
28466         v = v || []; // empty set..
28467         // this does not seem smart - it really only affects memoryproxy grids..
28468         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28469             var ds = this.grid.getDataSource();
28470             // assumes a json reader..
28471             var data = {}
28472             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28473             ds.loadData( data);
28474         }
28475         // clear selection so it does not get stale.
28476         if (this.grid.sm) { 
28477             this.grid.sm.clearSelections();
28478         }
28479         
28480         Roo.form.GridField.superclass.setValue.call(this, v);
28481         this.refreshValue();
28482         // should load data in the grid really....
28483     },
28484     
28485     // private
28486     refreshValue: function() {
28487          var val = [];
28488         this.grid.getDataSource().each(function(r) {
28489             val.push(r.data);
28490         });
28491         this.el.dom.value = Roo.encode(val);
28492     }
28493     
28494      
28495     
28496     
28497 });/*
28498  * Based on:
28499  * Ext JS Library 1.1.1
28500  * Copyright(c) 2006-2007, Ext JS, LLC.
28501  *
28502  * Originally Released Under LGPL - original licence link has changed is not relivant.
28503  *
28504  * Fork - LGPL
28505  * <script type="text/javascript">
28506  */
28507 /**
28508  * @class Roo.form.DisplayField
28509  * @extends Roo.form.Field
28510  * A generic Field to display non-editable data.
28511  * @cfg {Boolean} closable (true|false) default false
28512  * @constructor
28513  * Creates a new Display Field item.
28514  * @param {Object} config Configuration options
28515  */
28516 Roo.form.DisplayField = function(config){
28517     Roo.form.DisplayField.superclass.constructor.call(this, config);
28518     
28519     this.addEvents({
28520         /**
28521          * @event close
28522          * Fires after the click the close btn
28523              * @param {Roo.form.DisplayField} this
28524              */
28525         close : true
28526     });
28527 };
28528
28529 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28530     inputType:      'hidden',
28531     allowBlank:     true,
28532     readOnly:         true,
28533     
28534  
28535     /**
28536      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28537      */
28538     focusClass : undefined,
28539     /**
28540      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28541      */
28542     fieldClass: 'x-form-field',
28543     
28544      /**
28545      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28546      */
28547     valueRenderer: undefined,
28548     
28549     width: 100,
28550     /**
28551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28552      * {tag: "input", type: "checkbox", autocomplete: "off"})
28553      */
28554      
28555  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28556  
28557     closable : false,
28558     
28559     onResize : function(){
28560         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28561         
28562     },
28563
28564     initEvents : function(){
28565         // Roo.form.Checkbox.superclass.initEvents.call(this);
28566         // has no events...
28567         
28568         if(this.closable){
28569             this.closeEl.on('click', this.onClose, this);
28570         }
28571        
28572     },
28573
28574
28575     getResizeEl : function(){
28576         return this.wrap;
28577     },
28578
28579     getPositionEl : function(){
28580         return this.wrap;
28581     },
28582
28583     // private
28584     onRender : function(ct, position){
28585         
28586         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28587         //if(this.inputValue !== undefined){
28588         this.wrap = this.el.wrap();
28589         
28590         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28591         
28592         if(this.closable){
28593             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
28594         }
28595         
28596         if (this.bodyStyle) {
28597             this.viewEl.applyStyles(this.bodyStyle);
28598         }
28599         //this.viewEl.setStyle('padding', '2px');
28600         
28601         this.setValue(this.value);
28602         
28603     },
28604 /*
28605     // private
28606     initValue : Roo.emptyFn,
28607
28608   */
28609
28610         // private
28611     onClick : function(){
28612         
28613     },
28614
28615     /**
28616      * Sets the checked state of the checkbox.
28617      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28618      */
28619     setValue : function(v){
28620         this.value = v;
28621         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28622         // this might be called before we have a dom element..
28623         if (!this.viewEl) {
28624             return;
28625         }
28626         this.viewEl.dom.innerHTML = html;
28627         Roo.form.DisplayField.superclass.setValue.call(this, v);
28628
28629     },
28630     
28631     onClose : function(e)
28632     {
28633         e.preventDefault();
28634         
28635         this.fireEvent('close', this);
28636     }
28637 });/*
28638  * 
28639  * Licence- LGPL
28640  * 
28641  */
28642
28643 /**
28644  * @class Roo.form.DayPicker
28645  * @extends Roo.form.Field
28646  * A Day picker show [M] [T] [W] ....
28647  * @constructor
28648  * Creates a new Day Picker
28649  * @param {Object} config Configuration options
28650  */
28651 Roo.form.DayPicker= function(config){
28652     Roo.form.DayPicker.superclass.constructor.call(this, config);
28653      
28654 };
28655
28656 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28657     /**
28658      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28659      */
28660     focusClass : undefined,
28661     /**
28662      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28663      */
28664     fieldClass: "x-form-field",
28665    
28666     /**
28667      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28668      * {tag: "input", type: "checkbox", autocomplete: "off"})
28669      */
28670     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
28671     
28672    
28673     actionMode : 'viewEl', 
28674     //
28675     // private
28676  
28677     inputType : 'hidden',
28678     
28679      
28680     inputElement: false, // real input element?
28681     basedOn: false, // ????
28682     
28683     isFormField: true, // not sure where this is needed!!!!
28684
28685     onResize : function(){
28686         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28687         if(!this.boxLabel){
28688             this.el.alignTo(this.wrap, 'c-c');
28689         }
28690     },
28691
28692     initEvents : function(){
28693         Roo.form.Checkbox.superclass.initEvents.call(this);
28694         this.el.on("click", this.onClick,  this);
28695         this.el.on("change", this.onClick,  this);
28696     },
28697
28698
28699     getResizeEl : function(){
28700         return this.wrap;
28701     },
28702
28703     getPositionEl : function(){
28704         return this.wrap;
28705     },
28706
28707     
28708     // private
28709     onRender : function(ct, position){
28710         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28711        
28712         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28713         
28714         var r1 = '<table><tr>';
28715         var r2 = '<tr class="x-form-daypick-icons">';
28716         for (var i=0; i < 7; i++) {
28717             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28718             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28719         }
28720         
28721         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28722         viewEl.select('img').on('click', this.onClick, this);
28723         this.viewEl = viewEl;   
28724         
28725         
28726         // this will not work on Chrome!!!
28727         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28728         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28729         
28730         
28731           
28732
28733     },
28734
28735     // private
28736     initValue : Roo.emptyFn,
28737
28738     /**
28739      * Returns the checked state of the checkbox.
28740      * @return {Boolean} True if checked, else false
28741      */
28742     getValue : function(){
28743         return this.el.dom.value;
28744         
28745     },
28746
28747         // private
28748     onClick : function(e){ 
28749         //this.setChecked(!this.checked);
28750         Roo.get(e.target).toggleClass('x-menu-item-checked');
28751         this.refreshValue();
28752         //if(this.el.dom.checked != this.checked){
28753         //    this.setValue(this.el.dom.checked);
28754        // }
28755     },
28756     
28757     // private
28758     refreshValue : function()
28759     {
28760         var val = '';
28761         this.viewEl.select('img',true).each(function(e,i,n)  {
28762             val += e.is(".x-menu-item-checked") ? String(n) : '';
28763         });
28764         this.setValue(val, true);
28765     },
28766
28767     /**
28768      * Sets the checked state of the checkbox.
28769      * On is always based on a string comparison between inputValue and the param.
28770      * @param {Boolean/String} value - the value to set 
28771      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28772      */
28773     setValue : function(v,suppressEvent){
28774         if (!this.el.dom) {
28775             return;
28776         }
28777         var old = this.el.dom.value ;
28778         this.el.dom.value = v;
28779         if (suppressEvent) {
28780             return ;
28781         }
28782          
28783         // update display..
28784         this.viewEl.select('img',true).each(function(e,i,n)  {
28785             
28786             var on = e.is(".x-menu-item-checked");
28787             var newv = v.indexOf(String(n)) > -1;
28788             if (on != newv) {
28789                 e.toggleClass('x-menu-item-checked');
28790             }
28791             
28792         });
28793         
28794         
28795         this.fireEvent('change', this, v, old);
28796         
28797         
28798     },
28799    
28800     // handle setting of hidden value by some other method!!?!?
28801     setFromHidden: function()
28802     {
28803         if(!this.el){
28804             return;
28805         }
28806         //console.log("SET FROM HIDDEN");
28807         //alert('setFrom hidden');
28808         this.setValue(this.el.dom.value);
28809     },
28810     
28811     onDestroy : function()
28812     {
28813         if(this.viewEl){
28814             Roo.get(this.viewEl).remove();
28815         }
28816          
28817         Roo.form.DayPicker.superclass.onDestroy.call(this);
28818     }
28819
28820 });/*
28821  * RooJS Library 1.1.1
28822  * Copyright(c) 2008-2011  Alan Knowles
28823  *
28824  * License - LGPL
28825  */
28826  
28827
28828 /**
28829  * @class Roo.form.ComboCheck
28830  * @extends Roo.form.ComboBox
28831  * A combobox for multiple select items.
28832  *
28833  * FIXME - could do with a reset button..
28834  * 
28835  * @constructor
28836  * Create a new ComboCheck
28837  * @param {Object} config Configuration options
28838  */
28839 Roo.form.ComboCheck = function(config){
28840     Roo.form.ComboCheck.superclass.constructor.call(this, config);
28841     // should verify some data...
28842     // like
28843     // hiddenName = required..
28844     // displayField = required
28845     // valudField == required
28846     var req= [ 'hiddenName', 'displayField', 'valueField' ];
28847     var _t = this;
28848     Roo.each(req, function(e) {
28849         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
28850             throw "Roo.form.ComboCheck : missing value for: " + e;
28851         }
28852     });
28853     
28854     
28855 };
28856
28857 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
28858      
28859      
28860     editable : false,
28861      
28862     selectedClass: 'x-menu-item-checked', 
28863     
28864     // private
28865     onRender : function(ct, position){
28866         var _t = this;
28867         
28868         
28869         
28870         if(!this.tpl){
28871             var cls = 'x-combo-list';
28872
28873             
28874             this.tpl =  new Roo.Template({
28875                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
28876                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
28877                    '<span>{' + this.displayField + '}</span>' +
28878                     '</div>' 
28879                 
28880             });
28881         }
28882  
28883         
28884         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
28885         this.view.singleSelect = false;
28886         this.view.multiSelect = true;
28887         this.view.toggleSelect = true;
28888         this.pageTb.add(new Roo.Toolbar.Fill(), {
28889             
28890             text: 'Done',
28891             handler: function()
28892             {
28893                 _t.collapse();
28894             }
28895         });
28896     },
28897     
28898     onViewOver : function(e, t){
28899         // do nothing...
28900         return;
28901         
28902     },
28903     
28904     onViewClick : function(doFocus,index){
28905         return;
28906         
28907     },
28908     select: function () {
28909         //Roo.log("SELECT CALLED");
28910     },
28911      
28912     selectByValue : function(xv, scrollIntoView){
28913         var ar = this.getValueArray();
28914         var sels = [];
28915         
28916         Roo.each(ar, function(v) {
28917             if(v === undefined || v === null){
28918                 return;
28919             }
28920             var r = this.findRecord(this.valueField, v);
28921             if(r){
28922                 sels.push(this.store.indexOf(r))
28923                 
28924             }
28925         },this);
28926         this.view.select(sels);
28927         return false;
28928     },
28929     
28930     
28931     
28932     onSelect : function(record, index){
28933        // Roo.log("onselect Called");
28934        // this is only called by the clear button now..
28935         this.view.clearSelections();
28936         this.setValue('[]');
28937         if (this.value != this.valueBefore) {
28938             this.fireEvent('change', this, this.value, this.valueBefore);
28939             this.valueBefore = this.value;
28940         }
28941     },
28942     getValueArray : function()
28943     {
28944         var ar = [] ;
28945         
28946         try {
28947             //Roo.log(this.value);
28948             if (typeof(this.value) == 'undefined') {
28949                 return [];
28950             }
28951             var ar = Roo.decode(this.value);
28952             return  ar instanceof Array ? ar : []; //?? valid?
28953             
28954         } catch(e) {
28955             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
28956             return [];
28957         }
28958          
28959     },
28960     expand : function ()
28961     {
28962         
28963         Roo.form.ComboCheck.superclass.expand.call(this);
28964         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
28965         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
28966         
28967
28968     },
28969     
28970     collapse : function(){
28971         Roo.form.ComboCheck.superclass.collapse.call(this);
28972         var sl = this.view.getSelectedIndexes();
28973         var st = this.store;
28974         var nv = [];
28975         var tv = [];
28976         var r;
28977         Roo.each(sl, function(i) {
28978             r = st.getAt(i);
28979             nv.push(r.get(this.valueField));
28980         },this);
28981         this.setValue(Roo.encode(nv));
28982         if (this.value != this.valueBefore) {
28983
28984             this.fireEvent('change', this, this.value, this.valueBefore);
28985             this.valueBefore = this.value;
28986         }
28987         
28988     },
28989     
28990     setValue : function(v){
28991         // Roo.log(v);
28992         this.value = v;
28993         
28994         var vals = this.getValueArray();
28995         var tv = [];
28996         Roo.each(vals, function(k) {
28997             var r = this.findRecord(this.valueField, k);
28998             if(r){
28999                 tv.push(r.data[this.displayField]);
29000             }else if(this.valueNotFoundText !== undefined){
29001                 tv.push( this.valueNotFoundText );
29002             }
29003         },this);
29004        // Roo.log(tv);
29005         
29006         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29007         this.hiddenField.value = v;
29008         this.value = v;
29009     }
29010     
29011 });/*
29012  * Based on:
29013  * Ext JS Library 1.1.1
29014  * Copyright(c) 2006-2007, Ext JS, LLC.
29015  *
29016  * Originally Released Under LGPL - original licence link has changed is not relivant.
29017  *
29018  * Fork - LGPL
29019  * <script type="text/javascript">
29020  */
29021  
29022 /**
29023  * @class Roo.form.Signature
29024  * @extends Roo.form.Field
29025  * Signature field.  
29026  * @constructor
29027  * 
29028  * @param {Object} config Configuration options
29029  */
29030
29031 Roo.form.Signature = function(config){
29032     Roo.form.Signature.superclass.constructor.call(this, config);
29033     
29034     this.addEvents({// not in used??
29035          /**
29036          * @event confirm
29037          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
29038              * @param {Roo.form.Signature} combo This combo box
29039              */
29040         'confirm' : true,
29041         /**
29042          * @event reset
29043          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
29044              * @param {Roo.form.ComboBox} combo This combo box
29045              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
29046              */
29047         'reset' : true
29048     });
29049 };
29050
29051 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
29052     /**
29053      * @cfg {Object} labels Label to use when rendering a form.
29054      * defaults to 
29055      * labels : { 
29056      *      clear : "Clear",
29057      *      confirm : "Confirm"
29058      *  }
29059      */
29060     labels : { 
29061         clear : "Clear",
29062         confirm : "Confirm"
29063     },
29064     /**
29065      * @cfg {Number} width The signature panel width (defaults to 300)
29066      */
29067     width: 300,
29068     /**
29069      * @cfg {Number} height The signature panel height (defaults to 100)
29070      */
29071     height : 100,
29072     /**
29073      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
29074      */
29075     allowBlank : false,
29076     
29077     //private
29078     // {Object} signPanel The signature SVG panel element (defaults to {})
29079     signPanel : {},
29080     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
29081     isMouseDown : false,
29082     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
29083     isConfirmed : false,
29084     // {String} signatureTmp SVG mapping string (defaults to empty string)
29085     signatureTmp : '',
29086     
29087     
29088     defaultAutoCreate : { // modified by initCompnoent..
29089         tag: "input",
29090         type:"hidden"
29091     },
29092
29093     // private
29094     onRender : function(ct, position){
29095         
29096         Roo.form.Signature.superclass.onRender.call(this, ct, position);
29097         
29098         this.wrap = this.el.wrap({
29099             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
29100         });
29101         
29102         this.createToolbar(this);
29103         this.signPanel = this.wrap.createChild({
29104                 tag: 'div',
29105                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
29106             }, this.el
29107         );
29108             
29109         this.svgID = Roo.id();
29110         this.svgEl = this.signPanel.createChild({
29111               xmlns : 'http://www.w3.org/2000/svg',
29112               tag : 'svg',
29113               id : this.svgID + "-svg",
29114               width: this.width,
29115               height: this.height,
29116               viewBox: '0 0 '+this.width+' '+this.height,
29117               cn : [
29118                 {
29119                     tag: "rect",
29120                     id: this.svgID + "-svg-r",
29121                     width: this.width,
29122                     height: this.height,
29123                     fill: "#ffa"
29124                 },
29125                 {
29126                     tag: "line",
29127                     id: this.svgID + "-svg-l",
29128                     x1: "0", // start
29129                     y1: (this.height*0.8), // start set the line in 80% of height
29130                     x2: this.width, // end
29131                     y2: (this.height*0.8), // end set the line in 80% of height
29132                     'stroke': "#666",
29133                     'stroke-width': "1",
29134                     'stroke-dasharray': "3",
29135                     'shape-rendering': "crispEdges",
29136                     'pointer-events': "none"
29137                 },
29138                 {
29139                     tag: "path",
29140                     id: this.svgID + "-svg-p",
29141                     'stroke': "navy",
29142                     'stroke-width': "3",
29143                     'fill': "none",
29144                     'pointer-events': 'none'
29145                 }
29146               ]
29147         });
29148         this.createSVG();
29149         this.svgBox = this.svgEl.dom.getScreenCTM();
29150     },
29151     createSVG : function(){ 
29152         var svg = this.signPanel;
29153         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
29154         var t = this;
29155
29156         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
29157         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
29158         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
29159         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
29160         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
29161         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
29162         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
29163         
29164     },
29165     isTouchEvent : function(e){
29166         return e.type.match(/^touch/);
29167     },
29168     getCoords : function (e) {
29169         var pt    = this.svgEl.dom.createSVGPoint();
29170         pt.x = e.clientX; 
29171         pt.y = e.clientY;
29172         if (this.isTouchEvent(e)) {
29173             pt.x =  e.targetTouches[0].clientX;
29174             pt.y = e.targetTouches[0].clientY;
29175         }
29176         var a = this.svgEl.dom.getScreenCTM();
29177         var b = a.inverse();
29178         var mx = pt.matrixTransform(b);
29179         return mx.x + ',' + mx.y;
29180     },
29181     //mouse event headler 
29182     down : function (e) {
29183         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
29184         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
29185         
29186         this.isMouseDown = true;
29187         
29188         e.preventDefault();
29189     },
29190     move : function (e) {
29191         if (this.isMouseDown) {
29192             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
29193             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
29194         }
29195         
29196         e.preventDefault();
29197     },
29198     up : function (e) {
29199         this.isMouseDown = false;
29200         var sp = this.signatureTmp.split(' ');
29201         
29202         if(sp.length > 1){
29203             if(!sp[sp.length-2].match(/^L/)){
29204                 sp.pop();
29205                 sp.pop();
29206                 sp.push("");
29207                 this.signatureTmp = sp.join(" ");
29208             }
29209         }
29210         if(this.getValue() != this.signatureTmp){
29211             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29212             this.isConfirmed = false;
29213         }
29214         e.preventDefault();
29215     },
29216     
29217     /**
29218      * Protected method that will not generally be called directly. It
29219      * is called when the editor creates its toolbar. Override this method if you need to
29220      * add custom toolbar buttons.
29221      * @param {HtmlEditor} editor
29222      */
29223     createToolbar : function(editor){
29224          function btn(id, toggle, handler){
29225             var xid = fid + '-'+ id ;
29226             return {
29227                 id : xid,
29228                 cmd : id,
29229                 cls : 'x-btn-icon x-edit-'+id,
29230                 enableToggle:toggle !== false,
29231                 scope: editor, // was editor...
29232                 handler:handler||editor.relayBtnCmd,
29233                 clickEvent:'mousedown',
29234                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
29235                 tabIndex:-1
29236             };
29237         }
29238         
29239         
29240         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
29241         this.tb = tb;
29242         this.tb.add(
29243            {
29244                 cls : ' x-signature-btn x-signature-'+id,
29245                 scope: editor, // was editor...
29246                 handler: this.reset,
29247                 clickEvent:'mousedown',
29248                 text: this.labels.clear
29249             },
29250             {
29251                  xtype : 'Fill',
29252                  xns: Roo.Toolbar
29253             }, 
29254             {
29255                 cls : '  x-signature-btn x-signature-'+id,
29256                 scope: editor, // was editor...
29257                 handler: this.confirmHandler,
29258                 clickEvent:'mousedown',
29259                 text: this.labels.confirm
29260             }
29261         );
29262     
29263     },
29264     //public
29265     /**
29266      * when user is clicked confirm then show this image.....
29267      * 
29268      * @return {String} Image Data URI
29269      */
29270     getImageDataURI : function(){
29271         var svg = this.svgEl.dom.parentNode.innerHTML;
29272         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
29273         return src; 
29274     },
29275     /**
29276      * 
29277      * @return {Boolean} this.isConfirmed
29278      */
29279     getConfirmed : function(){
29280         return this.isConfirmed;
29281     },
29282     /**
29283      * 
29284      * @return {Number} this.width
29285      */
29286     getWidth : function(){
29287         return this.width;
29288     },
29289     /**
29290      * 
29291      * @return {Number} this.height
29292      */
29293     getHeight : function(){
29294         return this.height;
29295     },
29296     // private
29297     getSignature : function(){
29298         return this.signatureTmp;
29299     },
29300     // private
29301     reset : function(){
29302         this.signatureTmp = '';
29303         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29304         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
29305         this.isConfirmed = false;
29306         Roo.form.Signature.superclass.reset.call(this);
29307     },
29308     setSignature : function(s){
29309         this.signatureTmp = s;
29310         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29311         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
29312         this.setValue(s);
29313         this.isConfirmed = false;
29314         Roo.form.Signature.superclass.reset.call(this);
29315     }, 
29316     test : function(){
29317 //        Roo.log(this.signPanel.dom.contentWindow.up())
29318     },
29319     //private
29320     setConfirmed : function(){
29321         
29322         
29323         
29324 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
29325     },
29326     // private
29327     confirmHandler : function(){
29328         if(!this.getSignature()){
29329             return;
29330         }
29331         
29332         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
29333         this.setValue(this.getSignature());
29334         this.isConfirmed = true;
29335         
29336         this.fireEvent('confirm', this);
29337     },
29338     // private
29339     // Subclasses should provide the validation implementation by overriding this
29340     validateValue : function(value){
29341         if(this.allowBlank){
29342             return true;
29343         }
29344         
29345         if(this.isConfirmed){
29346             return true;
29347         }
29348         return false;
29349     }
29350 });/*
29351  * Based on:
29352  * Ext JS Library 1.1.1
29353  * Copyright(c) 2006-2007, Ext JS, LLC.
29354  *
29355  * Originally Released Under LGPL - original licence link has changed is not relivant.
29356  *
29357  * Fork - LGPL
29358  * <script type="text/javascript">
29359  */
29360  
29361
29362 /**
29363  * @class Roo.form.ComboBox
29364  * @extends Roo.form.TriggerField
29365  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
29366  * @constructor
29367  * Create a new ComboBox.
29368  * @param {Object} config Configuration options
29369  */
29370 Roo.form.Select = function(config){
29371     Roo.form.Select.superclass.constructor.call(this, config);
29372      
29373 };
29374
29375 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
29376     /**
29377      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
29378      */
29379     /**
29380      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
29381      * rendering into an Roo.Editor, defaults to false)
29382      */
29383     /**
29384      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
29385      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
29386      */
29387     /**
29388      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
29389      */
29390     /**
29391      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
29392      * the dropdown list (defaults to undefined, with no header element)
29393      */
29394
29395      /**
29396      * @cfg {String/Roo.Template} tpl The template to use to render the output
29397      */
29398      
29399     // private
29400     defaultAutoCreate : {tag: "select"  },
29401     /**
29402      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
29403      */
29404     listWidth: undefined,
29405     /**
29406      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
29407      * mode = 'remote' or 'text' if mode = 'local')
29408      */
29409     displayField: undefined,
29410     /**
29411      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
29412      * mode = 'remote' or 'value' if mode = 'local'). 
29413      * Note: use of a valueField requires the user make a selection
29414      * in order for a value to be mapped.
29415      */
29416     valueField: undefined,
29417     
29418     
29419     /**
29420      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
29421      * field's data value (defaults to the underlying DOM element's name)
29422      */
29423     hiddenName: undefined,
29424     /**
29425      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
29426      */
29427     listClass: '',
29428     /**
29429      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
29430      */
29431     selectedClass: 'x-combo-selected',
29432     /**
29433      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
29434      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
29435      * which displays a downward arrow icon).
29436      */
29437     triggerClass : 'x-form-arrow-trigger',
29438     /**
29439      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29440      */
29441     shadow:'sides',
29442     /**
29443      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
29444      * anchor positions (defaults to 'tl-bl')
29445      */
29446     listAlign: 'tl-bl?',
29447     /**
29448      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
29449      */
29450     maxHeight: 300,
29451     /**
29452      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
29453      * query specified by the allQuery config option (defaults to 'query')
29454      */
29455     triggerAction: 'query',
29456     /**
29457      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
29458      * (defaults to 4, does not apply if editable = false)
29459      */
29460     minChars : 4,
29461     /**
29462      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
29463      * delay (typeAheadDelay) if it matches a known value (defaults to false)
29464      */
29465     typeAhead: false,
29466     /**
29467      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
29468      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
29469      */
29470     queryDelay: 500,
29471     /**
29472      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
29473      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
29474      */
29475     pageSize: 0,
29476     /**
29477      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
29478      * when editable = true (defaults to false)
29479      */
29480     selectOnFocus:false,
29481     /**
29482      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
29483      */
29484     queryParam: 'query',
29485     /**
29486      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
29487      * when mode = 'remote' (defaults to 'Loading...')
29488      */
29489     loadingText: 'Loading...',
29490     /**
29491      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
29492      */
29493     resizable: false,
29494     /**
29495      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
29496      */
29497     handleHeight : 8,
29498     /**
29499      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
29500      * traditional select (defaults to true)
29501      */
29502     editable: true,
29503     /**
29504      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
29505      */
29506     allQuery: '',
29507     /**
29508      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
29509      */
29510     mode: 'remote',
29511     /**
29512      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
29513      * listWidth has a higher value)
29514      */
29515     minListWidth : 70,
29516     /**
29517      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
29518      * allow the user to set arbitrary text into the field (defaults to false)
29519      */
29520     forceSelection:false,
29521     /**
29522      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
29523      * if typeAhead = true (defaults to 250)
29524      */
29525     typeAheadDelay : 250,
29526     /**
29527      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
29528      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
29529      */
29530     valueNotFoundText : undefined,
29531     
29532     /**
29533      * @cfg {String} defaultValue The value displayed after loading the store.
29534      */
29535     defaultValue: '',
29536     
29537     /**
29538      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
29539      */
29540     blockFocus : false,
29541     
29542     /**
29543      * @cfg {Boolean} disableClear Disable showing of clear button.
29544      */
29545     disableClear : false,
29546     /**
29547      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
29548      */
29549     alwaysQuery : false,
29550     
29551     //private
29552     addicon : false,
29553     editicon: false,
29554     
29555     // element that contains real text value.. (when hidden is used..)
29556      
29557     // private
29558     onRender : function(ct, position){
29559         Roo.form.Field.prototype.onRender.call(this, ct, position);
29560         
29561         if(this.store){
29562             this.store.on('beforeload', this.onBeforeLoad, this);
29563             this.store.on('load', this.onLoad, this);
29564             this.store.on('loadexception', this.onLoadException, this);
29565             this.store.load({});
29566         }
29567         
29568         
29569         
29570     },
29571
29572     // private
29573     initEvents : function(){
29574         //Roo.form.ComboBox.superclass.initEvents.call(this);
29575  
29576     },
29577
29578     onDestroy : function(){
29579        
29580         if(this.store){
29581             this.store.un('beforeload', this.onBeforeLoad, this);
29582             this.store.un('load', this.onLoad, this);
29583             this.store.un('loadexception', this.onLoadException, this);
29584         }
29585         //Roo.form.ComboBox.superclass.onDestroy.call(this);
29586     },
29587
29588     // private
29589     fireKey : function(e){
29590         if(e.isNavKeyPress() && !this.list.isVisible()){
29591             this.fireEvent("specialkey", this, e);
29592         }
29593     },
29594
29595     // private
29596     onResize: function(w, h){
29597         
29598         return; 
29599     
29600         
29601     },
29602
29603     /**
29604      * Allow or prevent the user from directly editing the field text.  If false is passed,
29605      * the user will only be able to select from the items defined in the dropdown list.  This method
29606      * is the runtime equivalent of setting the 'editable' config option at config time.
29607      * @param {Boolean} value True to allow the user to directly edit the field text
29608      */
29609     setEditable : function(value){
29610          
29611     },
29612
29613     // private
29614     onBeforeLoad : function(){
29615         
29616         Roo.log("Select before load");
29617         return;
29618     
29619         this.innerList.update(this.loadingText ?
29620                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
29621         //this.restrictHeight();
29622         this.selectedIndex = -1;
29623     },
29624
29625     // private
29626     onLoad : function(){
29627
29628     
29629         var dom = this.el.dom;
29630         dom.innerHTML = '';
29631          var od = dom.ownerDocument;
29632          
29633         if (this.emptyText) {
29634             var op = od.createElement('option');
29635             op.setAttribute('value', '');
29636             op.innerHTML = String.format('{0}', this.emptyText);
29637             dom.appendChild(op);
29638         }
29639         if(this.store.getCount() > 0){
29640            
29641             var vf = this.valueField;
29642             var df = this.displayField;
29643             this.store.data.each(function(r) {
29644                 // which colmsn to use... testing - cdoe / title..
29645                 var op = od.createElement('option');
29646                 op.setAttribute('value', r.data[vf]);
29647                 op.innerHTML = String.format('{0}', r.data[df]);
29648                 dom.appendChild(op);
29649             });
29650             if (typeof(this.defaultValue != 'undefined')) {
29651                 this.setValue(this.defaultValue);
29652             }
29653             
29654              
29655         }else{
29656             //this.onEmptyResults();
29657         }
29658         //this.el.focus();
29659     },
29660     // private
29661     onLoadException : function()
29662     {
29663         dom.innerHTML = '';
29664             
29665         Roo.log("Select on load exception");
29666         return;
29667     
29668         this.collapse();
29669         Roo.log(this.store.reader.jsonData);
29670         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
29671             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
29672         }
29673         
29674         
29675     },
29676     // private
29677     onTypeAhead : function(){
29678          
29679     },
29680
29681     // private
29682     onSelect : function(record, index){
29683         Roo.log('on select?');
29684         return;
29685         if(this.fireEvent('beforeselect', this, record, index) !== false){
29686             this.setFromData(index > -1 ? record.data : false);
29687             this.collapse();
29688             this.fireEvent('select', this, record, index);
29689         }
29690     },
29691
29692     /**
29693      * Returns the currently selected field value or empty string if no value is set.
29694      * @return {String} value The selected value
29695      */
29696     getValue : function(){
29697         var dom = this.el.dom;
29698         this.value = dom.options[dom.selectedIndex].value;
29699         return this.value;
29700         
29701     },
29702
29703     /**
29704      * Clears any text/value currently set in the field
29705      */
29706     clearValue : function(){
29707         this.value = '';
29708         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
29709         
29710     },
29711
29712     /**
29713      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
29714      * will be displayed in the field.  If the value does not match the data value of an existing item,
29715      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
29716      * Otherwise the field will be blank (although the value will still be set).
29717      * @param {String} value The value to match
29718      */
29719     setValue : function(v){
29720         var d = this.el.dom;
29721         for (var i =0; i < d.options.length;i++) {
29722             if (v == d.options[i].value) {
29723                 d.selectedIndex = i;
29724                 this.value = v;
29725                 return;
29726             }
29727         }
29728         this.clearValue();
29729     },
29730     /**
29731      * @property {Object} the last set data for the element
29732      */
29733     
29734     lastData : false,
29735     /**
29736      * Sets the value of the field based on a object which is related to the record format for the store.
29737      * @param {Object} value the value to set as. or false on reset?
29738      */
29739     setFromData : function(o){
29740         Roo.log('setfrom data?');
29741          
29742         
29743         
29744     },
29745     // private
29746     reset : function(){
29747         this.clearValue();
29748     },
29749     // private
29750     findRecord : function(prop, value){
29751         
29752         return false;
29753     
29754         var record;
29755         if(this.store.getCount() > 0){
29756             this.store.each(function(r){
29757                 if(r.data[prop] == value){
29758                     record = r;
29759                     return false;
29760                 }
29761                 return true;
29762             });
29763         }
29764         return record;
29765     },
29766     
29767     getName: function()
29768     {
29769         // returns hidden if it's set..
29770         if (!this.rendered) {return ''};
29771         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
29772         
29773     },
29774      
29775
29776     
29777
29778     // private
29779     onEmptyResults : function(){
29780         Roo.log('empty results');
29781         //this.collapse();
29782     },
29783
29784     /**
29785      * Returns true if the dropdown list is expanded, else false.
29786      */
29787     isExpanded : function(){
29788         return false;
29789     },
29790
29791     /**
29792      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
29793      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29794      * @param {String} value The data value of the item to select
29795      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29796      * selected item if it is not currently in view (defaults to true)
29797      * @return {Boolean} True if the value matched an item in the list, else false
29798      */
29799     selectByValue : function(v, scrollIntoView){
29800         Roo.log('select By Value');
29801         return false;
29802     
29803         if(v !== undefined && v !== null){
29804             var r = this.findRecord(this.valueField || this.displayField, v);
29805             if(r){
29806                 this.select(this.store.indexOf(r), scrollIntoView);
29807                 return true;
29808             }
29809         }
29810         return false;
29811     },
29812
29813     /**
29814      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
29815      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29816      * @param {Number} index The zero-based index of the list item to select
29817      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29818      * selected item if it is not currently in view (defaults to true)
29819      */
29820     select : function(index, scrollIntoView){
29821         Roo.log('select ');
29822         return  ;
29823         
29824         this.selectedIndex = index;
29825         this.view.select(index);
29826         if(scrollIntoView !== false){
29827             var el = this.view.getNode(index);
29828             if(el){
29829                 this.innerList.scrollChildIntoView(el, false);
29830             }
29831         }
29832     },
29833
29834       
29835
29836     // private
29837     validateBlur : function(){
29838         
29839         return;
29840         
29841     },
29842
29843     // private
29844     initQuery : function(){
29845         this.doQuery(this.getRawValue());
29846     },
29847
29848     // private
29849     doForce : function(){
29850         if(this.el.dom.value.length > 0){
29851             this.el.dom.value =
29852                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
29853              
29854         }
29855     },
29856
29857     /**
29858      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
29859      * query allowing the query action to be canceled if needed.
29860      * @param {String} query The SQL query to execute
29861      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
29862      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
29863      * saved in the current store (defaults to false)
29864      */
29865     doQuery : function(q, forceAll){
29866         
29867         Roo.log('doQuery?');
29868         if(q === undefined || q === null){
29869             q = '';
29870         }
29871         var qe = {
29872             query: q,
29873             forceAll: forceAll,
29874             combo: this,
29875             cancel:false
29876         };
29877         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
29878             return false;
29879         }
29880         q = qe.query;
29881         forceAll = qe.forceAll;
29882         if(forceAll === true || (q.length >= this.minChars)){
29883             if(this.lastQuery != q || this.alwaysQuery){
29884                 this.lastQuery = q;
29885                 if(this.mode == 'local'){
29886                     this.selectedIndex = -1;
29887                     if(forceAll){
29888                         this.store.clearFilter();
29889                     }else{
29890                         this.store.filter(this.displayField, q);
29891                     }
29892                     this.onLoad();
29893                 }else{
29894                     this.store.baseParams[this.queryParam] = q;
29895                     this.store.load({
29896                         params: this.getParams(q)
29897                     });
29898                     this.expand();
29899                 }
29900             }else{
29901                 this.selectedIndex = -1;
29902                 this.onLoad();   
29903             }
29904         }
29905     },
29906
29907     // private
29908     getParams : function(q){
29909         var p = {};
29910         //p[this.queryParam] = q;
29911         if(this.pageSize){
29912             p.start = 0;
29913             p.limit = this.pageSize;
29914         }
29915         return p;
29916     },
29917
29918     /**
29919      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
29920      */
29921     collapse : function(){
29922         
29923     },
29924
29925     // private
29926     collapseIf : function(e){
29927         
29928     },
29929
29930     /**
29931      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
29932      */
29933     expand : function(){
29934         
29935     } ,
29936
29937     // private
29938      
29939
29940     /** 
29941     * @cfg {Boolean} grow 
29942     * @hide 
29943     */
29944     /** 
29945     * @cfg {Number} growMin 
29946     * @hide 
29947     */
29948     /** 
29949     * @cfg {Number} growMax 
29950     * @hide 
29951     */
29952     /**
29953      * @hide
29954      * @method autoSize
29955      */
29956     
29957     setWidth : function()
29958     {
29959         
29960     },
29961     getResizeEl : function(){
29962         return this.el;
29963     }
29964 });//<script type="text/javasscript">
29965  
29966
29967 /**
29968  * @class Roo.DDView
29969  * A DnD enabled version of Roo.View.
29970  * @param {Element/String} container The Element in which to create the View.
29971  * @param {String} tpl The template string used to create the markup for each element of the View
29972  * @param {Object} config The configuration properties. These include all the config options of
29973  * {@link Roo.View} plus some specific to this class.<br>
29974  * <p>
29975  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29976  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29977  * <p>
29978  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29979 .x-view-drag-insert-above {
29980         border-top:1px dotted #3366cc;
29981 }
29982 .x-view-drag-insert-below {
29983         border-bottom:1px dotted #3366cc;
29984 }
29985 </code></pre>
29986  * 
29987  */
29988  
29989 Roo.DDView = function(container, tpl, config) {
29990     Roo.DDView.superclass.constructor.apply(this, arguments);
29991     this.getEl().setStyle("outline", "0px none");
29992     this.getEl().unselectable();
29993     if (this.dragGroup) {
29994         this.setDraggable(this.dragGroup.split(","));
29995     }
29996     if (this.dropGroup) {
29997         this.setDroppable(this.dropGroup.split(","));
29998     }
29999     if (this.deletable) {
30000         this.setDeletable();
30001     }
30002     this.isDirtyFlag = false;
30003         this.addEvents({
30004                 "drop" : true
30005         });
30006 };
30007
30008 Roo.extend(Roo.DDView, Roo.View, {
30009 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30010 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30011 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30012 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30013
30014         isFormField: true,
30015
30016         reset: Roo.emptyFn,
30017         
30018         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30019
30020         validate: function() {
30021                 return true;
30022         },
30023         
30024         destroy: function() {
30025                 this.purgeListeners();
30026                 this.getEl.removeAllListeners();
30027                 this.getEl().remove();
30028                 if (this.dragZone) {
30029                         if (this.dragZone.destroy) {
30030                                 this.dragZone.destroy();
30031                         }
30032                 }
30033                 if (this.dropZone) {
30034                         if (this.dropZone.destroy) {
30035                                 this.dropZone.destroy();
30036                         }
30037                 }
30038         },
30039
30040 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30041         getName: function() {
30042                 return this.name;
30043         },
30044
30045 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30046         setValue: function(v) {
30047                 if (!this.store) {
30048                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30049                 }
30050                 var data = {};
30051                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30052                 this.store.proxy = new Roo.data.MemoryProxy(data);
30053                 this.store.load();
30054         },
30055
30056 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30057         getValue: function() {
30058                 var result = '(';
30059                 this.store.each(function(rec) {
30060                         result += rec.id + ',';
30061                 });
30062                 return result.substr(0, result.length - 1) + ')';
30063         },
30064         
30065         getIds: function() {
30066                 var i = 0, result = new Array(this.store.getCount());
30067                 this.store.each(function(rec) {
30068                         result[i++] = rec.id;
30069                 });
30070                 return result;
30071         },
30072         
30073         isDirty: function() {
30074                 return this.isDirtyFlag;
30075         },
30076
30077 /**
30078  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30079  *      whole Element becomes the target, and this causes the drop gesture to append.
30080  */
30081     getTargetFromEvent : function(e) {
30082                 var target = e.getTarget();
30083                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30084                 target = target.parentNode;
30085                 }
30086                 if (!target) {
30087                         target = this.el.dom.lastChild || this.el.dom;
30088                 }
30089                 return target;
30090     },
30091
30092 /**
30093  *      Create the drag data which consists of an object which has the property "ddel" as
30094  *      the drag proxy element. 
30095  */
30096     getDragData : function(e) {
30097         var target = this.findItemFromChild(e.getTarget());
30098                 if(target) {
30099                         this.handleSelection(e);
30100                         var selNodes = this.getSelectedNodes();
30101             var dragData = {
30102                 source: this,
30103                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30104                 nodes: selNodes,
30105                 records: []
30106                         };
30107                         var selectedIndices = this.getSelectedIndexes();
30108                         for (var i = 0; i < selectedIndices.length; i++) {
30109                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30110                         }
30111                         if (selNodes.length == 1) {
30112                                 dragData.ddel = target.cloneNode(true); // the div element
30113                         } else {
30114                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30115                                 div.className = 'multi-proxy';
30116                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30117                                         div.appendChild(selNodes[i].cloneNode(true));
30118                                 }
30119                                 dragData.ddel = div;
30120                         }
30121             //console.log(dragData)
30122             //console.log(dragData.ddel.innerHTML)
30123                         return dragData;
30124                 }
30125         //console.log('nodragData')
30126                 return false;
30127     },
30128     
30129 /**     Specify to which ddGroup items in this DDView may be dragged. */
30130     setDraggable: function(ddGroup) {
30131         if (ddGroup instanceof Array) {
30132                 Roo.each(ddGroup, this.setDraggable, this);
30133                 return;
30134         }
30135         if (this.dragZone) {
30136                 this.dragZone.addToGroup(ddGroup);
30137         } else {
30138                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30139                                 containerScroll: true,
30140                                 ddGroup: ddGroup 
30141
30142                         });
30143 //                      Draggability implies selection. DragZone's mousedown selects the element.
30144                         if (!this.multiSelect) { this.singleSelect = true; }
30145
30146 //                      Wire the DragZone's handlers up to methods in *this*
30147                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30148                 }
30149     },
30150
30151 /**     Specify from which ddGroup this DDView accepts drops. */
30152     setDroppable: function(ddGroup) {
30153         if (ddGroup instanceof Array) {
30154                 Roo.each(ddGroup, this.setDroppable, this);
30155                 return;
30156         }
30157         if (this.dropZone) {
30158                 this.dropZone.addToGroup(ddGroup);
30159         } else {
30160                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30161                                 containerScroll: true,
30162                                 ddGroup: ddGroup
30163                         });
30164
30165 //                      Wire the DropZone's handlers up to methods in *this*
30166                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30167                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30168                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30169                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30170                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30171                 }
30172     },
30173
30174 /**     Decide whether to drop above or below a View node. */
30175     getDropPoint : function(e, n, dd){
30176         if (n == this.el.dom) { return "above"; }
30177                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30178                 var c = t + (b - t) / 2;
30179                 var y = Roo.lib.Event.getPageY(e);
30180                 if(y <= c) {
30181                         return "above";
30182                 }else{
30183                         return "below";
30184                 }
30185     },
30186
30187     onNodeEnter : function(n, dd, e, data){
30188                 return false;
30189     },
30190     
30191     onNodeOver : function(n, dd, e, data){
30192                 var pt = this.getDropPoint(e, n, dd);
30193                 // set the insert point style on the target node
30194                 var dragElClass = this.dropNotAllowed;
30195                 if (pt) {
30196                         var targetElClass;
30197                         if (pt == "above"){
30198                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30199                                 targetElClass = "x-view-drag-insert-above";
30200                         } else {
30201                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30202                                 targetElClass = "x-view-drag-insert-below";
30203                         }
30204                         if (this.lastInsertClass != targetElClass){
30205                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30206                                 this.lastInsertClass = targetElClass;
30207                         }
30208                 }
30209                 return dragElClass;
30210         },
30211
30212     onNodeOut : function(n, dd, e, data){
30213                 this.removeDropIndicators(n);
30214     },
30215
30216     onNodeDrop : function(n, dd, e, data){
30217         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30218                 return false;
30219         }
30220         var pt = this.getDropPoint(e, n, dd);
30221                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30222                 if (pt == "below") { insertAt++; }
30223                 for (var i = 0; i < data.records.length; i++) {
30224                         var r = data.records[i];
30225                         var dup = this.store.getById(r.id);
30226                         if (dup && (dd != this.dragZone)) {
30227                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30228                         } else {
30229                                 if (data.copy) {
30230                                         this.store.insert(insertAt++, r.copy());
30231                                 } else {
30232                                         data.source.isDirtyFlag = true;
30233                                         r.store.remove(r);
30234                                         this.store.insert(insertAt++, r);
30235                                 }
30236                                 this.isDirtyFlag = true;
30237                         }
30238                 }
30239                 this.dragZone.cachedTarget = null;
30240                 return true;
30241     },
30242
30243     removeDropIndicators : function(n){
30244                 if(n){
30245                         Roo.fly(n).removeClass([
30246                                 "x-view-drag-insert-above",
30247                                 "x-view-drag-insert-below"]);
30248                         this.lastInsertClass = "_noclass";
30249                 }
30250     },
30251
30252 /**
30253  *      Utility method. Add a delete option to the DDView's context menu.
30254  *      @param {String} imageUrl The URL of the "delete" icon image.
30255  */
30256         setDeletable: function(imageUrl) {
30257                 if (!this.singleSelect && !this.multiSelect) {
30258                         this.singleSelect = true;
30259                 }
30260                 var c = this.getContextMenu();
30261                 this.contextMenu.on("itemclick", function(item) {
30262                         switch (item.id) {
30263                                 case "delete":
30264                                         this.remove(this.getSelectedIndexes());
30265                                         break;
30266                         }
30267                 }, this);
30268                 this.contextMenu.add({
30269                         icon: imageUrl,
30270                         id: "delete",
30271                         text: 'Delete'
30272                 });
30273         },
30274         
30275 /**     Return the context menu for this DDView. */
30276         getContextMenu: function() {
30277                 if (!this.contextMenu) {
30278 //                      Create the View's context menu
30279                         this.contextMenu = new Roo.menu.Menu({
30280                                 id: this.id + "-contextmenu"
30281                         });
30282                         this.el.on("contextmenu", this.showContextMenu, this);
30283                 }
30284                 return this.contextMenu;
30285         },
30286         
30287         disableContextMenu: function() {
30288                 if (this.contextMenu) {
30289                         this.el.un("contextmenu", this.showContextMenu, this);
30290                 }
30291         },
30292
30293         showContextMenu: function(e, item) {
30294         item = this.findItemFromChild(e.getTarget());
30295                 if (item) {
30296                         e.stopEvent();
30297                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30298                         this.contextMenu.showAt(e.getXY());
30299             }
30300     },
30301
30302 /**
30303  *      Remove {@link Roo.data.Record}s at the specified indices.
30304  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30305  */
30306     remove: function(selectedIndices) {
30307                 selectedIndices = [].concat(selectedIndices);
30308                 for (var i = 0; i < selectedIndices.length; i++) {
30309                         var rec = this.store.getAt(selectedIndices[i]);
30310                         this.store.remove(rec);
30311                 }
30312     },
30313
30314 /**
30315  *      Double click fires the event, but also, if this is draggable, and there is only one other
30316  *      related DropZone, it transfers the selected node.
30317  */
30318     onDblClick : function(e){
30319         var item = this.findItemFromChild(e.getTarget());
30320         if(item){
30321             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30322                 return false;
30323             }
30324             if (this.dragGroup) {
30325                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30326                     while (targets.indexOf(this.dropZone) > -1) {
30327                             targets.remove(this.dropZone);
30328                                 }
30329                     if (targets.length == 1) {
30330                                         this.dragZone.cachedTarget = null;
30331                         var el = Roo.get(targets[0].getEl());
30332                         var box = el.getBox(true);
30333                         targets[0].onNodeDrop(el.dom, {
30334                                 target: el.dom,
30335                                 xy: [box.x, box.y + box.height - 1]
30336                         }, null, this.getDragData(e));
30337                     }
30338                 }
30339         }
30340     },
30341     
30342     handleSelection: function(e) {
30343                 this.dragZone.cachedTarget = null;
30344         var item = this.findItemFromChild(e.getTarget());
30345         if (!item) {
30346                 this.clearSelections(true);
30347                 return;
30348         }
30349                 if (item && (this.multiSelect || this.singleSelect)){
30350                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30351                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30352                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30353                                 this.unselect(item);
30354                         } else {
30355                                 this.select(item, this.multiSelect && e.ctrlKey);
30356                                 this.lastSelection = item;
30357                         }
30358                 }
30359     },
30360
30361     onItemClick : function(item, index, e){
30362                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30363                         return false;
30364                 }
30365                 return true;
30366     },
30367
30368     unselect : function(nodeInfo, suppressEvent){
30369                 var node = this.getNode(nodeInfo);
30370                 if(node && this.isSelected(node)){
30371                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30372                                 Roo.fly(node).removeClass(this.selectedClass);
30373                                 this.selections.remove(node);
30374                                 if(!suppressEvent){
30375                                         this.fireEvent("selectionchange", this, this.selections);
30376                                 }
30377                         }
30378                 }
30379     }
30380 });
30381 /*
30382  * Based on:
30383  * Ext JS Library 1.1.1
30384  * Copyright(c) 2006-2007, Ext JS, LLC.
30385  *
30386  * Originally Released Under LGPL - original licence link has changed is not relivant.
30387  *
30388  * Fork - LGPL
30389  * <script type="text/javascript">
30390  */
30391  
30392 /**
30393  * @class Roo.LayoutManager
30394  * @extends Roo.util.Observable
30395  * Base class for layout managers.
30396  */
30397 Roo.LayoutManager = function(container, config){
30398     Roo.LayoutManager.superclass.constructor.call(this);
30399     this.el = Roo.get(container);
30400     // ie scrollbar fix
30401     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30402         document.body.scroll = "no";
30403     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30404         this.el.position('relative');
30405     }
30406     this.id = this.el.id;
30407     this.el.addClass("x-layout-container");
30408     /** false to disable window resize monitoring @type Boolean */
30409     this.monitorWindowResize = true;
30410     this.regions = {};
30411     this.addEvents({
30412         /**
30413          * @event layout
30414          * Fires when a layout is performed. 
30415          * @param {Roo.LayoutManager} this
30416          */
30417         "layout" : true,
30418         /**
30419          * @event regionresized
30420          * Fires when the user resizes a region. 
30421          * @param {Roo.LayoutRegion} region The resized region
30422          * @param {Number} newSize The new size (width for east/west, height for north/south)
30423          */
30424         "regionresized" : true,
30425         /**
30426          * @event regioncollapsed
30427          * Fires when a region is collapsed. 
30428          * @param {Roo.LayoutRegion} region The collapsed region
30429          */
30430         "regioncollapsed" : true,
30431         /**
30432          * @event regionexpanded
30433          * Fires when a region is expanded.  
30434          * @param {Roo.LayoutRegion} region The expanded region
30435          */
30436         "regionexpanded" : true
30437     });
30438     this.updating = false;
30439     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30440 };
30441
30442 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30443     /**
30444      * Returns true if this layout is currently being updated
30445      * @return {Boolean}
30446      */
30447     isUpdating : function(){
30448         return this.updating; 
30449     },
30450     
30451     /**
30452      * Suspend the LayoutManager from doing auto-layouts while
30453      * making multiple add or remove calls
30454      */
30455     beginUpdate : function(){
30456         this.updating = true;    
30457     },
30458     
30459     /**
30460      * Restore auto-layouts and optionally disable the manager from performing a layout
30461      * @param {Boolean} noLayout true to disable a layout update 
30462      */
30463     endUpdate : function(noLayout){
30464         this.updating = false;
30465         if(!noLayout){
30466             this.layout();
30467         }    
30468     },
30469     
30470     layout: function(){
30471         
30472     },
30473     
30474     onRegionResized : function(region, newSize){
30475         this.fireEvent("regionresized", region, newSize);
30476         this.layout();
30477     },
30478     
30479     onRegionCollapsed : function(region){
30480         this.fireEvent("regioncollapsed", region);
30481     },
30482     
30483     onRegionExpanded : function(region){
30484         this.fireEvent("regionexpanded", region);
30485     },
30486         
30487     /**
30488      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30489      * performs box-model adjustments.
30490      * @return {Object} The size as an object {width: (the width), height: (the height)}
30491      */
30492     getViewSize : function(){
30493         var size;
30494         if(this.el.dom != document.body){
30495             size = this.el.getSize();
30496         }else{
30497             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30498         }
30499         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30500         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30501         return size;
30502     },
30503     
30504     /**
30505      * Returns the Element this layout is bound to.
30506      * @return {Roo.Element}
30507      */
30508     getEl : function(){
30509         return this.el;
30510     },
30511     
30512     /**
30513      * Returns the specified region.
30514      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30515      * @return {Roo.LayoutRegion}
30516      */
30517     getRegion : function(target){
30518         return this.regions[target.toLowerCase()];
30519     },
30520     
30521     onWindowResize : function(){
30522         if(this.monitorWindowResize){
30523             this.layout();
30524         }
30525     }
30526 });/*
30527  * Based on:
30528  * Ext JS Library 1.1.1
30529  * Copyright(c) 2006-2007, Ext JS, LLC.
30530  *
30531  * Originally Released Under LGPL - original licence link has changed is not relivant.
30532  *
30533  * Fork - LGPL
30534  * <script type="text/javascript">
30535  */
30536 /**
30537  * @class Roo.BorderLayout
30538  * @extends Roo.LayoutManager
30539  * @children Roo.ContentPanel
30540  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30541  * please see: <br><br>
30542  * <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>
30543  * <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>
30544  * Example:
30545  <pre><code>
30546  var layout = new Roo.BorderLayout(document.body, {
30547     north: {
30548         initialSize: 25,
30549         titlebar: false
30550     },
30551     west: {
30552         split:true,
30553         initialSize: 200,
30554         minSize: 175,
30555         maxSize: 400,
30556         titlebar: true,
30557         collapsible: true
30558     },
30559     east: {
30560         split:true,
30561         initialSize: 202,
30562         minSize: 175,
30563         maxSize: 400,
30564         titlebar: true,
30565         collapsible: true
30566     },
30567     south: {
30568         split:true,
30569         initialSize: 100,
30570         minSize: 100,
30571         maxSize: 200,
30572         titlebar: true,
30573         collapsible: true
30574     },
30575     center: {
30576         titlebar: true,
30577         autoScroll:true,
30578         resizeTabs: true,
30579         minTabWidth: 50,
30580         preferredTabWidth: 150
30581     }
30582 });
30583
30584 // shorthand
30585 var CP = Roo.ContentPanel;
30586
30587 layout.beginUpdate();
30588 layout.add("north", new CP("north", "North"));
30589 layout.add("south", new CP("south", {title: "South", closable: true}));
30590 layout.add("west", new CP("west", {title: "West"}));
30591 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30592 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30593 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30594 layout.getRegion("center").showPanel("center1");
30595 layout.endUpdate();
30596 </code></pre>
30597
30598 <b>The container the layout is rendered into can be either the body element or any other element.
30599 If it is not the body element, the container needs to either be an absolute positioned element,
30600 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30601 the container size if it is not the body element.</b>
30602
30603 * @constructor
30604 * Create a new BorderLayout
30605 * @param {String/HTMLElement/Element} container The container this layout is bound to
30606 * @param {Object} config Configuration options
30607  */
30608 Roo.BorderLayout = function(container, config){
30609     config = config || {};
30610     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30611     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30612     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30613         var target = this.factory.validRegions[i];
30614         if(config[target]){
30615             this.addRegion(target, config[target]);
30616         }
30617     }
30618 };
30619
30620 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30621         
30622         /**
30623          * @cfg {Roo.LayoutRegion} east
30624          */
30625         /**
30626          * @cfg {Roo.LayoutRegion} west
30627          */
30628         /**
30629          * @cfg {Roo.LayoutRegion} north
30630          */
30631         /**
30632          * @cfg {Roo.LayoutRegion} south
30633          */
30634         /**
30635          * @cfg {Roo.LayoutRegion} center
30636          */
30637     /**
30638      * Creates and adds a new region if it doesn't already exist.
30639      * @param {String} target The target region key (north, south, east, west or center).
30640      * @param {Object} config The regions config object
30641      * @return {BorderLayoutRegion} The new region
30642      */
30643     addRegion : function(target, config){
30644         if(!this.regions[target]){
30645             var r = this.factory.create(target, this, config);
30646             this.bindRegion(target, r);
30647         }
30648         return this.regions[target];
30649     },
30650
30651     // private (kinda)
30652     bindRegion : function(name, r){
30653         this.regions[name] = r;
30654         r.on("visibilitychange", this.layout, this);
30655         r.on("paneladded", this.layout, this);
30656         r.on("panelremoved", this.layout, this);
30657         r.on("invalidated", this.layout, this);
30658         r.on("resized", this.onRegionResized, this);
30659         r.on("collapsed", this.onRegionCollapsed, this);
30660         r.on("expanded", this.onRegionExpanded, this);
30661     },
30662
30663     /**
30664      * Performs a layout update.
30665      */
30666     layout : function(){
30667         if(this.updating) {
30668             return;
30669         }
30670         var size = this.getViewSize();
30671         var w = size.width;
30672         var h = size.height;
30673         var centerW = w;
30674         var centerH = h;
30675         var centerY = 0;
30676         var centerX = 0;
30677         //var x = 0, y = 0;
30678
30679         var rs = this.regions;
30680         var north = rs["north"];
30681         var south = rs["south"]; 
30682         var west = rs["west"];
30683         var east = rs["east"];
30684         var center = rs["center"];
30685         //if(this.hideOnLayout){ // not supported anymore
30686             //c.el.setStyle("display", "none");
30687         //}
30688         if(north && north.isVisible()){
30689             var b = north.getBox();
30690             var m = north.getMargins();
30691             b.width = w - (m.left+m.right);
30692             b.x = m.left;
30693             b.y = m.top;
30694             centerY = b.height + b.y + m.bottom;
30695             centerH -= centerY;
30696             north.updateBox(this.safeBox(b));
30697         }
30698         if(south && south.isVisible()){
30699             var b = south.getBox();
30700             var m = south.getMargins();
30701             b.width = w - (m.left+m.right);
30702             b.x = m.left;
30703             var totalHeight = (b.height + m.top + m.bottom);
30704             b.y = h - totalHeight + m.top;
30705             centerH -= totalHeight;
30706             south.updateBox(this.safeBox(b));
30707         }
30708         if(west && west.isVisible()){
30709             var b = west.getBox();
30710             var m = west.getMargins();
30711             b.height = centerH - (m.top+m.bottom);
30712             b.x = m.left;
30713             b.y = centerY + m.top;
30714             var totalWidth = (b.width + m.left + m.right);
30715             centerX += totalWidth;
30716             centerW -= totalWidth;
30717             west.updateBox(this.safeBox(b));
30718         }
30719         if(east && east.isVisible()){
30720             var b = east.getBox();
30721             var m = east.getMargins();
30722             b.height = centerH - (m.top+m.bottom);
30723             var totalWidth = (b.width + m.left + m.right);
30724             b.x = w - totalWidth + m.left;
30725             b.y = centerY + m.top;
30726             centerW -= totalWidth;
30727             east.updateBox(this.safeBox(b));
30728         }
30729         if(center){
30730             var m = center.getMargins();
30731             var centerBox = {
30732                 x: centerX + m.left,
30733                 y: centerY + m.top,
30734                 width: centerW - (m.left+m.right),
30735                 height: centerH - (m.top+m.bottom)
30736             };
30737             //if(this.hideOnLayout){
30738                 //center.el.setStyle("display", "block");
30739             //}
30740             center.updateBox(this.safeBox(centerBox));
30741         }
30742         this.el.repaint();
30743         this.fireEvent("layout", this);
30744     },
30745
30746     // private
30747     safeBox : function(box){
30748         box.width = Math.max(0, box.width);
30749         box.height = Math.max(0, box.height);
30750         return box;
30751     },
30752
30753     /**
30754      * Adds a ContentPanel (or subclass) to this layout.
30755      * @param {String} target The target region key (north, south, east, west or center).
30756      * @param {Roo.ContentPanel} panel The panel to add
30757      * @return {Roo.ContentPanel} The added panel
30758      */
30759     add : function(target, panel){
30760          
30761         target = target.toLowerCase();
30762         return this.regions[target].add(panel);
30763     },
30764
30765     /**
30766      * Remove a ContentPanel (or subclass) to this layout.
30767      * @param {String} target The target region key (north, south, east, west or center).
30768      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30769      * @return {Roo.ContentPanel} The removed panel
30770      */
30771     remove : function(target, panel){
30772         target = target.toLowerCase();
30773         return this.regions[target].remove(panel);
30774     },
30775
30776     /**
30777      * Searches all regions for a panel with the specified id
30778      * @param {String} panelId
30779      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30780      */
30781     findPanel : function(panelId){
30782         var rs = this.regions;
30783         for(var target in rs){
30784             if(typeof rs[target] != "function"){
30785                 var p = rs[target].getPanel(panelId);
30786                 if(p){
30787                     return p;
30788                 }
30789             }
30790         }
30791         return null;
30792     },
30793
30794     /**
30795      * Searches all regions for a panel with the specified id and activates (shows) it.
30796      * @param {String/ContentPanel} panelId The panels id or the panel itself
30797      * @return {Roo.ContentPanel} The shown panel or null
30798      */
30799     showPanel : function(panelId) {
30800       var rs = this.regions;
30801       for(var target in rs){
30802          var r = rs[target];
30803          if(typeof r != "function"){
30804             if(r.hasPanel(panelId)){
30805                return r.showPanel(panelId);
30806             }
30807          }
30808       }
30809       return null;
30810    },
30811
30812    /**
30813      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30814      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30815      */
30816     restoreState : function(provider){
30817         if(!provider){
30818             provider = Roo.state.Manager;
30819         }
30820         var sm = new Roo.LayoutStateManager();
30821         sm.init(this, provider);
30822     },
30823
30824     /**
30825      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30826      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30827      * a valid ContentPanel config object.  Example:
30828      * <pre><code>
30829 // Create the main layout
30830 var layout = new Roo.BorderLayout('main-ct', {
30831     west: {
30832         split:true,
30833         minSize: 175,
30834         titlebar: true
30835     },
30836     center: {
30837         title:'Components'
30838     }
30839 }, 'main-ct');
30840
30841 // Create and add multiple ContentPanels at once via configs
30842 layout.batchAdd({
30843    west: {
30844        id: 'source-files',
30845        autoCreate:true,
30846        title:'Ext Source Files',
30847        autoScroll:true,
30848        fitToFrame:true
30849    },
30850    center : {
30851        el: cview,
30852        autoScroll:true,
30853        fitToFrame:true,
30854        toolbar: tb,
30855        resizeEl:'cbody'
30856    }
30857 });
30858 </code></pre>
30859      * @param {Object} regions An object containing ContentPanel configs by region name
30860      */
30861     batchAdd : function(regions){
30862         this.beginUpdate();
30863         for(var rname in regions){
30864             var lr = this.regions[rname];
30865             if(lr){
30866                 this.addTypedPanels(lr, regions[rname]);
30867             }
30868         }
30869         this.endUpdate();
30870     },
30871
30872     // private
30873     addTypedPanels : function(lr, ps){
30874         if(typeof ps == 'string'){
30875             lr.add(new Roo.ContentPanel(ps));
30876         }
30877         else if(ps instanceof Array){
30878             for(var i =0, len = ps.length; i < len; i++){
30879                 this.addTypedPanels(lr, ps[i]);
30880             }
30881         }
30882         else if(!ps.events){ // raw config?
30883             var el = ps.el;
30884             delete ps.el; // prevent conflict
30885             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30886         }
30887         else {  // panel object assumed!
30888             lr.add(ps);
30889         }
30890     },
30891     /**
30892      * Adds a xtype elements to the layout.
30893      * <pre><code>
30894
30895 layout.addxtype({
30896        xtype : 'ContentPanel',
30897        region: 'west',
30898        items: [ .... ]
30899    }
30900 );
30901
30902 layout.addxtype({
30903         xtype : 'NestedLayoutPanel',
30904         region: 'west',
30905         layout: {
30906            center: { },
30907            west: { }   
30908         },
30909         items : [ ... list of content panels or nested layout panels.. ]
30910    }
30911 );
30912 </code></pre>
30913      * @param {Object} cfg Xtype definition of item to add.
30914      */
30915     addxtype : function(cfg)
30916     {
30917         // basically accepts a pannel...
30918         // can accept a layout region..!?!?
30919         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30920         
30921         if (!cfg.xtype.match(/Panel$/)) {
30922             return false;
30923         }
30924         var ret = false;
30925         
30926         if (typeof(cfg.region) == 'undefined') {
30927             Roo.log("Failed to add Panel, region was not set");
30928             Roo.log(cfg);
30929             return false;
30930         }
30931         var region = cfg.region;
30932         delete cfg.region;
30933         
30934           
30935         var xitems = [];
30936         if (cfg.items) {
30937             xitems = cfg.items;
30938             delete cfg.items;
30939         }
30940         var nb = false;
30941         
30942         switch(cfg.xtype) 
30943         {
30944             case 'ContentPanel':  // ContentPanel (el, cfg)
30945             case 'ScrollPanel':  // ContentPanel (el, cfg)
30946             case 'ViewPanel': 
30947                 if(cfg.autoCreate) {
30948                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30949                 } else {
30950                     var el = this.el.createChild();
30951                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30952                 }
30953                 
30954                 this.add(region, ret);
30955                 break;
30956             
30957             
30958             case 'TreePanel': // our new panel!
30959                 cfg.el = this.el.createChild();
30960                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30961                 this.add(region, ret);
30962                 break;
30963             
30964             case 'NestedLayoutPanel': 
30965                 // create a new Layout (which is  a Border Layout...
30966                 var el = this.el.createChild();
30967                 var clayout = cfg.layout;
30968                 delete cfg.layout;
30969                 clayout.items   = clayout.items  || [];
30970                 // replace this exitems with the clayout ones..
30971                 xitems = clayout.items;
30972                  
30973                 
30974                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30975                     cfg.background = false;
30976                 }
30977                 var layout = new Roo.BorderLayout(el, clayout);
30978                 
30979                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30980                 //console.log('adding nested layout panel '  + cfg.toSource());
30981                 this.add(region, ret);
30982                 nb = {}; /// find first...
30983                 break;
30984                 
30985             case 'GridPanel': 
30986             
30987                 // needs grid and region
30988                 
30989                 //var el = this.getRegion(region).el.createChild();
30990                 var el = this.el.createChild();
30991                 // create the grid first...
30992                 
30993                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30994                 delete cfg.grid;
30995                 if (region == 'center' && this.active ) {
30996                     cfg.background = false;
30997                 }
30998                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30999                 
31000                 this.add(region, ret);
31001                 if (cfg.background) {
31002                     ret.on('activate', function(gp) {
31003                         if (!gp.grid.rendered) {
31004                             gp.grid.render();
31005                         }
31006                     });
31007                 } else {
31008                     grid.render();
31009                 }
31010                 break;
31011            
31012            
31013            
31014                 
31015                 
31016                 
31017             default:
31018                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31019                     
31020                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31021                     this.add(region, ret);
31022                 } else {
31023                 
31024                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31025                     return null;
31026                 }
31027                 
31028              // GridPanel (grid, cfg)
31029             
31030         }
31031         this.beginUpdate();
31032         // add children..
31033         var region = '';
31034         var abn = {};
31035         Roo.each(xitems, function(i)  {
31036             region = nb && i.region ? i.region : false;
31037             
31038             var add = ret.addxtype(i);
31039            
31040             if (region) {
31041                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31042                 if (!i.background) {
31043                     abn[region] = nb[region] ;
31044                 }
31045             }
31046             
31047         });
31048         this.endUpdate();
31049
31050         // make the last non-background panel active..
31051         //if (nb) { Roo.log(abn); }
31052         if (nb) {
31053             
31054             for(var r in abn) {
31055                 region = this.getRegion(r);
31056                 if (region) {
31057                     // tried using nb[r], but it does not work..
31058                      
31059                     region.showPanel(abn[r]);
31060                    
31061                 }
31062             }
31063         }
31064         return ret;
31065         
31066     }
31067 });
31068
31069 /**
31070  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31071  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31072  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31073  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31074  * <pre><code>
31075 // shorthand
31076 var CP = Roo.ContentPanel;
31077
31078 var layout = Roo.BorderLayout.create({
31079     north: {
31080         initialSize: 25,
31081         titlebar: false,
31082         panels: [new CP("north", "North")]
31083     },
31084     west: {
31085         split:true,
31086         initialSize: 200,
31087         minSize: 175,
31088         maxSize: 400,
31089         titlebar: true,
31090         collapsible: true,
31091         panels: [new CP("west", {title: "West"})]
31092     },
31093     east: {
31094         split:true,
31095         initialSize: 202,
31096         minSize: 175,
31097         maxSize: 400,
31098         titlebar: true,
31099         collapsible: true,
31100         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31101     },
31102     south: {
31103         split:true,
31104         initialSize: 100,
31105         minSize: 100,
31106         maxSize: 200,
31107         titlebar: true,
31108         collapsible: true,
31109         panels: [new CP("south", {title: "South", closable: true})]
31110     },
31111     center: {
31112         titlebar: true,
31113         autoScroll:true,
31114         resizeTabs: true,
31115         minTabWidth: 50,
31116         preferredTabWidth: 150,
31117         panels: [
31118             new CP("center1", {title: "Close Me", closable: true}),
31119             new CP("center2", {title: "Center Panel", closable: false})
31120         ]
31121     }
31122 }, document.body);
31123
31124 layout.getRegion("center").showPanel("center1");
31125 </code></pre>
31126  * @param config
31127  * @param targetEl
31128  */
31129 Roo.BorderLayout.create = function(config, targetEl){
31130     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31131     layout.beginUpdate();
31132     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31133     for(var j = 0, jlen = regions.length; j < jlen; j++){
31134         var lr = regions[j];
31135         if(layout.regions[lr] && config[lr].panels){
31136             var r = layout.regions[lr];
31137             var ps = config[lr].panels;
31138             layout.addTypedPanels(r, ps);
31139         }
31140     }
31141     layout.endUpdate();
31142     return layout;
31143 };
31144
31145 // private
31146 Roo.BorderLayout.RegionFactory = {
31147     // private
31148     validRegions : ["north","south","east","west","center"],
31149
31150     // private
31151     create : function(target, mgr, config){
31152         target = target.toLowerCase();
31153         if(config.lightweight || config.basic){
31154             return new Roo.BasicLayoutRegion(mgr, config, target);
31155         }
31156         switch(target){
31157             case "north":
31158                 return new Roo.NorthLayoutRegion(mgr, config);
31159             case "south":
31160                 return new Roo.SouthLayoutRegion(mgr, config);
31161             case "east":
31162                 return new Roo.EastLayoutRegion(mgr, config);
31163             case "west":
31164                 return new Roo.WestLayoutRegion(mgr, config);
31165             case "center":
31166                 return new Roo.CenterLayoutRegion(mgr, config);
31167         }
31168         throw 'Layout region "'+target+'" not supported.';
31169     }
31170 };/*
31171  * Based on:
31172  * Ext JS Library 1.1.1
31173  * Copyright(c) 2006-2007, Ext JS, LLC.
31174  *
31175  * Originally Released Under LGPL - original licence link has changed is not relivant.
31176  *
31177  * Fork - LGPL
31178  * <script type="text/javascript">
31179  */
31180  
31181 /**
31182  * @class Roo.BasicLayoutRegion
31183  * @extends Roo.util.Observable
31184  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31185  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31186  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31187  */
31188 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31189     this.mgr = mgr;
31190     this.position  = pos;
31191     this.events = {
31192         /**
31193          * @scope Roo.BasicLayoutRegion
31194          */
31195         
31196         /**
31197          * @event beforeremove
31198          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31199          * @param {Roo.LayoutRegion} this
31200          * @param {Roo.ContentPanel} panel The panel
31201          * @param {Object} e The cancel event object
31202          */
31203         "beforeremove" : true,
31204         /**
31205          * @event invalidated
31206          * Fires when the layout for this region is changed.
31207          * @param {Roo.LayoutRegion} this
31208          */
31209         "invalidated" : true,
31210         /**
31211          * @event visibilitychange
31212          * Fires when this region is shown or hidden 
31213          * @param {Roo.LayoutRegion} this
31214          * @param {Boolean} visibility true or false
31215          */
31216         "visibilitychange" : true,
31217         /**
31218          * @event paneladded
31219          * Fires when a panel is added. 
31220          * @param {Roo.LayoutRegion} this
31221          * @param {Roo.ContentPanel} panel The panel
31222          */
31223         "paneladded" : true,
31224         /**
31225          * @event panelremoved
31226          * Fires when a panel is removed. 
31227          * @param {Roo.LayoutRegion} this
31228          * @param {Roo.ContentPanel} panel The panel
31229          */
31230         "panelremoved" : true,
31231         /**
31232          * @event beforecollapse
31233          * Fires when this region before collapse.
31234          * @param {Roo.LayoutRegion} this
31235          */
31236         "beforecollapse" : true,
31237         /**
31238          * @event collapsed
31239          * Fires when this region is collapsed.
31240          * @param {Roo.LayoutRegion} this
31241          */
31242         "collapsed" : true,
31243         /**
31244          * @event expanded
31245          * Fires when this region is expanded.
31246          * @param {Roo.LayoutRegion} this
31247          */
31248         "expanded" : true,
31249         /**
31250          * @event slideshow
31251          * Fires when this region is slid into view.
31252          * @param {Roo.LayoutRegion} this
31253          */
31254         "slideshow" : true,
31255         /**
31256          * @event slidehide
31257          * Fires when this region slides out of view. 
31258          * @param {Roo.LayoutRegion} this
31259          */
31260         "slidehide" : true,
31261         /**
31262          * @event panelactivated
31263          * Fires when a panel is activated. 
31264          * @param {Roo.LayoutRegion} this
31265          * @param {Roo.ContentPanel} panel The activated panel
31266          */
31267         "panelactivated" : true,
31268         /**
31269          * @event resized
31270          * Fires when the user resizes this region. 
31271          * @param {Roo.LayoutRegion} this
31272          * @param {Number} newSize The new size (width for east/west, height for north/south)
31273          */
31274         "resized" : true
31275     };
31276     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31277     this.panels = new Roo.util.MixedCollection();
31278     this.panels.getKey = this.getPanelId.createDelegate(this);
31279     this.box = null;
31280     this.activePanel = null;
31281     // ensure listeners are added...
31282     
31283     if (config.listeners || config.events) {
31284         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31285             listeners : config.listeners || {},
31286             events : config.events || {}
31287         });
31288     }
31289     
31290     if(skipConfig !== true){
31291         this.applyConfig(config);
31292     }
31293 };
31294
31295 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31296     getPanelId : function(p){
31297         return p.getId();
31298     },
31299     
31300     applyConfig : function(config){
31301         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31302         this.config = config;
31303         
31304     },
31305     
31306     /**
31307      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31308      * the width, for horizontal (north, south) the height.
31309      * @param {Number} newSize The new width or height
31310      */
31311     resizeTo : function(newSize){
31312         var el = this.el ? this.el :
31313                  (this.activePanel ? this.activePanel.getEl() : null);
31314         if(el){
31315             switch(this.position){
31316                 case "east":
31317                 case "west":
31318                     el.setWidth(newSize);
31319                     this.fireEvent("resized", this, newSize);
31320                 break;
31321                 case "north":
31322                 case "south":
31323                     el.setHeight(newSize);
31324                     this.fireEvent("resized", this, newSize);
31325                 break;                
31326             }
31327         }
31328     },
31329     
31330     getBox : function(){
31331         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31332     },
31333     
31334     getMargins : function(){
31335         return this.margins;
31336     },
31337     
31338     updateBox : function(box){
31339         this.box = box;
31340         var el = this.activePanel.getEl();
31341         el.dom.style.left = box.x + "px";
31342         el.dom.style.top = box.y + "px";
31343         this.activePanel.setSize(box.width, box.height);
31344     },
31345     
31346     /**
31347      * Returns the container element for this region.
31348      * @return {Roo.Element}
31349      */
31350     getEl : function(){
31351         return this.activePanel;
31352     },
31353     
31354     /**
31355      * Returns true if this region is currently visible.
31356      * @return {Boolean}
31357      */
31358     isVisible : function(){
31359         return this.activePanel ? true : false;
31360     },
31361     
31362     setActivePanel : function(panel){
31363         panel = this.getPanel(panel);
31364         if(this.activePanel && this.activePanel != panel){
31365             this.activePanel.setActiveState(false);
31366             this.activePanel.getEl().setLeftTop(-10000,-10000);
31367         }
31368         this.activePanel = panel;
31369         panel.setActiveState(true);
31370         if(this.box){
31371             panel.setSize(this.box.width, this.box.height);
31372         }
31373         this.fireEvent("panelactivated", this, panel);
31374         this.fireEvent("invalidated");
31375     },
31376     
31377     /**
31378      * Show the specified panel.
31379      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31380      * @return {Roo.ContentPanel} The shown panel or null
31381      */
31382     showPanel : function(panel){
31383         if(panel = this.getPanel(panel)){
31384             this.setActivePanel(panel);
31385         }
31386         return panel;
31387     },
31388     
31389     /**
31390      * Get the active panel for this region.
31391      * @return {Roo.ContentPanel} The active panel or null
31392      */
31393     getActivePanel : function(){
31394         return this.activePanel;
31395     },
31396     
31397     /**
31398      * Add the passed ContentPanel(s)
31399      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31400      * @return {Roo.ContentPanel} The panel added (if only one was added)
31401      */
31402     add : function(panel){
31403         if(arguments.length > 1){
31404             for(var i = 0, len = arguments.length; i < len; i++) {
31405                 this.add(arguments[i]);
31406             }
31407             return null;
31408         }
31409         if(this.hasPanel(panel)){
31410             this.showPanel(panel);
31411             return panel;
31412         }
31413         var el = panel.getEl();
31414         if(el.dom.parentNode != this.mgr.el.dom){
31415             this.mgr.el.dom.appendChild(el.dom);
31416         }
31417         if(panel.setRegion){
31418             panel.setRegion(this);
31419         }
31420         this.panels.add(panel);
31421         el.setStyle("position", "absolute");
31422         if(!panel.background){
31423             this.setActivePanel(panel);
31424             if(this.config.initialSize && this.panels.getCount()==1){
31425                 this.resizeTo(this.config.initialSize);
31426             }
31427         }
31428         this.fireEvent("paneladded", this, panel);
31429         return panel;
31430     },
31431     
31432     /**
31433      * Returns true if the panel is in this region.
31434      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31435      * @return {Boolean}
31436      */
31437     hasPanel : function(panel){
31438         if(typeof panel == "object"){ // must be panel obj
31439             panel = panel.getId();
31440         }
31441         return this.getPanel(panel) ? true : false;
31442     },
31443     
31444     /**
31445      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31446      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31447      * @param {Boolean} preservePanel Overrides the config preservePanel option
31448      * @return {Roo.ContentPanel} The panel that was removed
31449      */
31450     remove : function(panel, preservePanel){
31451         panel = this.getPanel(panel);
31452         if(!panel){
31453             return null;
31454         }
31455         var e = {};
31456         this.fireEvent("beforeremove", this, panel, e);
31457         if(e.cancel === true){
31458             return null;
31459         }
31460         var panelId = panel.getId();
31461         this.panels.removeKey(panelId);
31462         return panel;
31463     },
31464     
31465     /**
31466      * Returns the panel specified or null if it's not in this region.
31467      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31468      * @return {Roo.ContentPanel}
31469      */
31470     getPanel : function(id){
31471         if(typeof id == "object"){ // must be panel obj
31472             return id;
31473         }
31474         return this.panels.get(id);
31475     },
31476     
31477     /**
31478      * Returns this regions position (north/south/east/west/center).
31479      * @return {String} 
31480      */
31481     getPosition: function(){
31482         return this.position;    
31483     }
31484 });/*
31485  * Based on:
31486  * Ext JS Library 1.1.1
31487  * Copyright(c) 2006-2007, Ext JS, LLC.
31488  *
31489  * Originally Released Under LGPL - original licence link has changed is not relivant.
31490  *
31491  * Fork - LGPL
31492  * <script type="text/javascript">
31493  */
31494  
31495 /**
31496  * @class Roo.LayoutRegion
31497  * @extends Roo.BasicLayoutRegion
31498  * This class represents a region in a layout manager.
31499  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31500  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31501  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31502  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31503  * @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})
31504  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31505  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31506  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31507  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31508  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31509  * @cfg {String}    title           The title for the region (overrides panel titles)
31510  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31511  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31512  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31513  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31514  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31515  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31516  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31517  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31518  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31519  * @cfg {Boolean}   showPin         True to show a pin button
31520  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31521  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31522  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31523  * @cfg {Number}    width           For East/West panels
31524  * @cfg {Number}    height          For North/South panels
31525  * @cfg {Boolean}   split           To show the splitter
31526  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31527  */
31528 Roo.LayoutRegion = function(mgr, config, pos){
31529     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31530     var dh = Roo.DomHelper;
31531     /** This region's container element 
31532     * @type Roo.Element */
31533     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31534     /** This region's title element 
31535     * @type Roo.Element */
31536
31537     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31538         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31539         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31540     ]}, true);
31541     this.titleEl.enableDisplayMode();
31542     /** This region's title text element 
31543     * @type HTMLElement */
31544     this.titleTextEl = this.titleEl.dom.firstChild;
31545     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31546     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31547     this.closeBtn.enableDisplayMode();
31548     this.closeBtn.on("click", this.closeClicked, this);
31549     this.closeBtn.hide();
31550
31551     this.createBody(config);
31552     this.visible = true;
31553     this.collapsed = false;
31554
31555     if(config.hideWhenEmpty){
31556         this.hide();
31557         this.on("paneladded", this.validateVisibility, this);
31558         this.on("panelremoved", this.validateVisibility, this);
31559     }
31560     this.applyConfig(config);
31561 };
31562
31563 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31564
31565     createBody : function(){
31566         /** This region's body element 
31567         * @type Roo.Element */
31568         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31569     },
31570
31571     applyConfig : function(c){
31572         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31573             var dh = Roo.DomHelper;
31574             if(c.titlebar !== false){
31575                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31576                 this.collapseBtn.on("click", this.collapse, this);
31577                 this.collapseBtn.enableDisplayMode();
31578
31579                 if(c.showPin === true || this.showPin){
31580                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31581                     this.stickBtn.enableDisplayMode();
31582                     this.stickBtn.on("click", this.expand, this);
31583                     this.stickBtn.hide();
31584                 }
31585             }
31586             /** This region's collapsed element
31587             * @type Roo.Element */
31588             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31589                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31590             ]}, true);
31591             if(c.floatable !== false){
31592                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31593                this.collapsedEl.on("click", this.collapseClick, this);
31594             }
31595
31596             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31597                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31598                    id: "message", unselectable: "on", style:{"float":"left"}});
31599                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31600              }
31601             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31602             this.expandBtn.on("click", this.expand, this);
31603         }
31604         if(this.collapseBtn){
31605             this.collapseBtn.setVisible(c.collapsible == true);
31606         }
31607         this.cmargins = c.cmargins || this.cmargins ||
31608                          (this.position == "west" || this.position == "east" ?
31609                              {top: 0, left: 2, right:2, bottom: 0} :
31610                              {top: 2, left: 0, right:0, bottom: 2});
31611         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31612         this.bottomTabs = c.tabPosition != "top";
31613         this.autoScroll = c.autoScroll || false;
31614         if(this.autoScroll){
31615             this.bodyEl.setStyle("overflow", "auto");
31616         }else{
31617             this.bodyEl.setStyle("overflow", "hidden");
31618         }
31619         //if(c.titlebar !== false){
31620             if((!c.titlebar && !c.title) || c.titlebar === false){
31621                 this.titleEl.hide();
31622             }else{
31623                 this.titleEl.show();
31624                 if(c.title){
31625                     this.titleTextEl.innerHTML = c.title;
31626                 }
31627             }
31628         //}
31629         this.duration = c.duration || .30;
31630         this.slideDuration = c.slideDuration || .45;
31631         this.config = c;
31632         if(c.collapsed){
31633             this.collapse(true);
31634         }
31635         if(c.hidden){
31636             this.hide();
31637         }
31638     },
31639     /**
31640      * Returns true if this region is currently visible.
31641      * @return {Boolean}
31642      */
31643     isVisible : function(){
31644         return this.visible;
31645     },
31646
31647     /**
31648      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31649      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31650      */
31651     setCollapsedTitle : function(title){
31652         title = title || "&#160;";
31653         if(this.collapsedTitleTextEl){
31654             this.collapsedTitleTextEl.innerHTML = title;
31655         }
31656     },
31657
31658     getBox : function(){
31659         var b;
31660         if(!this.collapsed){
31661             b = this.el.getBox(false, true);
31662         }else{
31663             b = this.collapsedEl.getBox(false, true);
31664         }
31665         return b;
31666     },
31667
31668     getMargins : function(){
31669         return this.collapsed ? this.cmargins : this.margins;
31670     },
31671
31672     highlight : function(){
31673         this.el.addClass("x-layout-panel-dragover");
31674     },
31675
31676     unhighlight : function(){
31677         this.el.removeClass("x-layout-panel-dragover");
31678     },
31679
31680     updateBox : function(box){
31681         this.box = box;
31682         if(!this.collapsed){
31683             this.el.dom.style.left = box.x + "px";
31684             this.el.dom.style.top = box.y + "px";
31685             this.updateBody(box.width, box.height);
31686         }else{
31687             this.collapsedEl.dom.style.left = box.x + "px";
31688             this.collapsedEl.dom.style.top = box.y + "px";
31689             this.collapsedEl.setSize(box.width, box.height);
31690         }
31691         if(this.tabs){
31692             this.tabs.autoSizeTabs();
31693         }
31694     },
31695
31696     updateBody : function(w, h){
31697         if(w !== null){
31698             this.el.setWidth(w);
31699             w -= this.el.getBorderWidth("rl");
31700             if(this.config.adjustments){
31701                 w += this.config.adjustments[0];
31702             }
31703         }
31704         if(h !== null){
31705             this.el.setHeight(h);
31706             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31707             h -= this.el.getBorderWidth("tb");
31708             if(this.config.adjustments){
31709                 h += this.config.adjustments[1];
31710             }
31711             this.bodyEl.setHeight(h);
31712             if(this.tabs){
31713                 h = this.tabs.syncHeight(h);
31714             }
31715         }
31716         if(this.panelSize){
31717             w = w !== null ? w : this.panelSize.width;
31718             h = h !== null ? h : this.panelSize.height;
31719         }
31720         if(this.activePanel){
31721             var el = this.activePanel.getEl();
31722             w = w !== null ? w : el.getWidth();
31723             h = h !== null ? h : el.getHeight();
31724             this.panelSize = {width: w, height: h};
31725             this.activePanel.setSize(w, h);
31726         }
31727         if(Roo.isIE && this.tabs){
31728             this.tabs.el.repaint();
31729         }
31730     },
31731
31732     /**
31733      * Returns the container element for this region.
31734      * @return {Roo.Element}
31735      */
31736     getEl : function(){
31737         return this.el;
31738     },
31739
31740     /**
31741      * Hides this region.
31742      */
31743     hide : function(){
31744         if(!this.collapsed){
31745             this.el.dom.style.left = "-2000px";
31746             this.el.hide();
31747         }else{
31748             this.collapsedEl.dom.style.left = "-2000px";
31749             this.collapsedEl.hide();
31750         }
31751         this.visible = false;
31752         this.fireEvent("visibilitychange", this, false);
31753     },
31754
31755     /**
31756      * Shows this region if it was previously hidden.
31757      */
31758     show : function(){
31759         if(!this.collapsed){
31760             this.el.show();
31761         }else{
31762             this.collapsedEl.show();
31763         }
31764         this.visible = true;
31765         this.fireEvent("visibilitychange", this, true);
31766     },
31767
31768     closeClicked : function(){
31769         if(this.activePanel){
31770             this.remove(this.activePanel);
31771         }
31772     },
31773
31774     collapseClick : function(e){
31775         if(this.isSlid){
31776            e.stopPropagation();
31777            this.slideIn();
31778         }else{
31779            e.stopPropagation();
31780            this.slideOut();
31781         }
31782     },
31783
31784     /**
31785      * Collapses this region.
31786      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31787      */
31788     collapse : function(skipAnim, skipCheck){
31789         if(this.collapsed) {
31790             return;
31791         }
31792         
31793         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
31794             
31795             this.collapsed = true;
31796             if(this.split){
31797                 this.split.el.hide();
31798             }
31799             if(this.config.animate && skipAnim !== true){
31800                 this.fireEvent("invalidated", this);
31801                 this.animateCollapse();
31802             }else{
31803                 this.el.setLocation(-20000,-20000);
31804                 this.el.hide();
31805                 this.collapsedEl.show();
31806                 this.fireEvent("collapsed", this);
31807                 this.fireEvent("invalidated", this);
31808             }
31809         }
31810         
31811     },
31812
31813     animateCollapse : function(){
31814         // overridden
31815     },
31816
31817     /**
31818      * Expands this region if it was previously collapsed.
31819      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31820      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31821      */
31822     expand : function(e, skipAnim){
31823         if(e) {
31824             e.stopPropagation();
31825         }
31826         if(!this.collapsed || this.el.hasActiveFx()) {
31827             return;
31828         }
31829         if(this.isSlid){
31830             this.afterSlideIn();
31831             skipAnim = true;
31832         }
31833         this.collapsed = false;
31834         if(this.config.animate && skipAnim !== true){
31835             this.animateExpand();
31836         }else{
31837             this.el.show();
31838             if(this.split){
31839                 this.split.el.show();
31840             }
31841             this.collapsedEl.setLocation(-2000,-2000);
31842             this.collapsedEl.hide();
31843             this.fireEvent("invalidated", this);
31844             this.fireEvent("expanded", this);
31845         }
31846     },
31847
31848     animateExpand : function(){
31849         // overridden
31850     },
31851
31852     initTabs : function()
31853     {
31854         this.bodyEl.setStyle("overflow", "hidden");
31855         var ts = new Roo.TabPanel(
31856                 this.bodyEl.dom,
31857                 {
31858                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31859                     disableTooltips: this.config.disableTabTips,
31860                     toolbar : this.config.toolbar
31861                 }
31862         );
31863         if(this.config.hideTabs){
31864             ts.stripWrap.setDisplayed(false);
31865         }
31866         this.tabs = ts;
31867         ts.resizeTabs = this.config.resizeTabs === true;
31868         ts.minTabWidth = this.config.minTabWidth || 40;
31869         ts.maxTabWidth = this.config.maxTabWidth || 250;
31870         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31871         ts.monitorResize = false;
31872         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31873         ts.bodyEl.addClass('x-layout-tabs-body');
31874         this.panels.each(this.initPanelAsTab, this);
31875     },
31876
31877     initPanelAsTab : function(panel){
31878         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31879                     this.config.closeOnTab && panel.isClosable());
31880         if(panel.tabTip !== undefined){
31881             ti.setTooltip(panel.tabTip);
31882         }
31883         ti.on("activate", function(){
31884               this.setActivePanel(panel);
31885         }, this);
31886         if(this.config.closeOnTab){
31887             ti.on("beforeclose", function(t, e){
31888                 e.cancel = true;
31889                 this.remove(panel);
31890             }, this);
31891         }
31892         return ti;
31893     },
31894
31895     updatePanelTitle : function(panel, title){
31896         if(this.activePanel == panel){
31897             this.updateTitle(title);
31898         }
31899         if(this.tabs){
31900             var ti = this.tabs.getTab(panel.getEl().id);
31901             ti.setText(title);
31902             if(panel.tabTip !== undefined){
31903                 ti.setTooltip(panel.tabTip);
31904             }
31905         }
31906     },
31907
31908     updateTitle : function(title){
31909         if(this.titleTextEl && !this.config.title){
31910             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31911         }
31912     },
31913
31914     setActivePanel : function(panel){
31915         panel = this.getPanel(panel);
31916         if(this.activePanel && this.activePanel != panel){
31917             this.activePanel.setActiveState(false);
31918         }
31919         this.activePanel = panel;
31920         panel.setActiveState(true);
31921         if(this.panelSize){
31922             panel.setSize(this.panelSize.width, this.panelSize.height);
31923         }
31924         if(this.closeBtn){
31925             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31926         }
31927         this.updateTitle(panel.getTitle());
31928         if(this.tabs){
31929             this.fireEvent("invalidated", this);
31930         }
31931         this.fireEvent("panelactivated", this, panel);
31932     },
31933
31934     /**
31935      * Shows the specified panel.
31936      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31937      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31938      */
31939     showPanel : function(panel)
31940     {
31941         panel = this.getPanel(panel);
31942         if(panel){
31943             if(this.tabs){
31944                 var tab = this.tabs.getTab(panel.getEl().id);
31945                 if(tab.isHidden()){
31946                     this.tabs.unhideTab(tab.id);
31947                 }
31948                 tab.activate();
31949             }else{
31950                 this.setActivePanel(panel);
31951             }
31952         }
31953         return panel;
31954     },
31955
31956     /**
31957      * Get the active panel for this region.
31958      * @return {Roo.ContentPanel} The active panel or null
31959      */
31960     getActivePanel : function(){
31961         return this.activePanel;
31962     },
31963
31964     validateVisibility : function(){
31965         if(this.panels.getCount() < 1){
31966             this.updateTitle("&#160;");
31967             this.closeBtn.hide();
31968             this.hide();
31969         }else{
31970             if(!this.isVisible()){
31971                 this.show();
31972             }
31973         }
31974     },
31975
31976     /**
31977      * Adds the passed ContentPanel(s) to this region.
31978      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31979      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31980      */
31981     add : function(panel){
31982         if(arguments.length > 1){
31983             for(var i = 0, len = arguments.length; i < len; i++) {
31984                 this.add(arguments[i]);
31985             }
31986             return null;
31987         }
31988         if(this.hasPanel(panel)){
31989             this.showPanel(panel);
31990             return panel;
31991         }
31992         panel.setRegion(this);
31993         this.panels.add(panel);
31994         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31995             this.bodyEl.dom.appendChild(panel.getEl().dom);
31996             if(panel.background !== true){
31997                 this.setActivePanel(panel);
31998             }
31999             this.fireEvent("paneladded", this, panel);
32000             return panel;
32001         }
32002         if(!this.tabs){
32003             this.initTabs();
32004         }else{
32005             this.initPanelAsTab(panel);
32006         }
32007         if(panel.background !== true){
32008             this.tabs.activate(panel.getEl().id);
32009         }
32010         this.fireEvent("paneladded", this, panel);
32011         return panel;
32012     },
32013
32014     /**
32015      * Hides the tab for the specified panel.
32016      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32017      */
32018     hidePanel : function(panel){
32019         if(this.tabs && (panel = this.getPanel(panel))){
32020             this.tabs.hideTab(panel.getEl().id);
32021         }
32022     },
32023
32024     /**
32025      * Unhides the tab for a previously hidden panel.
32026      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32027      */
32028     unhidePanel : function(panel){
32029         if(this.tabs && (panel = this.getPanel(panel))){
32030             this.tabs.unhideTab(panel.getEl().id);
32031         }
32032     },
32033
32034     clearPanels : function(){
32035         while(this.panels.getCount() > 0){
32036              this.remove(this.panels.first());
32037         }
32038     },
32039
32040     /**
32041      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32042      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32043      * @param {Boolean} preservePanel Overrides the config preservePanel option
32044      * @return {Roo.ContentPanel} The panel that was removed
32045      */
32046     remove : function(panel, preservePanel){
32047         panel = this.getPanel(panel);
32048         if(!panel){
32049             return null;
32050         }
32051         var e = {};
32052         this.fireEvent("beforeremove", this, panel, e);
32053         if(e.cancel === true){
32054             return null;
32055         }
32056         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32057         var panelId = panel.getId();
32058         this.panels.removeKey(panelId);
32059         if(preservePanel){
32060             document.body.appendChild(panel.getEl().dom);
32061         }
32062         if(this.tabs){
32063             this.tabs.removeTab(panel.getEl().id);
32064         }else if (!preservePanel){
32065             this.bodyEl.dom.removeChild(panel.getEl().dom);
32066         }
32067         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32068             var p = this.panels.first();
32069             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32070             tempEl.appendChild(p.getEl().dom);
32071             this.bodyEl.update("");
32072             this.bodyEl.dom.appendChild(p.getEl().dom);
32073             tempEl = null;
32074             this.updateTitle(p.getTitle());
32075             this.tabs = null;
32076             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32077             this.setActivePanel(p);
32078         }
32079         panel.setRegion(null);
32080         if(this.activePanel == panel){
32081             this.activePanel = null;
32082         }
32083         if(this.config.autoDestroy !== false && preservePanel !== true){
32084             try{panel.destroy();}catch(e){}
32085         }
32086         this.fireEvent("panelremoved", this, panel);
32087         return panel;
32088     },
32089
32090     /**
32091      * Returns the TabPanel component used by this region
32092      * @return {Roo.TabPanel}
32093      */
32094     getTabs : function(){
32095         return this.tabs;
32096     },
32097
32098     createTool : function(parentEl, className){
32099         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32100             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32101         btn.addClassOnOver("x-layout-tools-button-over");
32102         return btn;
32103     }
32104 });/*
32105  * Based on:
32106  * Ext JS Library 1.1.1
32107  * Copyright(c) 2006-2007, Ext JS, LLC.
32108  *
32109  * Originally Released Under LGPL - original licence link has changed is not relivant.
32110  *
32111  * Fork - LGPL
32112  * <script type="text/javascript">
32113  */
32114  
32115
32116
32117 /**
32118  * @class Roo.SplitLayoutRegion
32119  * @extends Roo.LayoutRegion
32120  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32121  */
32122 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32123     this.cursor = cursor;
32124     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32125 };
32126
32127 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32128     splitTip : "Drag to resize.",
32129     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32130     useSplitTips : false,
32131
32132     applyConfig : function(config){
32133         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32134         if(config.split){
32135             if(!this.split){
32136                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32137                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32138                 /** The SplitBar for this region 
32139                 * @type Roo.SplitBar */
32140                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32141                 this.split.on("moved", this.onSplitMove, this);
32142                 this.split.useShim = config.useShim === true;
32143                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32144                 if(this.useSplitTips){
32145                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32146                 }
32147                 if(config.collapsible){
32148                     this.split.el.on("dblclick", this.collapse,  this);
32149                 }
32150             }
32151             if(typeof config.minSize != "undefined"){
32152                 this.split.minSize = config.minSize;
32153             }
32154             if(typeof config.maxSize != "undefined"){
32155                 this.split.maxSize = config.maxSize;
32156             }
32157             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32158                 this.hideSplitter();
32159             }
32160         }
32161     },
32162
32163     getHMaxSize : function(){
32164          var cmax = this.config.maxSize || 10000;
32165          var center = this.mgr.getRegion("center");
32166          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32167     },
32168
32169     getVMaxSize : function(){
32170          var cmax = this.config.maxSize || 10000;
32171          var center = this.mgr.getRegion("center");
32172          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32173     },
32174
32175     onSplitMove : function(split, newSize){
32176         this.fireEvent("resized", this, newSize);
32177     },
32178     
32179     /** 
32180      * Returns the {@link Roo.SplitBar} for this region.
32181      * @return {Roo.SplitBar}
32182      */
32183     getSplitBar : function(){
32184         return this.split;
32185     },
32186     
32187     hide : function(){
32188         this.hideSplitter();
32189         Roo.SplitLayoutRegion.superclass.hide.call(this);
32190     },
32191
32192     hideSplitter : function(){
32193         if(this.split){
32194             this.split.el.setLocation(-2000,-2000);
32195             this.split.el.hide();
32196         }
32197     },
32198
32199     show : function(){
32200         if(this.split){
32201             this.split.el.show();
32202         }
32203         Roo.SplitLayoutRegion.superclass.show.call(this);
32204     },
32205     
32206     beforeSlide: function(){
32207         if(Roo.isGecko){// firefox overflow auto bug workaround
32208             this.bodyEl.clip();
32209             if(this.tabs) {
32210                 this.tabs.bodyEl.clip();
32211             }
32212             if(this.activePanel){
32213                 this.activePanel.getEl().clip();
32214                 
32215                 if(this.activePanel.beforeSlide){
32216                     this.activePanel.beforeSlide();
32217                 }
32218             }
32219         }
32220     },
32221     
32222     afterSlide : function(){
32223         if(Roo.isGecko){// firefox overflow auto bug workaround
32224             this.bodyEl.unclip();
32225             if(this.tabs) {
32226                 this.tabs.bodyEl.unclip();
32227             }
32228             if(this.activePanel){
32229                 this.activePanel.getEl().unclip();
32230                 if(this.activePanel.afterSlide){
32231                     this.activePanel.afterSlide();
32232                 }
32233             }
32234         }
32235     },
32236
32237     initAutoHide : function(){
32238         if(this.autoHide !== false){
32239             if(!this.autoHideHd){
32240                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32241                 this.autoHideHd = {
32242                     "mouseout": function(e){
32243                         if(!e.within(this.el, true)){
32244                             st.delay(500);
32245                         }
32246                     },
32247                     "mouseover" : function(e){
32248                         st.cancel();
32249                     },
32250                     scope : this
32251                 };
32252             }
32253             this.el.on(this.autoHideHd);
32254         }
32255     },
32256
32257     clearAutoHide : function(){
32258         if(this.autoHide !== false){
32259             this.el.un("mouseout", this.autoHideHd.mouseout);
32260             this.el.un("mouseover", this.autoHideHd.mouseover);
32261         }
32262     },
32263
32264     clearMonitor : function(){
32265         Roo.get(document).un("click", this.slideInIf, this);
32266     },
32267
32268     // these names are backwards but not changed for compat
32269     slideOut : function(){
32270         if(this.isSlid || this.el.hasActiveFx()){
32271             return;
32272         }
32273         this.isSlid = true;
32274         if(this.collapseBtn){
32275             this.collapseBtn.hide();
32276         }
32277         this.closeBtnState = this.closeBtn.getStyle('display');
32278         this.closeBtn.hide();
32279         if(this.stickBtn){
32280             this.stickBtn.show();
32281         }
32282         this.el.show();
32283         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32284         this.beforeSlide();
32285         this.el.setStyle("z-index", 10001);
32286         this.el.slideIn(this.getSlideAnchor(), {
32287             callback: function(){
32288                 this.afterSlide();
32289                 this.initAutoHide();
32290                 Roo.get(document).on("click", this.slideInIf, this);
32291                 this.fireEvent("slideshow", this);
32292             },
32293             scope: this,
32294             block: true
32295         });
32296     },
32297
32298     afterSlideIn : function(){
32299         this.clearAutoHide();
32300         this.isSlid = false;
32301         this.clearMonitor();
32302         this.el.setStyle("z-index", "");
32303         if(this.collapseBtn){
32304             this.collapseBtn.show();
32305         }
32306         this.closeBtn.setStyle('display', this.closeBtnState);
32307         if(this.stickBtn){
32308             this.stickBtn.hide();
32309         }
32310         this.fireEvent("slidehide", this);
32311     },
32312
32313     slideIn : function(cb){
32314         if(!this.isSlid || this.el.hasActiveFx()){
32315             Roo.callback(cb);
32316             return;
32317         }
32318         this.isSlid = false;
32319         this.beforeSlide();
32320         this.el.slideOut(this.getSlideAnchor(), {
32321             callback: function(){
32322                 this.el.setLeftTop(-10000, -10000);
32323                 this.afterSlide();
32324                 this.afterSlideIn();
32325                 Roo.callback(cb);
32326             },
32327             scope: this,
32328             block: true
32329         });
32330     },
32331     
32332     slideInIf : function(e){
32333         if(!e.within(this.el)){
32334             this.slideIn();
32335         }
32336     },
32337
32338     animateCollapse : function(){
32339         this.beforeSlide();
32340         this.el.setStyle("z-index", 20000);
32341         var anchor = this.getSlideAnchor();
32342         this.el.slideOut(anchor, {
32343             callback : function(){
32344                 this.el.setStyle("z-index", "");
32345                 this.collapsedEl.slideIn(anchor, {duration:.3});
32346                 this.afterSlide();
32347                 this.el.setLocation(-10000,-10000);
32348                 this.el.hide();
32349                 this.fireEvent("collapsed", this);
32350             },
32351             scope: this,
32352             block: true
32353         });
32354     },
32355
32356     animateExpand : function(){
32357         this.beforeSlide();
32358         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32359         this.el.setStyle("z-index", 20000);
32360         this.collapsedEl.hide({
32361             duration:.1
32362         });
32363         this.el.slideIn(this.getSlideAnchor(), {
32364             callback : function(){
32365                 this.el.setStyle("z-index", "");
32366                 this.afterSlide();
32367                 if(this.split){
32368                     this.split.el.show();
32369                 }
32370                 this.fireEvent("invalidated", this);
32371                 this.fireEvent("expanded", this);
32372             },
32373             scope: this,
32374             block: true
32375         });
32376     },
32377
32378     anchors : {
32379         "west" : "left",
32380         "east" : "right",
32381         "north" : "top",
32382         "south" : "bottom"
32383     },
32384
32385     sanchors : {
32386         "west" : "l",
32387         "east" : "r",
32388         "north" : "t",
32389         "south" : "b"
32390     },
32391
32392     canchors : {
32393         "west" : "tl-tr",
32394         "east" : "tr-tl",
32395         "north" : "tl-bl",
32396         "south" : "bl-tl"
32397     },
32398
32399     getAnchor : function(){
32400         return this.anchors[this.position];
32401     },
32402
32403     getCollapseAnchor : function(){
32404         return this.canchors[this.position];
32405     },
32406
32407     getSlideAnchor : function(){
32408         return this.sanchors[this.position];
32409     },
32410
32411     getAlignAdj : function(){
32412         var cm = this.cmargins;
32413         switch(this.position){
32414             case "west":
32415                 return [0, 0];
32416             break;
32417             case "east":
32418                 return [0, 0];
32419             break;
32420             case "north":
32421                 return [0, 0];
32422             break;
32423             case "south":
32424                 return [0, 0];
32425             break;
32426         }
32427     },
32428
32429     getExpandAdj : function(){
32430         var c = this.collapsedEl, cm = this.cmargins;
32431         switch(this.position){
32432             case "west":
32433                 return [-(cm.right+c.getWidth()+cm.left), 0];
32434             break;
32435             case "east":
32436                 return [cm.right+c.getWidth()+cm.left, 0];
32437             break;
32438             case "north":
32439                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32440             break;
32441             case "south":
32442                 return [0, cm.top+cm.bottom+c.getHeight()];
32443             break;
32444         }
32445     }
32446 });/*
32447  * Based on:
32448  * Ext JS Library 1.1.1
32449  * Copyright(c) 2006-2007, Ext JS, LLC.
32450  *
32451  * Originally Released Under LGPL - original licence link has changed is not relivant.
32452  *
32453  * Fork - LGPL
32454  * <script type="text/javascript">
32455  */
32456 /*
32457  * These classes are private internal classes
32458  */
32459 Roo.CenterLayoutRegion = function(mgr, config){
32460     Roo.LayoutRegion.call(this, mgr, config, "center");
32461     this.visible = true;
32462     this.minWidth = config.minWidth || 20;
32463     this.minHeight = config.minHeight || 20;
32464 };
32465
32466 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32467     hide : function(){
32468         // center panel can't be hidden
32469     },
32470     
32471     show : function(){
32472         // center panel can't be hidden
32473     },
32474     
32475     getMinWidth: function(){
32476         return this.minWidth;
32477     },
32478     
32479     getMinHeight: function(){
32480         return this.minHeight;
32481     }
32482 });
32483
32484
32485 Roo.NorthLayoutRegion = function(mgr, config){
32486     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32487     if(this.split){
32488         this.split.placement = Roo.SplitBar.TOP;
32489         this.split.orientation = Roo.SplitBar.VERTICAL;
32490         this.split.el.addClass("x-layout-split-v");
32491     }
32492     var size = config.initialSize || config.height;
32493     if(typeof size != "undefined"){
32494         this.el.setHeight(size);
32495     }
32496 };
32497 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32498     orientation: Roo.SplitBar.VERTICAL,
32499     getBox : function(){
32500         if(this.collapsed){
32501             return this.collapsedEl.getBox();
32502         }
32503         var box = this.el.getBox();
32504         if(this.split){
32505             box.height += this.split.el.getHeight();
32506         }
32507         return box;
32508     },
32509     
32510     updateBox : function(box){
32511         if(this.split && !this.collapsed){
32512             box.height -= this.split.el.getHeight();
32513             this.split.el.setLeft(box.x);
32514             this.split.el.setTop(box.y+box.height);
32515             this.split.el.setWidth(box.width);
32516         }
32517         if(this.collapsed){
32518             this.updateBody(box.width, null);
32519         }
32520         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32521     }
32522 });
32523
32524 Roo.SouthLayoutRegion = function(mgr, config){
32525     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32526     if(this.split){
32527         this.split.placement = Roo.SplitBar.BOTTOM;
32528         this.split.orientation = Roo.SplitBar.VERTICAL;
32529         this.split.el.addClass("x-layout-split-v");
32530     }
32531     var size = config.initialSize || config.height;
32532     if(typeof size != "undefined"){
32533         this.el.setHeight(size);
32534     }
32535 };
32536 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32537     orientation: Roo.SplitBar.VERTICAL,
32538     getBox : function(){
32539         if(this.collapsed){
32540             return this.collapsedEl.getBox();
32541         }
32542         var box = this.el.getBox();
32543         if(this.split){
32544             var sh = this.split.el.getHeight();
32545             box.height += sh;
32546             box.y -= sh;
32547         }
32548         return box;
32549     },
32550     
32551     updateBox : function(box){
32552         if(this.split && !this.collapsed){
32553             var sh = this.split.el.getHeight();
32554             box.height -= sh;
32555             box.y += sh;
32556             this.split.el.setLeft(box.x);
32557             this.split.el.setTop(box.y-sh);
32558             this.split.el.setWidth(box.width);
32559         }
32560         if(this.collapsed){
32561             this.updateBody(box.width, null);
32562         }
32563         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32564     }
32565 });
32566
32567 Roo.EastLayoutRegion = function(mgr, config){
32568     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32569     if(this.split){
32570         this.split.placement = Roo.SplitBar.RIGHT;
32571         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32572         this.split.el.addClass("x-layout-split-h");
32573     }
32574     var size = config.initialSize || config.width;
32575     if(typeof size != "undefined"){
32576         this.el.setWidth(size);
32577     }
32578 };
32579 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32580     orientation: Roo.SplitBar.HORIZONTAL,
32581     getBox : function(){
32582         if(this.collapsed){
32583             return this.collapsedEl.getBox();
32584         }
32585         var box = this.el.getBox();
32586         if(this.split){
32587             var sw = this.split.el.getWidth();
32588             box.width += sw;
32589             box.x -= sw;
32590         }
32591         return box;
32592     },
32593
32594     updateBox : function(box){
32595         if(this.split && !this.collapsed){
32596             var sw = this.split.el.getWidth();
32597             box.width -= sw;
32598             this.split.el.setLeft(box.x);
32599             this.split.el.setTop(box.y);
32600             this.split.el.setHeight(box.height);
32601             box.x += sw;
32602         }
32603         if(this.collapsed){
32604             this.updateBody(null, box.height);
32605         }
32606         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32607     }
32608 });
32609
32610 Roo.WestLayoutRegion = function(mgr, config){
32611     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32612     if(this.split){
32613         this.split.placement = Roo.SplitBar.LEFT;
32614         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32615         this.split.el.addClass("x-layout-split-h");
32616     }
32617     var size = config.initialSize || config.width;
32618     if(typeof size != "undefined"){
32619         this.el.setWidth(size);
32620     }
32621 };
32622 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32623     orientation: Roo.SplitBar.HORIZONTAL,
32624     getBox : function(){
32625         if(this.collapsed){
32626             return this.collapsedEl.getBox();
32627         }
32628         var box = this.el.getBox();
32629         if(this.split){
32630             box.width += this.split.el.getWidth();
32631         }
32632         return box;
32633     },
32634     
32635     updateBox : function(box){
32636         if(this.split && !this.collapsed){
32637             var sw = this.split.el.getWidth();
32638             box.width -= sw;
32639             this.split.el.setLeft(box.x+box.width);
32640             this.split.el.setTop(box.y);
32641             this.split.el.setHeight(box.height);
32642         }
32643         if(this.collapsed){
32644             this.updateBody(null, box.height);
32645         }
32646         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32647     }
32648 });
32649 /*
32650  * Based on:
32651  * Ext JS Library 1.1.1
32652  * Copyright(c) 2006-2007, Ext JS, LLC.
32653  *
32654  * Originally Released Under LGPL - original licence link has changed is not relivant.
32655  *
32656  * Fork - LGPL
32657  * <script type="text/javascript">
32658  */
32659  
32660  
32661 /*
32662  * Private internal class for reading and applying state
32663  */
32664 Roo.LayoutStateManager = function(layout){
32665      // default empty state
32666      this.state = {
32667         north: {},
32668         south: {},
32669         east: {},
32670         west: {}       
32671     };
32672 };
32673
32674 Roo.LayoutStateManager.prototype = {
32675     init : function(layout, provider){
32676         this.provider = provider;
32677         var state = provider.get(layout.id+"-layout-state");
32678         if(state){
32679             var wasUpdating = layout.isUpdating();
32680             if(!wasUpdating){
32681                 layout.beginUpdate();
32682             }
32683             for(var key in state){
32684                 if(typeof state[key] != "function"){
32685                     var rstate = state[key];
32686                     var r = layout.getRegion(key);
32687                     if(r && rstate){
32688                         if(rstate.size){
32689                             r.resizeTo(rstate.size);
32690                         }
32691                         if(rstate.collapsed == true){
32692                             r.collapse(true);
32693                         }else{
32694                             r.expand(null, true);
32695                         }
32696                     }
32697                 }
32698             }
32699             if(!wasUpdating){
32700                 layout.endUpdate();
32701             }
32702             this.state = state; 
32703         }
32704         this.layout = layout;
32705         layout.on("regionresized", this.onRegionResized, this);
32706         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32707         layout.on("regionexpanded", this.onRegionExpanded, this);
32708     },
32709     
32710     storeState : function(){
32711         this.provider.set(this.layout.id+"-layout-state", this.state);
32712     },
32713     
32714     onRegionResized : function(region, newSize){
32715         this.state[region.getPosition()].size = newSize;
32716         this.storeState();
32717     },
32718     
32719     onRegionCollapsed : function(region){
32720         this.state[region.getPosition()].collapsed = true;
32721         this.storeState();
32722     },
32723     
32724     onRegionExpanded : function(region){
32725         this.state[region.getPosition()].collapsed = false;
32726         this.storeState();
32727     }
32728 };/*
32729  * Based on:
32730  * Ext JS Library 1.1.1
32731  * Copyright(c) 2006-2007, Ext JS, LLC.
32732  *
32733  * Originally Released Under LGPL - original licence link has changed is not relivant.
32734  *
32735  * Fork - LGPL
32736  * <script type="text/javascript">
32737  */
32738 /**
32739  * @class Roo.ContentPanel
32740  * @extends Roo.util.Observable
32741  * @children Roo.form.Form Roo.JsonView Roo.View
32742  * @parent Roo.BorderLayout Roo.LayoutDialog builder
32743  * A basic ContentPanel element.
32744  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32745  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32746  * @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
32747  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32748  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32749  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32750  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
32751  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32752  * @cfg {String} title          The title for this panel
32753  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32754  * @cfg {String} url            Calls {@link #setUrl} with this value
32755  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
32756  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32757  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32758  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32759  * @cfg {String}    style  Extra style to add to the content panel
32760  * @cfg {Roo.menu.Menu} menu  popup menu
32761
32762  * @constructor
32763  * Create a new ContentPanel.
32764  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32765  * @param {String/Object} config A string to set only the title or a config object
32766  * @param {String} content (optional) Set the HTML content for this panel
32767  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32768  */
32769 Roo.ContentPanel = function(el, config, content){
32770     
32771      
32772     /*
32773     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32774         config = el;
32775         el = Roo.id();
32776     }
32777     if (config && config.parentLayout) { 
32778         el = config.parentLayout.el.createChild(); 
32779     }
32780     */
32781     if(el.autoCreate){ // xtype is available if this is called from factory
32782         config = el;
32783         el = Roo.id();
32784     }
32785     this.el = Roo.get(el);
32786     if(!this.el && config && config.autoCreate){
32787         if(typeof config.autoCreate == "object"){
32788             if(!config.autoCreate.id){
32789                 config.autoCreate.id = config.id||el;
32790             }
32791             this.el = Roo.DomHelper.append(document.body,
32792                         config.autoCreate, true);
32793         }else{
32794             this.el = Roo.DomHelper.append(document.body,
32795                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32796         }
32797     }
32798     
32799     
32800     this.closable = false;
32801     this.loaded = false;
32802     this.active = false;
32803     if(typeof config == "string"){
32804         this.title = config;
32805     }else{
32806         Roo.apply(this, config);
32807     }
32808     
32809     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32810         this.wrapEl = this.el.wrap();
32811         this.toolbar.container = this.el.insertSibling(false, 'before');
32812         this.toolbar = new Roo.Toolbar(this.toolbar);
32813     }
32814     
32815     // xtype created footer. - not sure if will work as we normally have to render first..
32816     if (this.footer && !this.footer.el && this.footer.xtype) {
32817         if (!this.wrapEl) {
32818             this.wrapEl = this.el.wrap();
32819         }
32820     
32821         this.footer.container = this.wrapEl.createChild();
32822          
32823         this.footer = Roo.factory(this.footer, Roo);
32824         
32825     }
32826     
32827     if(this.resizeEl){
32828         this.resizeEl = Roo.get(this.resizeEl, true);
32829     }else{
32830         this.resizeEl = this.el;
32831     }
32832     // handle view.xtype
32833     
32834  
32835     
32836     
32837     this.addEvents({
32838         /**
32839          * @event activate
32840          * Fires when this panel is activated. 
32841          * @param {Roo.ContentPanel} this
32842          */
32843         "activate" : true,
32844         /**
32845          * @event deactivate
32846          * Fires when this panel is activated. 
32847          * @param {Roo.ContentPanel} this
32848          */
32849         "deactivate" : true,
32850
32851         /**
32852          * @event resize
32853          * Fires when this panel is resized if fitToFrame is true.
32854          * @param {Roo.ContentPanel} this
32855          * @param {Number} width The width after any component adjustments
32856          * @param {Number} height The height after any component adjustments
32857          */
32858         "resize" : true,
32859         
32860          /**
32861          * @event render
32862          * Fires when this tab is created
32863          * @param {Roo.ContentPanel} this
32864          */
32865         "render" : true
32866          
32867         
32868     });
32869     
32870
32871     
32872     
32873     if(this.autoScroll){
32874         this.resizeEl.setStyle("overflow", "auto");
32875     } else {
32876         // fix randome scrolling
32877         this.el.on('scroll', function() {
32878             Roo.log('fix random scolling');
32879             this.scrollTo('top',0); 
32880         });
32881     }
32882     content = content || this.content;
32883     if(content){
32884         this.setContent(content);
32885     }
32886     if(config && config.url){
32887         this.setUrl(this.url, this.params, this.loadOnce);
32888     }
32889     
32890     
32891     
32892     Roo.ContentPanel.superclass.constructor.call(this);
32893     
32894     if (this.view && typeof(this.view.xtype) != 'undefined') {
32895         this.view.el = this.el.appendChild(document.createElement("div"));
32896         this.view = Roo.factory(this.view); 
32897         this.view.render  &&  this.view.render(false, '');  
32898     }
32899     
32900     
32901     this.fireEvent('render', this);
32902 };
32903
32904 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32905     tabTip:'',
32906     setRegion : function(region){
32907         this.region = region;
32908         if(region){
32909            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32910         }else{
32911            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32912         } 
32913     },
32914     
32915     /**
32916      * Returns the toolbar for this Panel if one was configured. 
32917      * @return {Roo.Toolbar} 
32918      */
32919     getToolbar : function(){
32920         return this.toolbar;
32921     },
32922     
32923     setActiveState : function(active){
32924         this.active = active;
32925         if(!active){
32926             this.fireEvent("deactivate", this);
32927         }else{
32928             this.fireEvent("activate", this);
32929         }
32930     },
32931     /**
32932      * Updates this panel's element
32933      * @param {String} content The new content
32934      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32935     */
32936     setContent : function(content, loadScripts){
32937         this.el.update(content, loadScripts);
32938     },
32939
32940     ignoreResize : function(w, h){
32941         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32942             return true;
32943         }else{
32944             this.lastSize = {width: w, height: h};
32945             return false;
32946         }
32947     },
32948     /**
32949      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32950      * @return {Roo.UpdateManager} The UpdateManager
32951      */
32952     getUpdateManager : function(){
32953         return this.el.getUpdateManager();
32954     },
32955      /**
32956      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32957      * @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:
32958 <pre><code>
32959 panel.load({
32960     url: "your-url.php",
32961     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32962     callback: yourFunction,
32963     scope: yourObject, //(optional scope)
32964     discardUrl: false,
32965     nocache: false,
32966     text: "Loading...",
32967     timeout: 30,
32968     scripts: false
32969 });
32970 </code></pre>
32971      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32972      * 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.
32973      * @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}
32974      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32975      * @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.
32976      * @return {Roo.ContentPanel} this
32977      */
32978     load : function(){
32979         var um = this.el.getUpdateManager();
32980         um.update.apply(um, arguments);
32981         return this;
32982     },
32983
32984
32985     /**
32986      * 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.
32987      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32988      * @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)
32989      * @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)
32990      * @return {Roo.UpdateManager} The UpdateManager
32991      */
32992     setUrl : function(url, params, loadOnce){
32993         if(this.refreshDelegate){
32994             this.removeListener("activate", this.refreshDelegate);
32995         }
32996         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32997         this.on("activate", this.refreshDelegate);
32998         return this.el.getUpdateManager();
32999     },
33000     
33001     _handleRefresh : function(url, params, loadOnce){
33002         if(!loadOnce || !this.loaded){
33003             var updater = this.el.getUpdateManager();
33004             updater.update(url, params, this._setLoaded.createDelegate(this));
33005         }
33006     },
33007     
33008     _setLoaded : function(){
33009         this.loaded = true;
33010     }, 
33011     
33012     /**
33013      * Returns this panel's id
33014      * @return {String} 
33015      */
33016     getId : function(){
33017         return this.el.id;
33018     },
33019     
33020     /** 
33021      * Returns this panel's element - used by regiosn to add.
33022      * @return {Roo.Element} 
33023      */
33024     getEl : function(){
33025         return this.wrapEl || this.el;
33026     },
33027     
33028     adjustForComponents : function(width, height)
33029     {
33030         //Roo.log('adjustForComponents ');
33031         if(this.resizeEl != this.el){
33032             width -= this.el.getFrameWidth('lr');
33033             height -= this.el.getFrameWidth('tb');
33034         }
33035         if(this.toolbar){
33036             var te = this.toolbar.getEl();
33037             height -= te.getHeight();
33038             te.setWidth(width);
33039         }
33040         if(this.footer){
33041             var te = this.footer.getEl();
33042             //Roo.log("footer:" + te.getHeight());
33043             
33044             height -= te.getHeight();
33045             te.setWidth(width);
33046         }
33047         
33048         
33049         if(this.adjustments){
33050             width += this.adjustments[0];
33051             height += this.adjustments[1];
33052         }
33053         return {"width": width, "height": height};
33054     },
33055     
33056     setSize : function(width, height){
33057         if(this.fitToFrame && !this.ignoreResize(width, height)){
33058             if(this.fitContainer && this.resizeEl != this.el){
33059                 this.el.setSize(width, height);
33060             }
33061             var size = this.adjustForComponents(width, height);
33062             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33063             this.fireEvent('resize', this, size.width, size.height);
33064         }
33065     },
33066     
33067     /**
33068      * Returns this panel's title
33069      * @return {String} 
33070      */
33071     getTitle : function(){
33072         return this.title;
33073     },
33074     
33075     /**
33076      * Set this panel's title
33077      * @param {String} title
33078      */
33079     setTitle : function(title){
33080         this.title = title;
33081         if(this.region){
33082             this.region.updatePanelTitle(this, title);
33083         }
33084     },
33085     
33086     /**
33087      * Returns true is this panel was configured to be closable
33088      * @return {Boolean} 
33089      */
33090     isClosable : function(){
33091         return this.closable;
33092     },
33093     
33094     beforeSlide : function(){
33095         this.el.clip();
33096         this.resizeEl.clip();
33097     },
33098     
33099     afterSlide : function(){
33100         this.el.unclip();
33101         this.resizeEl.unclip();
33102     },
33103     
33104     /**
33105      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33106      *   Will fail silently if the {@link #setUrl} method has not been called.
33107      *   This does not activate the panel, just updates its content.
33108      */
33109     refresh : function(){
33110         if(this.refreshDelegate){
33111            this.loaded = false;
33112            this.refreshDelegate();
33113         }
33114     },
33115     
33116     /**
33117      * Destroys this panel
33118      */
33119     destroy : function(){
33120         this.el.removeAllListeners();
33121         var tempEl = document.createElement("span");
33122         tempEl.appendChild(this.el.dom);
33123         tempEl.innerHTML = "";
33124         this.el.remove();
33125         this.el = null;
33126     },
33127     
33128     /**
33129      * form - if the content panel contains a form - this is a reference to it.
33130      * @type {Roo.form.Form}
33131      */
33132     form : false,
33133     /**
33134      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33135      *    This contains a reference to it.
33136      * @type {Roo.View}
33137      */
33138     view : false,
33139     
33140       /**
33141      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33142      * <pre><code>
33143
33144 layout.addxtype({
33145        xtype : 'Form',
33146        items: [ .... ]
33147    }
33148 );
33149
33150 </code></pre>
33151      * @param {Object} cfg Xtype definition of item to add.
33152      */
33153     
33154     addxtype : function(cfg) {
33155         // add form..
33156         if (cfg.xtype.match(/^Form$/)) {
33157             
33158             var el;
33159             //if (this.footer) {
33160             //    el = this.footer.container.insertSibling(false, 'before');
33161             //} else {
33162                 el = this.el.createChild();
33163             //}
33164
33165             this.form = new  Roo.form.Form(cfg);
33166             
33167             
33168             if ( this.form.allItems.length) {
33169                 this.form.render(el.dom);
33170             }
33171             return this.form;
33172         }
33173         // should only have one of theses..
33174         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33175             // views.. should not be just added - used named prop 'view''
33176             
33177             cfg.el = this.el.appendChild(document.createElement("div"));
33178             // factory?
33179             
33180             var ret = new Roo.factory(cfg);
33181              
33182              ret.render && ret.render(false, ''); // render blank..
33183             this.view = ret;
33184             return ret;
33185         }
33186         return false;
33187     }
33188 });
33189
33190 /**
33191  * @class Roo.GridPanel
33192  * @extends Roo.ContentPanel
33193  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33194  * @constructor
33195  * Create a new GridPanel.
33196  * @cfg {Roo.grid.Grid} grid The grid for this panel
33197  */
33198 Roo.GridPanel = function(grid, config){
33199     
33200     // universal ctor...
33201     if (typeof(grid.grid) != 'undefined') {
33202         config = grid;
33203         grid = config.grid;
33204     }
33205     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33206         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33207         
33208     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33209     
33210     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33211     
33212     if(this.toolbar){
33213         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33214     }
33215     // xtype created footer. - not sure if will work as we normally have to render first..
33216     if (this.footer && !this.footer.el && this.footer.xtype) {
33217         
33218         this.footer.container = this.grid.getView().getFooterPanel(true);
33219         this.footer.dataSource = this.grid.dataSource;
33220         this.footer = Roo.factory(this.footer, Roo);
33221         
33222     }
33223     
33224     grid.monitorWindowResize = false; // turn off autosizing
33225     grid.autoHeight = false;
33226     grid.autoWidth = false;
33227     this.grid = grid;
33228     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33229 };
33230
33231 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33232     getId : function(){
33233         return this.grid.id;
33234     },
33235     
33236     /**
33237      * Returns the grid for this panel
33238      * @return {Roo.grid.Grid} 
33239      */
33240     getGrid : function(){
33241         return this.grid;    
33242     },
33243     
33244     setSize : function(width, height){
33245         if(!this.ignoreResize(width, height)){
33246             var grid = this.grid;
33247             var size = this.adjustForComponents(width, height);
33248             grid.getGridEl().setSize(size.width, size.height);
33249             grid.autoSize();
33250         }
33251     },
33252     
33253     beforeSlide : function(){
33254         this.grid.getView().scroller.clip();
33255     },
33256     
33257     afterSlide : function(){
33258         this.grid.getView().scroller.unclip();
33259     },
33260     
33261     destroy : function(){
33262         this.grid.destroy();
33263         delete this.grid;
33264         Roo.GridPanel.superclass.destroy.call(this); 
33265     }
33266 });
33267
33268
33269 /**
33270  * @class Roo.NestedLayoutPanel
33271  * @extends Roo.ContentPanel
33272  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33273  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
33274  *
33275  * 
33276  * @constructor
33277  * Create a new NestedLayoutPanel.
33278  * 
33279  * 
33280  * @param {Roo.BorderLayout} layout [required] The layout for this panel
33281  * @param {String/Object} config A string to set only the title or a config object
33282  */
33283 Roo.NestedLayoutPanel = function(layout, config)
33284 {
33285     // construct with only one argument..
33286     /* FIXME - implement nicer consturctors
33287     if (layout.layout) {
33288         config = layout;
33289         layout = config.layout;
33290         delete config.layout;
33291     }
33292     if (layout.xtype && !layout.getEl) {
33293         // then layout needs constructing..
33294         layout = Roo.factory(layout, Roo);
33295     }
33296     */
33297     
33298     
33299     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33300     
33301     layout.monitorWindowResize = false; // turn off autosizing
33302     this.layout = layout;
33303     this.layout.getEl().addClass("x-layout-nested-layout");
33304     
33305     
33306     
33307     
33308 };
33309
33310 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33311
33312     setSize : function(width, height){
33313         if(!this.ignoreResize(width, height)){
33314             var size = this.adjustForComponents(width, height);
33315             var el = this.layout.getEl();
33316             el.setSize(size.width, size.height);
33317             var touch = el.dom.offsetWidth;
33318             this.layout.layout();
33319             // ie requires a double layout on the first pass
33320             if(Roo.isIE && !this.initialized){
33321                 this.initialized = true;
33322                 this.layout.layout();
33323             }
33324         }
33325     },
33326     
33327     // activate all subpanels if not currently active..
33328     
33329     setActiveState : function(active){
33330         this.active = active;
33331         if(!active){
33332             this.fireEvent("deactivate", this);
33333             return;
33334         }
33335         
33336         this.fireEvent("activate", this);
33337         // not sure if this should happen before or after..
33338         if (!this.layout) {
33339             return; // should not happen..
33340         }
33341         var reg = false;
33342         for (var r in this.layout.regions) {
33343             reg = this.layout.getRegion(r);
33344             if (reg.getActivePanel()) {
33345                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33346                 reg.setActivePanel(reg.getActivePanel());
33347                 continue;
33348             }
33349             if (!reg.panels.length) {
33350                 continue;
33351             }
33352             reg.showPanel(reg.getPanel(0));
33353         }
33354         
33355         
33356         
33357         
33358     },
33359     
33360     /**
33361      * Returns the nested BorderLayout for this panel
33362      * @return {Roo.BorderLayout} 
33363      */
33364     getLayout : function(){
33365         return this.layout;
33366     },
33367     
33368      /**
33369      * Adds a xtype elements to the layout of the nested panel
33370      * <pre><code>
33371
33372 panel.addxtype({
33373        xtype : 'ContentPanel',
33374        region: 'west',
33375        items: [ .... ]
33376    }
33377 );
33378
33379 panel.addxtype({
33380         xtype : 'NestedLayoutPanel',
33381         region: 'west',
33382         layout: {
33383            center: { },
33384            west: { }   
33385         },
33386         items : [ ... list of content panels or nested layout panels.. ]
33387    }
33388 );
33389 </code></pre>
33390      * @param {Object} cfg Xtype definition of item to add.
33391      */
33392     addxtype : function(cfg) {
33393         return this.layout.addxtype(cfg);
33394     
33395     }
33396 });
33397
33398 Roo.ScrollPanel = function(el, config, content){
33399     config = config || {};
33400     config.fitToFrame = true;
33401     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33402     
33403     this.el.dom.style.overflow = "hidden";
33404     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33405     this.el.removeClass("x-layout-inactive-content");
33406     this.el.on("mousewheel", this.onWheel, this);
33407
33408     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33409     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33410     up.unselectable(); down.unselectable();
33411     up.on("click", this.scrollUp, this);
33412     down.on("click", this.scrollDown, this);
33413     up.addClassOnOver("x-scroller-btn-over");
33414     down.addClassOnOver("x-scroller-btn-over");
33415     up.addClassOnClick("x-scroller-btn-click");
33416     down.addClassOnClick("x-scroller-btn-click");
33417     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33418
33419     this.resizeEl = this.el;
33420     this.el = wrap; this.up = up; this.down = down;
33421 };
33422
33423 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33424     increment : 100,
33425     wheelIncrement : 5,
33426     scrollUp : function(){
33427         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33428     },
33429
33430     scrollDown : function(){
33431         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33432     },
33433
33434     afterScroll : function(){
33435         var el = this.resizeEl;
33436         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33437         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33438         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33439     },
33440
33441     setSize : function(){
33442         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33443         this.afterScroll();
33444     },
33445
33446     onWheel : function(e){
33447         var d = e.getWheelDelta();
33448         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33449         this.afterScroll();
33450         e.stopEvent();
33451     },
33452
33453     setContent : function(content, loadScripts){
33454         this.resizeEl.update(content, loadScripts);
33455     }
33456
33457 });
33458
33459
33460
33461 /**
33462  * @class Roo.TreePanel
33463  * @extends Roo.ContentPanel
33464  * @parent Roo.BorderLayout Roo.LayoutDialog builder
33465  * Treepanel component
33466  * 
33467  * @constructor
33468  * Create a new TreePanel. - defaults to fit/scoll contents.
33469  * @param {String/Object} config A string to set only the panel's title, or a config object
33470  */
33471 Roo.TreePanel = function(config){
33472     var el = config.el;
33473     var tree = config.tree;
33474     delete config.tree; 
33475     delete config.el; // hopefull!
33476     
33477     // wrapper for IE7 strict & safari scroll issue
33478     
33479     var treeEl = el.createChild();
33480     config.resizeEl = treeEl;
33481     
33482     
33483     
33484     Roo.TreePanel.superclass.constructor.call(this, el, config);
33485  
33486  
33487     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33488     //console.log(tree);
33489     this.on('activate', function()
33490     {
33491         if (this.tree.rendered) {
33492             return;
33493         }
33494         //console.log('render tree');
33495         this.tree.render();
33496     });
33497     // this should not be needed.. - it's actually the 'el' that resizes?
33498     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33499     
33500     //this.on('resize',  function (cp, w, h) {
33501     //        this.tree.innerCt.setWidth(w);
33502     //        this.tree.innerCt.setHeight(h);
33503     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33504     //});
33505
33506         
33507     
33508 };
33509
33510 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33511     fitToFrame : true,
33512     autoScroll : true,
33513     /*
33514      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
33515      */
33516     tree : false
33517
33518 });
33519
33520
33521
33522
33523
33524
33525
33526
33527
33528
33529
33530 /*
33531  * Based on:
33532  * Ext JS Library 1.1.1
33533  * Copyright(c) 2006-2007, Ext JS, LLC.
33534  *
33535  * Originally Released Under LGPL - original licence link has changed is not relivant.
33536  *
33537  * Fork - LGPL
33538  * <script type="text/javascript">
33539  */
33540  
33541
33542 /**
33543  * @class Roo.ReaderLayout
33544  * @extends Roo.BorderLayout
33545  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33546  * center region containing two nested regions (a top one for a list view and one for item preview below),
33547  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33548  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33549  * expedites the setup of the overall layout and regions for this common application style.
33550  * Example:
33551  <pre><code>
33552 var reader = new Roo.ReaderLayout();
33553 var CP = Roo.ContentPanel;  // shortcut for adding
33554
33555 reader.beginUpdate();
33556 reader.add("north", new CP("north", "North"));
33557 reader.add("west", new CP("west", {title: "West"}));
33558 reader.add("east", new CP("east", {title: "East"}));
33559
33560 reader.regions.listView.add(new CP("listView", "List"));
33561 reader.regions.preview.add(new CP("preview", "Preview"));
33562 reader.endUpdate();
33563 </code></pre>
33564 * @constructor
33565 * Create a new ReaderLayout
33566 * @param {Object} config Configuration options
33567 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33568 * document.body if omitted)
33569 */
33570 Roo.ReaderLayout = function(config, renderTo){
33571     var c = config || {size:{}};
33572     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33573         north: c.north !== false ? Roo.apply({
33574             split:false,
33575             initialSize: 32,
33576             titlebar: false
33577         }, c.north) : false,
33578         west: c.west !== false ? Roo.apply({
33579             split:true,
33580             initialSize: 200,
33581             minSize: 175,
33582             maxSize: 400,
33583             titlebar: true,
33584             collapsible: true,
33585             animate: true,
33586             margins:{left:5,right:0,bottom:5,top:5},
33587             cmargins:{left:5,right:5,bottom:5,top:5}
33588         }, c.west) : false,
33589         east: c.east !== false ? Roo.apply({
33590             split:true,
33591             initialSize: 200,
33592             minSize: 175,
33593             maxSize: 400,
33594             titlebar: true,
33595             collapsible: true,
33596             animate: true,
33597             margins:{left:0,right:5,bottom:5,top:5},
33598             cmargins:{left:5,right:5,bottom:5,top:5}
33599         }, c.east) : false,
33600         center: Roo.apply({
33601             tabPosition: 'top',
33602             autoScroll:false,
33603             closeOnTab: true,
33604             titlebar:false,
33605             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33606         }, c.center)
33607     });
33608
33609     this.el.addClass('x-reader');
33610
33611     this.beginUpdate();
33612
33613     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33614         south: c.preview !== false ? Roo.apply({
33615             split:true,
33616             initialSize: 200,
33617             minSize: 100,
33618             autoScroll:true,
33619             collapsible:true,
33620             titlebar: true,
33621             cmargins:{top:5,left:0, right:0, bottom:0}
33622         }, c.preview) : false,
33623         center: Roo.apply({
33624             autoScroll:false,
33625             titlebar:false,
33626             minHeight:200
33627         }, c.listView)
33628     });
33629     this.add('center', new Roo.NestedLayoutPanel(inner,
33630             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33631
33632     this.endUpdate();
33633
33634     this.regions.preview = inner.getRegion('south');
33635     this.regions.listView = inner.getRegion('center');
33636 };
33637
33638 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33639  * Based on:
33640  * Ext JS Library 1.1.1
33641  * Copyright(c) 2006-2007, Ext JS, LLC.
33642  *
33643  * Originally Released Under LGPL - original licence link has changed is not relivant.
33644  *
33645  * Fork - LGPL
33646  * <script type="text/javascript">
33647  */
33648  
33649 /**
33650  * @class Roo.grid.Grid
33651  * @extends Roo.util.Observable
33652  * This class represents the primary interface of a component based grid control.
33653  * <br><br>Usage:<pre><code>
33654  var grid = new Roo.grid.Grid("my-container-id", {
33655      ds: myDataStore,
33656      cm: myColModel,
33657      selModel: mySelectionModel,
33658      autoSizeColumns: true,
33659      monitorWindowResize: false,
33660      trackMouseOver: true
33661  });
33662  // set any options
33663  grid.render();
33664  * </code></pre>
33665  * <b>Common Problems:</b><br/>
33666  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33667  * element will correct this<br/>
33668  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33669  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33670  * are unpredictable.<br/>
33671  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33672  * grid to calculate dimensions/offsets.<br/>
33673   * @constructor
33674  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33675  * The container MUST have some type of size defined for the grid to fill. The container will be
33676  * automatically set to position relative if it isn't already.
33677  * @param {Object} config A config object that sets properties on this grid.
33678  */
33679 Roo.grid.Grid = function(container, config){
33680         // initialize the container
33681         this.container = Roo.get(container);
33682         this.container.update("");
33683         this.container.setStyle("overflow", "hidden");
33684     this.container.addClass('x-grid-container');
33685
33686     this.id = this.container.id;
33687
33688     Roo.apply(this, config);
33689     // check and correct shorthanded configs
33690     if(this.ds){
33691         this.dataSource = this.ds;
33692         delete this.ds;
33693     }
33694     if(this.cm){
33695         this.colModel = this.cm;
33696         delete this.cm;
33697     }
33698     if(this.sm){
33699         this.selModel = this.sm;
33700         delete this.sm;
33701     }
33702
33703     if (this.selModel) {
33704         this.selModel = Roo.factory(this.selModel, Roo.grid);
33705         this.sm = this.selModel;
33706         this.sm.xmodule = this.xmodule || false;
33707     }
33708     if (typeof(this.colModel.config) == 'undefined') {
33709         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33710         this.cm = this.colModel;
33711         this.cm.xmodule = this.xmodule || false;
33712     }
33713     if (this.dataSource) {
33714         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33715         this.ds = this.dataSource;
33716         this.ds.xmodule = this.xmodule || false;
33717          
33718     }
33719     
33720     
33721     
33722     if(this.width){
33723         this.container.setWidth(this.width);
33724     }
33725
33726     if(this.height){
33727         this.container.setHeight(this.height);
33728     }
33729     /** @private */
33730         this.addEvents({
33731         // raw events
33732         /**
33733          * @event click
33734          * The raw click event for the entire grid.
33735          * @param {Roo.EventObject} e
33736          */
33737         "click" : true,
33738         /**
33739          * @event dblclick
33740          * The raw dblclick event for the entire grid.
33741          * @param {Roo.EventObject} e
33742          */
33743         "dblclick" : true,
33744         /**
33745          * @event contextmenu
33746          * The raw contextmenu event for the entire grid.
33747          * @param {Roo.EventObject} e
33748          */
33749         "contextmenu" : true,
33750         /**
33751          * @event mousedown
33752          * The raw mousedown event for the entire grid.
33753          * @param {Roo.EventObject} e
33754          */
33755         "mousedown" : true,
33756         /**
33757          * @event mouseup
33758          * The raw mouseup event for the entire grid.
33759          * @param {Roo.EventObject} e
33760          */
33761         "mouseup" : true,
33762         /**
33763          * @event mouseover
33764          * The raw mouseover event for the entire grid.
33765          * @param {Roo.EventObject} e
33766          */
33767         "mouseover" : true,
33768         /**
33769          * @event mouseout
33770          * The raw mouseout event for the entire grid.
33771          * @param {Roo.EventObject} e
33772          */
33773         "mouseout" : true,
33774         /**
33775          * @event keypress
33776          * The raw keypress event for the entire grid.
33777          * @param {Roo.EventObject} e
33778          */
33779         "keypress" : true,
33780         /**
33781          * @event keydown
33782          * The raw keydown event for the entire grid.
33783          * @param {Roo.EventObject} e
33784          */
33785         "keydown" : true,
33786
33787         // custom events
33788
33789         /**
33790          * @event cellclick
33791          * Fires when a cell is clicked
33792          * @param {Grid} this
33793          * @param {Number} rowIndex
33794          * @param {Number} columnIndex
33795          * @param {Roo.EventObject} e
33796          */
33797         "cellclick" : true,
33798         /**
33799          * @event celldblclick
33800          * Fires when a cell is double clicked
33801          * @param {Grid} this
33802          * @param {Number} rowIndex
33803          * @param {Number} columnIndex
33804          * @param {Roo.EventObject} e
33805          */
33806         "celldblclick" : true,
33807         /**
33808          * @event rowclick
33809          * Fires when a row is clicked
33810          * @param {Grid} this
33811          * @param {Number} rowIndex
33812          * @param {Roo.EventObject} e
33813          */
33814         "rowclick" : true,
33815         /**
33816          * @event rowdblclick
33817          * Fires when a row is double clicked
33818          * @param {Grid} this
33819          * @param {Number} rowIndex
33820          * @param {Roo.EventObject} e
33821          */
33822         "rowdblclick" : true,
33823         /**
33824          * @event headerclick
33825          * Fires when a header is clicked
33826          * @param {Grid} this
33827          * @param {Number} columnIndex
33828          * @param {Roo.EventObject} e
33829          */
33830         "headerclick" : true,
33831         /**
33832          * @event headerdblclick
33833          * Fires when a header cell is double clicked
33834          * @param {Grid} this
33835          * @param {Number} columnIndex
33836          * @param {Roo.EventObject} e
33837          */
33838         "headerdblclick" : true,
33839         /**
33840          * @event rowcontextmenu
33841          * Fires when a row is right clicked
33842          * @param {Grid} this
33843          * @param {Number} rowIndex
33844          * @param {Roo.EventObject} e
33845          */
33846         "rowcontextmenu" : true,
33847         /**
33848          * @event cellcontextmenu
33849          * Fires when a cell is right clicked
33850          * @param {Grid} this
33851          * @param {Number} rowIndex
33852          * @param {Number} cellIndex
33853          * @param {Roo.EventObject} e
33854          */
33855          "cellcontextmenu" : true,
33856         /**
33857          * @event headercontextmenu
33858          * Fires when a header is right clicked
33859          * @param {Grid} this
33860          * @param {Number} columnIndex
33861          * @param {Roo.EventObject} e
33862          */
33863         "headercontextmenu" : true,
33864         /**
33865          * @event bodyscroll
33866          * Fires when the body element is scrolled
33867          * @param {Number} scrollLeft
33868          * @param {Number} scrollTop
33869          */
33870         "bodyscroll" : true,
33871         /**
33872          * @event columnresize
33873          * Fires when the user resizes a column
33874          * @param {Number} columnIndex
33875          * @param {Number} newSize
33876          */
33877         "columnresize" : true,
33878         /**
33879          * @event columnmove
33880          * Fires when the user moves a column
33881          * @param {Number} oldIndex
33882          * @param {Number} newIndex
33883          */
33884         "columnmove" : true,
33885         /**
33886          * @event startdrag
33887          * Fires when row(s) start being dragged
33888          * @param {Grid} this
33889          * @param {Roo.GridDD} dd The drag drop object
33890          * @param {event} e The raw browser event
33891          */
33892         "startdrag" : true,
33893         /**
33894          * @event enddrag
33895          * Fires when a drag operation is complete
33896          * @param {Grid} this
33897          * @param {Roo.GridDD} dd The drag drop object
33898          * @param {event} e The raw browser event
33899          */
33900         "enddrag" : true,
33901         /**
33902          * @event dragdrop
33903          * Fires when dragged row(s) are dropped on a valid DD target
33904          * @param {Grid} this
33905          * @param {Roo.GridDD} dd The drag drop object
33906          * @param {String} targetId The target drag drop object
33907          * @param {event} e The raw browser event
33908          */
33909         "dragdrop" : true,
33910         /**
33911          * @event dragover
33912          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33913          * @param {Grid} this
33914          * @param {Roo.GridDD} dd The drag drop object
33915          * @param {String} targetId The target drag drop object
33916          * @param {event} e The raw browser event
33917          */
33918         "dragover" : true,
33919         /**
33920          * @event dragenter
33921          *  Fires when the dragged row(s) first cross another DD target while being dragged
33922          * @param {Grid} this
33923          * @param {Roo.GridDD} dd The drag drop object
33924          * @param {String} targetId The target drag drop object
33925          * @param {event} e The raw browser event
33926          */
33927         "dragenter" : true,
33928         /**
33929          * @event dragout
33930          * Fires when the dragged row(s) leave another DD target while being dragged
33931          * @param {Grid} this
33932          * @param {Roo.GridDD} dd The drag drop object
33933          * @param {String} targetId The target drag drop object
33934          * @param {event} e The raw browser event
33935          */
33936         "dragout" : true,
33937         /**
33938          * @event rowclass
33939          * Fires when a row is rendered, so you can change add a style to it.
33940          * @param {GridView} gridview   The grid view
33941          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33942          */
33943         'rowclass' : true,
33944
33945         /**
33946          * @event render
33947          * Fires when the grid is rendered
33948          * @param {Grid} grid
33949          */
33950         'render' : true
33951     });
33952
33953     Roo.grid.Grid.superclass.constructor.call(this);
33954 };
33955 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33956     
33957     /**
33958          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
33959          */
33960         /**
33961          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
33962          */
33963         /**
33964          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
33965          */
33966         /**
33967          * @cfg {Roo.grid.Store} ds The data store for the grid
33968          */
33969         /**
33970          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
33971          */
33972         /**
33973      * @cfg {String} ddGroup - drag drop group.
33974      */
33975       /**
33976      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
33977      */
33978
33979     /**
33980      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33981      */
33982     minColumnWidth : 25,
33983
33984     /**
33985      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33986      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33987      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33988      */
33989     autoSizeColumns : false,
33990
33991     /**
33992      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33993      */
33994     autoSizeHeaders : true,
33995
33996     /**
33997      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33998      */
33999     monitorWindowResize : true,
34000
34001     /**
34002      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34003      * rows measured to get a columns size. Default is 0 (all rows).
34004      */
34005     maxRowsToMeasure : 0,
34006
34007     /**
34008      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34009      */
34010     trackMouseOver : true,
34011
34012     /**
34013     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34014     */
34015       /**
34016     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
34017     */
34018     
34019     /**
34020     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34021     */
34022     enableDragDrop : false,
34023     
34024     /**
34025     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34026     */
34027     enableColumnMove : true,
34028     
34029     /**
34030     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34031     */
34032     enableColumnHide : true,
34033     
34034     /**
34035     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34036     */
34037     enableRowHeightSync : false,
34038     
34039     /**
34040     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34041     */
34042     stripeRows : true,
34043     
34044     /**
34045     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34046     */
34047     autoHeight : false,
34048
34049     /**
34050      * @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.
34051      */
34052     autoExpandColumn : false,
34053
34054     /**
34055     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34056     * Default is 50.
34057     */
34058     autoExpandMin : 50,
34059
34060     /**
34061     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34062     */
34063     autoExpandMax : 1000,
34064
34065     /**
34066     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34067     */
34068     view : null,
34069
34070     /**
34071     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34072     */
34073     loadMask : false,
34074     /**
34075     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34076     */
34077     dropTarget: false,
34078     
34079    
34080     
34081     // private
34082     rendered : false,
34083
34084     /**
34085     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34086     * of a fixed width. Default is false.
34087     */
34088     /**
34089     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34090     */
34091     
34092     
34093     /**
34094     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34095     * %0 is replaced with the number of selected rows.
34096     */
34097     ddText : "{0} selected row{1}",
34098     
34099     
34100     /**
34101      * Called once after all setup has been completed and the grid is ready to be rendered.
34102      * @return {Roo.grid.Grid} this
34103      */
34104     render : function()
34105     {
34106         var c = this.container;
34107         // try to detect autoHeight/width mode
34108         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34109             this.autoHeight = true;
34110         }
34111         var view = this.getView();
34112         view.init(this);
34113
34114         c.on("click", this.onClick, this);
34115         c.on("dblclick", this.onDblClick, this);
34116         c.on("contextmenu", this.onContextMenu, this);
34117         c.on("keydown", this.onKeyDown, this);
34118         if (Roo.isTouch) {
34119             c.on("touchstart", this.onTouchStart, this);
34120         }
34121
34122         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34123
34124         this.getSelectionModel().init(this);
34125
34126         view.render();
34127
34128         if(this.loadMask){
34129             this.loadMask = new Roo.LoadMask(this.container,
34130                     Roo.apply({store:this.dataSource}, this.loadMask));
34131         }
34132         
34133         
34134         if (this.toolbar && this.toolbar.xtype) {
34135             this.toolbar.container = this.getView().getHeaderPanel(true);
34136             this.toolbar = new Roo.Toolbar(this.toolbar);
34137         }
34138         if (this.footer && this.footer.xtype) {
34139             this.footer.dataSource = this.getDataSource();
34140             this.footer.container = this.getView().getFooterPanel(true);
34141             this.footer = Roo.factory(this.footer, Roo);
34142         }
34143         if (this.dropTarget && this.dropTarget.xtype) {
34144             delete this.dropTarget.xtype;
34145             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34146         }
34147         
34148         
34149         this.rendered = true;
34150         this.fireEvent('render', this);
34151         return this;
34152     },
34153
34154     /**
34155      * Reconfigures the grid to use a different Store and Column Model.
34156      * The View will be bound to the new objects and refreshed.
34157      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34158      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34159      */
34160     reconfigure : function(dataSource, colModel){
34161         if(this.loadMask){
34162             this.loadMask.destroy();
34163             this.loadMask = new Roo.LoadMask(this.container,
34164                     Roo.apply({store:dataSource}, this.loadMask));
34165         }
34166         this.view.bind(dataSource, colModel);
34167         this.dataSource = dataSource;
34168         this.colModel = colModel;
34169         this.view.refresh(true);
34170     },
34171     /**
34172      * addColumns
34173      * Add's a column, default at the end..
34174      
34175      * @param {int} position to add (default end)
34176      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
34177      */
34178     addColumns : function(pos, ar)
34179     {
34180         
34181         for (var i =0;i< ar.length;i++) {
34182             var cfg = ar[i];
34183             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
34184             this.cm.lookup[cfg.id] = cfg;
34185         }
34186         
34187         
34188         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
34189             pos = this.cm.config.length; //this.cm.config.push(cfg);
34190         } 
34191         pos = Math.max(0,pos);
34192         ar.unshift(0);
34193         ar.unshift(pos);
34194         this.cm.config.splice.apply(this.cm.config, ar);
34195         
34196         
34197         
34198         this.view.generateRules(this.cm);
34199         this.view.refresh(true);
34200         
34201     },
34202     
34203     
34204     
34205     
34206     // private
34207     onKeyDown : function(e){
34208         this.fireEvent("keydown", e);
34209     },
34210
34211     /**
34212      * Destroy this grid.
34213      * @param {Boolean} removeEl True to remove the element
34214      */
34215     destroy : function(removeEl, keepListeners){
34216         if(this.loadMask){
34217             this.loadMask.destroy();
34218         }
34219         var c = this.container;
34220         c.removeAllListeners();
34221         this.view.destroy();
34222         this.colModel.purgeListeners();
34223         if(!keepListeners){
34224             this.purgeListeners();
34225         }
34226         c.update("");
34227         if(removeEl === true){
34228             c.remove();
34229         }
34230     },
34231
34232     // private
34233     processEvent : function(name, e){
34234         // does this fire select???
34235         //Roo.log('grid:processEvent '  + name);
34236         
34237         if (name != 'touchstart' ) {
34238             this.fireEvent(name, e);    
34239         }
34240         
34241         var t = e.getTarget();
34242         var v = this.view;
34243         var header = v.findHeaderIndex(t);
34244         if(header !== false){
34245             var ename = name == 'touchstart' ? 'click' : name;
34246              
34247             this.fireEvent("header" + ename, this, header, e);
34248         }else{
34249             var row = v.findRowIndex(t);
34250             var cell = v.findCellIndex(t);
34251             if (name == 'touchstart') {
34252                 // first touch is always a click.
34253                 // hopefull this happens after selection is updated.?
34254                 name = false;
34255                 
34256                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
34257                     var cs = this.selModel.getSelectedCell();
34258                     if (row == cs[0] && cell == cs[1]){
34259                         name = 'dblclick';
34260                     }
34261                 }
34262                 if (typeof(this.selModel.getSelections) != 'undefined') {
34263                     var cs = this.selModel.getSelections();
34264                     var ds = this.dataSource;
34265                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
34266                         name = 'dblclick';
34267                     }
34268                 }
34269                 if (!name) {
34270                     return;
34271                 }
34272             }
34273             
34274             
34275             if(row !== false){
34276                 this.fireEvent("row" + name, this, row, e);
34277                 if(cell !== false){
34278                     this.fireEvent("cell" + name, this, row, cell, e);
34279                 }
34280             }
34281         }
34282     },
34283
34284     // private
34285     onClick : function(e){
34286         this.processEvent("click", e);
34287     },
34288    // private
34289     onTouchStart : function(e){
34290         this.processEvent("touchstart", e);
34291     },
34292
34293     // private
34294     onContextMenu : function(e, t){
34295         this.processEvent("contextmenu", e);
34296     },
34297
34298     // private
34299     onDblClick : function(e){
34300         this.processEvent("dblclick", e);
34301     },
34302
34303     // private
34304     walkCells : function(row, col, step, fn, scope){
34305         var cm = this.colModel, clen = cm.getColumnCount();
34306         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34307         if(step < 0){
34308             if(col < 0){
34309                 row--;
34310                 first = false;
34311             }
34312             while(row >= 0){
34313                 if(!first){
34314                     col = clen-1;
34315                 }
34316                 first = false;
34317                 while(col >= 0){
34318                     if(fn.call(scope || this, row, col, cm) === true){
34319                         return [row, col];
34320                     }
34321                     col--;
34322                 }
34323                 row--;
34324             }
34325         } else {
34326             if(col >= clen){
34327                 row++;
34328                 first = false;
34329             }
34330             while(row < rlen){
34331                 if(!first){
34332                     col = 0;
34333                 }
34334                 first = false;
34335                 while(col < clen){
34336                     if(fn.call(scope || this, row, col, cm) === true){
34337                         return [row, col];
34338                     }
34339                     col++;
34340                 }
34341                 row++;
34342             }
34343         }
34344         return null;
34345     },
34346
34347     // private
34348     getSelections : function(){
34349         return this.selModel.getSelections();
34350     },
34351
34352     /**
34353      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34354      * but if manual update is required this method will initiate it.
34355      */
34356     autoSize : function(){
34357         if(this.rendered){
34358             this.view.layout();
34359             if(this.view.adjustForScroll){
34360                 this.view.adjustForScroll();
34361             }
34362         }
34363     },
34364
34365     /**
34366      * Returns the grid's underlying element.
34367      * @return {Element} The element
34368      */
34369     getGridEl : function(){
34370         return this.container;
34371     },
34372
34373     // private for compatibility, overridden by editor grid
34374     stopEditing : function(){},
34375
34376     /**
34377      * Returns the grid's SelectionModel.
34378      * @return {SelectionModel}
34379      */
34380     getSelectionModel : function(){
34381         if(!this.selModel){
34382             this.selModel = new Roo.grid.RowSelectionModel();
34383         }
34384         return this.selModel;
34385     },
34386
34387     /**
34388      * Returns the grid's DataSource.
34389      * @return {DataSource}
34390      */
34391     getDataSource : function(){
34392         return this.dataSource;
34393     },
34394
34395     /**
34396      * Returns the grid's ColumnModel.
34397      * @return {ColumnModel}
34398      */
34399     getColumnModel : function(){
34400         return this.colModel;
34401     },
34402
34403     /**
34404      * Returns the grid's GridView object.
34405      * @return {GridView}
34406      */
34407     getView : function(){
34408         if(!this.view){
34409             this.view = new Roo.grid.GridView(this.viewConfig);
34410             this.relayEvents(this.view, [
34411                 "beforerowremoved", "beforerowsinserted",
34412                 "beforerefresh", "rowremoved",
34413                 "rowsinserted", "rowupdated" ,"refresh"
34414             ]);
34415         }
34416         return this.view;
34417     },
34418     /**
34419      * Called to get grid's drag proxy text, by default returns this.ddText.
34420      * Override this to put something different in the dragged text.
34421      * @return {String}
34422      */
34423     getDragDropText : function(){
34424         var count = this.selModel.getCount();
34425         return String.format(this.ddText, count, count == 1 ? '' : 's');
34426     }
34427 });
34428 /*
34429  * Based on:
34430  * Ext JS Library 1.1.1
34431  * Copyright(c) 2006-2007, Ext JS, LLC.
34432  *
34433  * Originally Released Under LGPL - original licence link has changed is not relivant.
34434  *
34435  * Fork - LGPL
34436  * <script type="text/javascript">
34437  */
34438  /**
34439  * @class Roo.grid.AbstractGridView
34440  * @extends Roo.util.Observable
34441  * @abstract
34442  * Abstract base class for grid Views
34443  * @constructor
34444  */
34445 Roo.grid.AbstractGridView = function(){
34446         this.grid = null;
34447         
34448         this.events = {
34449             "beforerowremoved" : true,
34450             "beforerowsinserted" : true,
34451             "beforerefresh" : true,
34452             "rowremoved" : true,
34453             "rowsinserted" : true,
34454             "rowupdated" : true,
34455             "refresh" : true
34456         };
34457     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34458 };
34459
34460 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34461     rowClass : "x-grid-row",
34462     cellClass : "x-grid-cell",
34463     tdClass : "x-grid-td",
34464     hdClass : "x-grid-hd",
34465     splitClass : "x-grid-hd-split",
34466     
34467     init: function(grid){
34468         this.grid = grid;
34469                 var cid = this.grid.getGridEl().id;
34470         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34471         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34472         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34473         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34474         },
34475         
34476     getColumnRenderers : function(){
34477         var renderers = [];
34478         var cm = this.grid.colModel;
34479         var colCount = cm.getColumnCount();
34480         for(var i = 0; i < colCount; i++){
34481             renderers[i] = cm.getRenderer(i);
34482         }
34483         return renderers;
34484     },
34485     
34486     getColumnIds : function(){
34487         var ids = [];
34488         var cm = this.grid.colModel;
34489         var colCount = cm.getColumnCount();
34490         for(var i = 0; i < colCount; i++){
34491             ids[i] = cm.getColumnId(i);
34492         }
34493         return ids;
34494     },
34495     
34496     getDataIndexes : function(){
34497         if(!this.indexMap){
34498             this.indexMap = this.buildIndexMap();
34499         }
34500         return this.indexMap.colToData;
34501     },
34502     
34503     getColumnIndexByDataIndex : function(dataIndex){
34504         if(!this.indexMap){
34505             this.indexMap = this.buildIndexMap();
34506         }
34507         return this.indexMap.dataToCol[dataIndex];
34508     },
34509     
34510     /**
34511      * Set a css style for a column dynamically. 
34512      * @param {Number} colIndex The index of the column
34513      * @param {String} name The css property name
34514      * @param {String} value The css value
34515      */
34516     setCSSStyle : function(colIndex, name, value){
34517         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34518         Roo.util.CSS.updateRule(selector, name, value);
34519     },
34520     
34521     generateRules : function(cm){
34522         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34523         Roo.util.CSS.removeStyleSheet(rulesId);
34524         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34525             var cid = cm.getColumnId(i);
34526             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34527                          this.tdSelector, cid, " {\n}\n",
34528                          this.hdSelector, cid, " {\n}\n",
34529                          this.splitSelector, cid, " {\n}\n");
34530         }
34531         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34532     }
34533 });/*
34534  * Based on:
34535  * Ext JS Library 1.1.1
34536  * Copyright(c) 2006-2007, Ext JS, LLC.
34537  *
34538  * Originally Released Under LGPL - original licence link has changed is not relivant.
34539  *
34540  * Fork - LGPL
34541  * <script type="text/javascript">
34542  */
34543
34544 // private
34545 // This is a support class used internally by the Grid components
34546 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34547     this.grid = grid;
34548     this.view = grid.getView();
34549     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34550     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34551     if(hd2){
34552         this.setHandleElId(Roo.id(hd));
34553         this.setOuterHandleElId(Roo.id(hd2));
34554     }
34555     this.scroll = false;
34556 };
34557 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34558     maxDragWidth: 120,
34559     getDragData : function(e){
34560         var t = Roo.lib.Event.getTarget(e);
34561         var h = this.view.findHeaderCell(t);
34562         if(h){
34563             return {ddel: h.firstChild, header:h};
34564         }
34565         return false;
34566     },
34567
34568     onInitDrag : function(e){
34569         this.view.headersDisabled = true;
34570         var clone = this.dragData.ddel.cloneNode(true);
34571         clone.id = Roo.id();
34572         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34573         this.proxy.update(clone);
34574         return true;
34575     },
34576
34577     afterValidDrop : function(){
34578         var v = this.view;
34579         setTimeout(function(){
34580             v.headersDisabled = false;
34581         }, 50);
34582     },
34583
34584     afterInvalidDrop : function(){
34585         var v = this.view;
34586         setTimeout(function(){
34587             v.headersDisabled = false;
34588         }, 50);
34589     }
34590 });
34591 /*
34592  * Based on:
34593  * Ext JS Library 1.1.1
34594  * Copyright(c) 2006-2007, Ext JS, LLC.
34595  *
34596  * Originally Released Under LGPL - original licence link has changed is not relivant.
34597  *
34598  * Fork - LGPL
34599  * <script type="text/javascript">
34600  */
34601 // private
34602 // This is a support class used internally by the Grid components
34603 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34604     this.grid = grid;
34605     this.view = grid.getView();
34606     // split the proxies so they don't interfere with mouse events
34607     this.proxyTop = Roo.DomHelper.append(document.body, {
34608         cls:"col-move-top", html:"&#160;"
34609     }, true);
34610     this.proxyBottom = Roo.DomHelper.append(document.body, {
34611         cls:"col-move-bottom", html:"&#160;"
34612     }, true);
34613     this.proxyTop.hide = this.proxyBottom.hide = function(){
34614         this.setLeftTop(-100,-100);
34615         this.setStyle("visibility", "hidden");
34616     };
34617     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34618     // temporarily disabled
34619     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34620     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34621 };
34622 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34623     proxyOffsets : [-4, -9],
34624     fly: Roo.Element.fly,
34625
34626     getTargetFromEvent : function(e){
34627         var t = Roo.lib.Event.getTarget(e);
34628         var cindex = this.view.findCellIndex(t);
34629         if(cindex !== false){
34630             return this.view.getHeaderCell(cindex);
34631         }
34632         return null;
34633     },
34634
34635     nextVisible : function(h){
34636         var v = this.view, cm = this.grid.colModel;
34637         h = h.nextSibling;
34638         while(h){
34639             if(!cm.isHidden(v.getCellIndex(h))){
34640                 return h;
34641             }
34642             h = h.nextSibling;
34643         }
34644         return null;
34645     },
34646
34647     prevVisible : function(h){
34648         var v = this.view, cm = this.grid.colModel;
34649         h = h.prevSibling;
34650         while(h){
34651             if(!cm.isHidden(v.getCellIndex(h))){
34652                 return h;
34653             }
34654             h = h.prevSibling;
34655         }
34656         return null;
34657     },
34658
34659     positionIndicator : function(h, n, e){
34660         var x = Roo.lib.Event.getPageX(e);
34661         var r = Roo.lib.Dom.getRegion(n.firstChild);
34662         var px, pt, py = r.top + this.proxyOffsets[1];
34663         if((r.right - x) <= (r.right-r.left)/2){
34664             px = r.right+this.view.borderWidth;
34665             pt = "after";
34666         }else{
34667             px = r.left;
34668             pt = "before";
34669         }
34670         var oldIndex = this.view.getCellIndex(h);
34671         var newIndex = this.view.getCellIndex(n);
34672
34673         if(this.grid.colModel.isFixed(newIndex)){
34674             return false;
34675         }
34676
34677         var locked = this.grid.colModel.isLocked(newIndex);
34678
34679         if(pt == "after"){
34680             newIndex++;
34681         }
34682         if(oldIndex < newIndex){
34683             newIndex--;
34684         }
34685         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34686             return false;
34687         }
34688         px +=  this.proxyOffsets[0];
34689         this.proxyTop.setLeftTop(px, py);
34690         this.proxyTop.show();
34691         if(!this.bottomOffset){
34692             this.bottomOffset = this.view.mainHd.getHeight();
34693         }
34694         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34695         this.proxyBottom.show();
34696         return pt;
34697     },
34698
34699     onNodeEnter : function(n, dd, e, data){
34700         if(data.header != n){
34701             this.positionIndicator(data.header, n, e);
34702         }
34703     },
34704
34705     onNodeOver : function(n, dd, e, data){
34706         var result = false;
34707         if(data.header != n){
34708             result = this.positionIndicator(data.header, n, e);
34709         }
34710         if(!result){
34711             this.proxyTop.hide();
34712             this.proxyBottom.hide();
34713         }
34714         return result ? this.dropAllowed : this.dropNotAllowed;
34715     },
34716
34717     onNodeOut : function(n, dd, e, data){
34718         this.proxyTop.hide();
34719         this.proxyBottom.hide();
34720     },
34721
34722     onNodeDrop : function(n, dd, e, data){
34723         var h = data.header;
34724         if(h != n){
34725             var cm = this.grid.colModel;
34726             var x = Roo.lib.Event.getPageX(e);
34727             var r = Roo.lib.Dom.getRegion(n.firstChild);
34728             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34729             var oldIndex = this.view.getCellIndex(h);
34730             var newIndex = this.view.getCellIndex(n);
34731             var locked = cm.isLocked(newIndex);
34732             if(pt == "after"){
34733                 newIndex++;
34734             }
34735             if(oldIndex < newIndex){
34736                 newIndex--;
34737             }
34738             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34739                 return false;
34740             }
34741             cm.setLocked(oldIndex, locked, true);
34742             cm.moveColumn(oldIndex, newIndex);
34743             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34744             return true;
34745         }
34746         return false;
34747     }
34748 });
34749 /*
34750  * Based on:
34751  * Ext JS Library 1.1.1
34752  * Copyright(c) 2006-2007, Ext JS, LLC.
34753  *
34754  * Originally Released Under LGPL - original licence link has changed is not relivant.
34755  *
34756  * Fork - LGPL
34757  * <script type="text/javascript">
34758  */
34759   
34760 /**
34761  * @class Roo.grid.GridView
34762  * @extends Roo.util.Observable
34763  *
34764  * @constructor
34765  * @param {Object} config
34766  */
34767 Roo.grid.GridView = function(config){
34768     Roo.grid.GridView.superclass.constructor.call(this);
34769     this.el = null;
34770
34771     Roo.apply(this, config);
34772 };
34773
34774 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34775
34776     unselectable :  'unselectable="on"',
34777     unselectableCls :  'x-unselectable',
34778     
34779     
34780     rowClass : "x-grid-row",
34781
34782     cellClass : "x-grid-col",
34783
34784     tdClass : "x-grid-td",
34785
34786     hdClass : "x-grid-hd",
34787
34788     splitClass : "x-grid-split",
34789
34790     sortClasses : ["sort-asc", "sort-desc"],
34791
34792     enableMoveAnim : false,
34793
34794     hlColor: "C3DAF9",
34795
34796     dh : Roo.DomHelper,
34797
34798     fly : Roo.Element.fly,
34799
34800     css : Roo.util.CSS,
34801
34802     borderWidth: 1,
34803
34804     splitOffset: 3,
34805
34806     scrollIncrement : 22,
34807
34808     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34809
34810     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34811
34812     bind : function(ds, cm){
34813         if(this.ds){
34814             this.ds.un("load", this.onLoad, this);
34815             this.ds.un("datachanged", this.onDataChange, this);
34816             this.ds.un("add", this.onAdd, this);
34817             this.ds.un("remove", this.onRemove, this);
34818             this.ds.un("update", this.onUpdate, this);
34819             this.ds.un("clear", this.onClear, this);
34820         }
34821         if(ds){
34822             ds.on("load", this.onLoad, this);
34823             ds.on("datachanged", this.onDataChange, this);
34824             ds.on("add", this.onAdd, this);
34825             ds.on("remove", this.onRemove, this);
34826             ds.on("update", this.onUpdate, this);
34827             ds.on("clear", this.onClear, this);
34828         }
34829         this.ds = ds;
34830
34831         if(this.cm){
34832             this.cm.un("widthchange", this.onColWidthChange, this);
34833             this.cm.un("headerchange", this.onHeaderChange, this);
34834             this.cm.un("hiddenchange", this.onHiddenChange, this);
34835             this.cm.un("columnmoved", this.onColumnMove, this);
34836             this.cm.un("columnlockchange", this.onColumnLock, this);
34837         }
34838         if(cm){
34839             this.generateRules(cm);
34840             cm.on("widthchange", this.onColWidthChange, this);
34841             cm.on("headerchange", this.onHeaderChange, this);
34842             cm.on("hiddenchange", this.onHiddenChange, this);
34843             cm.on("columnmoved", this.onColumnMove, this);
34844             cm.on("columnlockchange", this.onColumnLock, this);
34845         }
34846         this.cm = cm;
34847     },
34848
34849     init: function(grid){
34850         Roo.grid.GridView.superclass.init.call(this, grid);
34851
34852         this.bind(grid.dataSource, grid.colModel);
34853
34854         grid.on("headerclick", this.handleHeaderClick, this);
34855
34856         if(grid.trackMouseOver){
34857             grid.on("mouseover", this.onRowOver, this);
34858             grid.on("mouseout", this.onRowOut, this);
34859         }
34860         grid.cancelTextSelection = function(){};
34861         this.gridId = grid.id;
34862
34863         var tpls = this.templates || {};
34864
34865         if(!tpls.master){
34866             tpls.master = new Roo.Template(
34867                '<div class="x-grid" hidefocus="true">',
34868                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34869                   '<div class="x-grid-topbar"></div>',
34870                   '<div class="x-grid-scroller"><div></div></div>',
34871                   '<div class="x-grid-locked">',
34872                       '<div class="x-grid-header">{lockedHeader}</div>',
34873                       '<div class="x-grid-body">{lockedBody}</div>',
34874                   "</div>",
34875                   '<div class="x-grid-viewport">',
34876                       '<div class="x-grid-header">{header}</div>',
34877                       '<div class="x-grid-body">{body}</div>',
34878                   "</div>",
34879                   '<div class="x-grid-bottombar"></div>',
34880                  
34881                   '<div class="x-grid-resize-proxy">&#160;</div>',
34882                "</div>"
34883             );
34884             tpls.master.disableformats = true;
34885         }
34886
34887         if(!tpls.header){
34888             tpls.header = new Roo.Template(
34889                '<table border="0" cellspacing="0" cellpadding="0">',
34890                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34891                "</table>{splits}"
34892             );
34893             tpls.header.disableformats = true;
34894         }
34895         tpls.header.compile();
34896
34897         if(!tpls.hcell){
34898             tpls.hcell = new Roo.Template(
34899                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34900                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34901                 "</div></td>"
34902              );
34903              tpls.hcell.disableFormats = true;
34904         }
34905         tpls.hcell.compile();
34906
34907         if(!tpls.hsplit){
34908             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
34909                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
34910             tpls.hsplit.disableFormats = true;
34911         }
34912         tpls.hsplit.compile();
34913
34914         if(!tpls.body){
34915             tpls.body = new Roo.Template(
34916                '<table border="0" cellspacing="0" cellpadding="0">',
34917                "<tbody>{rows}</tbody>",
34918                "</table>"
34919             );
34920             tpls.body.disableFormats = true;
34921         }
34922         tpls.body.compile();
34923
34924         if(!tpls.row){
34925             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34926             tpls.row.disableFormats = true;
34927         }
34928         tpls.row.compile();
34929
34930         if(!tpls.cell){
34931             tpls.cell = new Roo.Template(
34932                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34933                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
34934                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
34935                 "</td>"
34936             );
34937             tpls.cell.disableFormats = true;
34938         }
34939         tpls.cell.compile();
34940
34941         this.templates = tpls;
34942     },
34943
34944     // remap these for backwards compat
34945     onColWidthChange : function(){
34946         this.updateColumns.apply(this, arguments);
34947     },
34948     onHeaderChange : function(){
34949         this.updateHeaders.apply(this, arguments);
34950     }, 
34951     onHiddenChange : function(){
34952         this.handleHiddenChange.apply(this, arguments);
34953     },
34954     onColumnMove : function(){
34955         this.handleColumnMove.apply(this, arguments);
34956     },
34957     onColumnLock : function(){
34958         this.handleLockChange.apply(this, arguments);
34959     },
34960
34961     onDataChange : function(){
34962         this.refresh();
34963         this.updateHeaderSortState();
34964     },
34965
34966     onClear : function(){
34967         this.refresh();
34968     },
34969
34970     onUpdate : function(ds, record){
34971         this.refreshRow(record);
34972     },
34973
34974     refreshRow : function(record){
34975         var ds = this.ds, index;
34976         if(typeof record == 'number'){
34977             index = record;
34978             record = ds.getAt(index);
34979         }else{
34980             index = ds.indexOf(record);
34981         }
34982         this.insertRows(ds, index, index, true);
34983         this.onRemove(ds, record, index+1, true);
34984         this.syncRowHeights(index, index);
34985         this.layout();
34986         this.fireEvent("rowupdated", this, index, record);
34987     },
34988
34989     onAdd : function(ds, records, index){
34990         this.insertRows(ds, index, index + (records.length-1));
34991     },
34992
34993     onRemove : function(ds, record, index, isUpdate){
34994         if(isUpdate !== true){
34995             this.fireEvent("beforerowremoved", this, index, record);
34996         }
34997         var bt = this.getBodyTable(), lt = this.getLockedTable();
34998         if(bt.rows[index]){
34999             bt.firstChild.removeChild(bt.rows[index]);
35000         }
35001         if(lt.rows[index]){
35002             lt.firstChild.removeChild(lt.rows[index]);
35003         }
35004         if(isUpdate !== true){
35005             this.stripeRows(index);
35006             this.syncRowHeights(index, index);
35007             this.layout();
35008             this.fireEvent("rowremoved", this, index, record);
35009         }
35010     },
35011
35012     onLoad : function(){
35013         this.scrollToTop();
35014     },
35015
35016     /**
35017      * Scrolls the grid to the top
35018      */
35019     scrollToTop : function(){
35020         if(this.scroller){
35021             this.scroller.dom.scrollTop = 0;
35022             this.syncScroll();
35023         }
35024     },
35025
35026     /**
35027      * Gets a panel in the header of the grid that can be used for toolbars etc.
35028      * After modifying the contents of this panel a call to grid.autoSize() may be
35029      * required to register any changes in size.
35030      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35031      * @return Roo.Element
35032      */
35033     getHeaderPanel : function(doShow){
35034         if(doShow){
35035             this.headerPanel.show();
35036         }
35037         return this.headerPanel;
35038     },
35039
35040     /**
35041      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35042      * After modifying the contents of this panel a call to grid.autoSize() may be
35043      * required to register any changes in size.
35044      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35045      * @return Roo.Element
35046      */
35047     getFooterPanel : function(doShow){
35048         if(doShow){
35049             this.footerPanel.show();
35050         }
35051         return this.footerPanel;
35052     },
35053
35054     initElements : function(){
35055         var E = Roo.Element;
35056         var el = this.grid.getGridEl().dom.firstChild;
35057         var cs = el.childNodes;
35058
35059         this.el = new E(el);
35060         
35061          this.focusEl = new E(el.firstChild);
35062         this.focusEl.swallowEvent("click", true);
35063         
35064         this.headerPanel = new E(cs[1]);
35065         this.headerPanel.enableDisplayMode("block");
35066
35067         this.scroller = new E(cs[2]);
35068         this.scrollSizer = new E(this.scroller.dom.firstChild);
35069
35070         this.lockedWrap = new E(cs[3]);
35071         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35072         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35073
35074         this.mainWrap = new E(cs[4]);
35075         this.mainHd = new E(this.mainWrap.dom.firstChild);
35076         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35077
35078         this.footerPanel = new E(cs[5]);
35079         this.footerPanel.enableDisplayMode("block");
35080
35081         this.resizeProxy = new E(cs[6]);
35082
35083         this.headerSelector = String.format(
35084            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35085            this.lockedHd.id, this.mainHd.id
35086         );
35087
35088         this.splitterSelector = String.format(
35089            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35090            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35091         );
35092     },
35093     idToCssName : function(s)
35094     {
35095         return s.replace(/[^a-z0-9]+/ig, '-');
35096     },
35097
35098     getHeaderCell : function(index){
35099         return Roo.DomQuery.select(this.headerSelector)[index];
35100     },
35101
35102     getHeaderCellMeasure : function(index){
35103         return this.getHeaderCell(index).firstChild;
35104     },
35105
35106     getHeaderCellText : function(index){
35107         return this.getHeaderCell(index).firstChild.firstChild;
35108     },
35109
35110     getLockedTable : function(){
35111         return this.lockedBody.dom.firstChild;
35112     },
35113
35114     getBodyTable : function(){
35115         return this.mainBody.dom.firstChild;
35116     },
35117
35118     getLockedRow : function(index){
35119         return this.getLockedTable().rows[index];
35120     },
35121
35122     getRow : function(index){
35123         return this.getBodyTable().rows[index];
35124     },
35125
35126     getRowComposite : function(index){
35127         if(!this.rowEl){
35128             this.rowEl = new Roo.CompositeElementLite();
35129         }
35130         var els = [], lrow, mrow;
35131         if(lrow = this.getLockedRow(index)){
35132             els.push(lrow);
35133         }
35134         if(mrow = this.getRow(index)){
35135             els.push(mrow);
35136         }
35137         this.rowEl.elements = els;
35138         return this.rowEl;
35139     },
35140     /**
35141      * Gets the 'td' of the cell
35142      * 
35143      * @param {Integer} rowIndex row to select
35144      * @param {Integer} colIndex column to select
35145      * 
35146      * @return {Object} 
35147      */
35148     getCell : function(rowIndex, colIndex){
35149         var locked = this.cm.getLockedCount();
35150         var source;
35151         if(colIndex < locked){
35152             source = this.lockedBody.dom.firstChild;
35153         }else{
35154             source = this.mainBody.dom.firstChild;
35155             colIndex -= locked;
35156         }
35157         return source.rows[rowIndex].childNodes[colIndex];
35158     },
35159
35160     getCellText : function(rowIndex, colIndex){
35161         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35162     },
35163
35164     getCellBox : function(cell){
35165         var b = this.fly(cell).getBox();
35166         if(Roo.isOpera){ // opera fails to report the Y
35167             b.y = cell.offsetTop + this.mainBody.getY();
35168         }
35169         return b;
35170     },
35171
35172     getCellIndex : function(cell){
35173         var id = String(cell.className).match(this.cellRE);
35174         if(id){
35175             return parseInt(id[1], 10);
35176         }
35177         return 0;
35178     },
35179
35180     findHeaderIndex : function(n){
35181         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35182         return r ? this.getCellIndex(r) : false;
35183     },
35184
35185     findHeaderCell : function(n){
35186         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35187         return r ? r : false;
35188     },
35189
35190     findRowIndex : function(n){
35191         if(!n){
35192             return false;
35193         }
35194         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35195         return r ? r.rowIndex : false;
35196     },
35197
35198     findCellIndex : function(node){
35199         var stop = this.el.dom;
35200         while(node && node != stop){
35201             if(this.findRE.test(node.className)){
35202                 return this.getCellIndex(node);
35203             }
35204             node = node.parentNode;
35205         }
35206         return false;
35207     },
35208
35209     getColumnId : function(index){
35210         return this.cm.getColumnId(index);
35211     },
35212
35213     getSplitters : function()
35214     {
35215         if(this.splitterSelector){
35216            return Roo.DomQuery.select(this.splitterSelector);
35217         }else{
35218             return null;
35219       }
35220     },
35221
35222     getSplitter : function(index){
35223         return this.getSplitters()[index];
35224     },
35225
35226     onRowOver : function(e, t){
35227         var row;
35228         if((row = this.findRowIndex(t)) !== false){
35229             this.getRowComposite(row).addClass("x-grid-row-over");
35230         }
35231     },
35232
35233     onRowOut : function(e, t){
35234         var row;
35235         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35236             this.getRowComposite(row).removeClass("x-grid-row-over");
35237         }
35238     },
35239
35240     renderHeaders : function(){
35241         var cm = this.cm;
35242         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35243         var cb = [], lb = [], sb = [], lsb = [], p = {};
35244         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35245             p.cellId = "x-grid-hd-0-" + i;
35246             p.splitId = "x-grid-csplit-0-" + i;
35247             p.id = cm.getColumnId(i);
35248             p.value = cm.getColumnHeader(i) || "";
35249             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
35250             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35251             if(!cm.isLocked(i)){
35252                 cb[cb.length] = ct.apply(p);
35253                 sb[sb.length] = st.apply(p);
35254             }else{
35255                 lb[lb.length] = ct.apply(p);
35256                 lsb[lsb.length] = st.apply(p);
35257             }
35258         }
35259         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35260                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35261     },
35262
35263     updateHeaders : function(){
35264         var html = this.renderHeaders();
35265         this.lockedHd.update(html[0]);
35266         this.mainHd.update(html[1]);
35267     },
35268
35269     /**
35270      * Focuses the specified row.
35271      * @param {Number} row The row index
35272      */
35273     focusRow : function(row)
35274     {
35275         //Roo.log('GridView.focusRow');
35276         var x = this.scroller.dom.scrollLeft;
35277         this.focusCell(row, 0, false);
35278         this.scroller.dom.scrollLeft = x;
35279     },
35280
35281     /**
35282      * Focuses the specified cell.
35283      * @param {Number} row The row index
35284      * @param {Number} col The column index
35285      * @param {Boolean} hscroll false to disable horizontal scrolling
35286      */
35287     focusCell : function(row, col, hscroll)
35288     {
35289         //Roo.log('GridView.focusCell');
35290         var el = this.ensureVisible(row, col, hscroll);
35291         this.focusEl.alignTo(el, "tl-tl");
35292         if(Roo.isGecko){
35293             this.focusEl.focus();
35294         }else{
35295             this.focusEl.focus.defer(1, this.focusEl);
35296         }
35297     },
35298
35299     /**
35300      * Scrolls the specified cell into view
35301      * @param {Number} row The row index
35302      * @param {Number} col The column index
35303      * @param {Boolean} hscroll false to disable horizontal scrolling
35304      */
35305     ensureVisible : function(row, col, hscroll)
35306     {
35307         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35308         //return null; //disable for testing.
35309         if(typeof row != "number"){
35310             row = row.rowIndex;
35311         }
35312         if(row < 0 && row >= this.ds.getCount()){
35313             return  null;
35314         }
35315         col = (col !== undefined ? col : 0);
35316         var cm = this.grid.colModel;
35317         while(cm.isHidden(col)){
35318             col++;
35319         }
35320
35321         var el = this.getCell(row, col);
35322         if(!el){
35323             return null;
35324         }
35325         var c = this.scroller.dom;
35326
35327         var ctop = parseInt(el.offsetTop, 10);
35328         var cleft = parseInt(el.offsetLeft, 10);
35329         var cbot = ctop + el.offsetHeight;
35330         var cright = cleft + el.offsetWidth;
35331         
35332         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35333         var stop = parseInt(c.scrollTop, 10);
35334         var sleft = parseInt(c.scrollLeft, 10);
35335         var sbot = stop + ch;
35336         var sright = sleft + c.clientWidth;
35337         /*
35338         Roo.log('GridView.ensureVisible:' +
35339                 ' ctop:' + ctop +
35340                 ' c.clientHeight:' + c.clientHeight +
35341                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35342                 ' stop:' + stop +
35343                 ' cbot:' + cbot +
35344                 ' sbot:' + sbot +
35345                 ' ch:' + ch  
35346                 );
35347         */
35348         if(ctop < stop){
35349             c.scrollTop = ctop;
35350             //Roo.log("set scrolltop to ctop DISABLE?");
35351         }else if(cbot > sbot){
35352             //Roo.log("set scrolltop to cbot-ch");
35353             c.scrollTop = cbot-ch;
35354         }
35355         
35356         if(hscroll !== false){
35357             if(cleft < sleft){
35358                 c.scrollLeft = cleft;
35359             }else if(cright > sright){
35360                 c.scrollLeft = cright-c.clientWidth;
35361             }
35362         }
35363          
35364         return el;
35365     },
35366
35367     updateColumns : function(){
35368         this.grid.stopEditing();
35369         var cm = this.grid.colModel, colIds = this.getColumnIds();
35370         //var totalWidth = cm.getTotalWidth();
35371         var pos = 0;
35372         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35373             //if(cm.isHidden(i)) continue;
35374             var w = cm.getColumnWidth(i);
35375             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35376             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35377         }
35378         this.updateSplitters();
35379     },
35380
35381     generateRules : function(cm){
35382         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35383         Roo.util.CSS.removeStyleSheet(rulesId);
35384         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35385             var cid = cm.getColumnId(i);
35386             var align = '';
35387             if(cm.config[i].align){
35388                 align = 'text-align:'+cm.config[i].align+';';
35389             }
35390             var hidden = '';
35391             if(cm.isHidden(i)){
35392                 hidden = 'display:none;';
35393             }
35394             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35395             ruleBuf.push(
35396                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35397                     this.hdSelector, cid, " {\n", align, width, "}\n",
35398                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35399                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35400         }
35401         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35402     },
35403
35404     updateSplitters : function(){
35405         var cm = this.cm, s = this.getSplitters();
35406         if(s){ // splitters not created yet
35407             var pos = 0, locked = true;
35408             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35409                 if(cm.isHidden(i)) {
35410                     continue;
35411                 }
35412                 var w = cm.getColumnWidth(i); // make sure it's a number
35413                 if(!cm.isLocked(i) && locked){
35414                     pos = 0;
35415                     locked = false;
35416                 }
35417                 pos += w;
35418                 s[i].style.left = (pos-this.splitOffset) + "px";
35419             }
35420         }
35421     },
35422
35423     handleHiddenChange : function(colModel, colIndex, hidden){
35424         if(hidden){
35425             this.hideColumn(colIndex);
35426         }else{
35427             this.unhideColumn(colIndex);
35428         }
35429     },
35430
35431     hideColumn : function(colIndex){
35432         var cid = this.getColumnId(colIndex);
35433         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35434         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35435         if(Roo.isSafari){
35436             this.updateHeaders();
35437         }
35438         this.updateSplitters();
35439         this.layout();
35440     },
35441
35442     unhideColumn : function(colIndex){
35443         var cid = this.getColumnId(colIndex);
35444         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35445         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35446
35447         if(Roo.isSafari){
35448             this.updateHeaders();
35449         }
35450         this.updateSplitters();
35451         this.layout();
35452     },
35453
35454     insertRows : function(dm, firstRow, lastRow, isUpdate){
35455         if(firstRow == 0 && lastRow == dm.getCount()-1){
35456             this.refresh();
35457         }else{
35458             if(!isUpdate){
35459                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35460             }
35461             var s = this.getScrollState();
35462             var markup = this.renderRows(firstRow, lastRow);
35463             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35464             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35465             this.restoreScroll(s);
35466             if(!isUpdate){
35467                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35468                 this.syncRowHeights(firstRow, lastRow);
35469                 this.stripeRows(firstRow);
35470                 this.layout();
35471             }
35472         }
35473     },
35474
35475     bufferRows : function(markup, target, index){
35476         var before = null, trows = target.rows, tbody = target.tBodies[0];
35477         if(index < trows.length){
35478             before = trows[index];
35479         }
35480         var b = document.createElement("div");
35481         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35482         var rows = b.firstChild.rows;
35483         for(var i = 0, len = rows.length; i < len; i++){
35484             if(before){
35485                 tbody.insertBefore(rows[0], before);
35486             }else{
35487                 tbody.appendChild(rows[0]);
35488             }
35489         }
35490         b.innerHTML = "";
35491         b = null;
35492     },
35493
35494     deleteRows : function(dm, firstRow, lastRow){
35495         if(dm.getRowCount()<1){
35496             this.fireEvent("beforerefresh", this);
35497             this.mainBody.update("");
35498             this.lockedBody.update("");
35499             this.fireEvent("refresh", this);
35500         }else{
35501             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35502             var bt = this.getBodyTable();
35503             var tbody = bt.firstChild;
35504             var rows = bt.rows;
35505             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35506                 tbody.removeChild(rows[firstRow]);
35507             }
35508             this.stripeRows(firstRow);
35509             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35510         }
35511     },
35512
35513     updateRows : function(dataSource, firstRow, lastRow){
35514         var s = this.getScrollState();
35515         this.refresh();
35516         this.restoreScroll(s);
35517     },
35518
35519     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35520         if(!noRefresh){
35521            this.refresh();
35522         }
35523         this.updateHeaderSortState();
35524     },
35525
35526     getScrollState : function(){
35527         
35528         var sb = this.scroller.dom;
35529         return {left: sb.scrollLeft, top: sb.scrollTop};
35530     },
35531
35532     stripeRows : function(startRow){
35533         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35534             return;
35535         }
35536         startRow = startRow || 0;
35537         var rows = this.getBodyTable().rows;
35538         var lrows = this.getLockedTable().rows;
35539         var cls = ' x-grid-row-alt ';
35540         for(var i = startRow, len = rows.length; i < len; i++){
35541             var row = rows[i], lrow = lrows[i];
35542             var isAlt = ((i+1) % 2 == 0);
35543             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35544             if(isAlt == hasAlt){
35545                 continue;
35546             }
35547             if(isAlt){
35548                 row.className += " x-grid-row-alt";
35549             }else{
35550                 row.className = row.className.replace("x-grid-row-alt", "");
35551             }
35552             if(lrow){
35553                 lrow.className = row.className;
35554             }
35555         }
35556     },
35557
35558     restoreScroll : function(state){
35559         //Roo.log('GridView.restoreScroll');
35560         var sb = this.scroller.dom;
35561         sb.scrollLeft = state.left;
35562         sb.scrollTop = state.top;
35563         this.syncScroll();
35564     },
35565
35566     syncScroll : function(){
35567         //Roo.log('GridView.syncScroll');
35568         var sb = this.scroller.dom;
35569         var sh = this.mainHd.dom;
35570         var bs = this.mainBody.dom;
35571         var lv = this.lockedBody.dom;
35572         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35573         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35574     },
35575
35576     handleScroll : function(e){
35577         this.syncScroll();
35578         var sb = this.scroller.dom;
35579         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35580         e.stopEvent();
35581     },
35582
35583     handleWheel : function(e){
35584         var d = e.getWheelDelta();
35585         this.scroller.dom.scrollTop -= d*22;
35586         // set this here to prevent jumpy scrolling on large tables
35587         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35588         e.stopEvent();
35589     },
35590
35591     renderRows : function(startRow, endRow){
35592         // pull in all the crap needed to render rows
35593         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35594         var colCount = cm.getColumnCount();
35595
35596         if(ds.getCount() < 1){
35597             return ["", ""];
35598         }
35599
35600         // build a map for all the columns
35601         var cs = [];
35602         for(var i = 0; i < colCount; i++){
35603             var name = cm.getDataIndex(i);
35604             cs[i] = {
35605                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35606                 renderer : cm.getRenderer(i),
35607                 id : cm.getColumnId(i),
35608                 locked : cm.isLocked(i),
35609                 has_editor : cm.isCellEditable(i)
35610             };
35611         }
35612
35613         startRow = startRow || 0;
35614         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35615
35616         // records to render
35617         var rs = ds.getRange(startRow, endRow);
35618
35619         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35620     },
35621
35622     // As much as I hate to duplicate code, this was branched because FireFox really hates
35623     // [].join("") on strings. The performance difference was substantial enough to
35624     // branch this function
35625     doRender : Roo.isGecko ?
35626             function(cs, rs, ds, startRow, colCount, stripe){
35627                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35628                 // buffers
35629                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35630                 
35631                 var hasListener = this.grid.hasListener('rowclass');
35632                 var rowcfg = {};
35633                 for(var j = 0, len = rs.length; j < len; j++){
35634                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35635                     for(var i = 0; i < colCount; i++){
35636                         c = cs[i];
35637                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35638                         p.id = c.id;
35639                         p.css = p.attr = "";
35640                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35641                         if(p.value == undefined || p.value === "") {
35642                             p.value = "&#160;";
35643                         }
35644                         if(c.has_editor){
35645                             p.css += ' x-grid-editable-cell';
35646                         }
35647                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
35648                             p.css +=  ' x-grid-dirty-cell';
35649                         }
35650                         var markup = ct.apply(p);
35651                         if(!c.locked){
35652                             cb+= markup;
35653                         }else{
35654                             lcb+= markup;
35655                         }
35656                     }
35657                     var alt = [];
35658                     if(stripe && ((rowIndex+1) % 2 == 0)){
35659                         alt.push("x-grid-row-alt")
35660                     }
35661                     if(r.dirty){
35662                         alt.push(  " x-grid-dirty-row");
35663                     }
35664                     rp.cells = lcb;
35665                     if(this.getRowClass){
35666                         alt.push(this.getRowClass(r, rowIndex));
35667                     }
35668                     if (hasListener) {
35669                         rowcfg = {
35670                              
35671                             record: r,
35672                             rowIndex : rowIndex,
35673                             rowClass : ''
35674                         };
35675                         this.grid.fireEvent('rowclass', this, rowcfg);
35676                         alt.push(rowcfg.rowClass);
35677                     }
35678                     rp.alt = alt.join(" ");
35679                     lbuf+= rt.apply(rp);
35680                     rp.cells = cb;
35681                     buf+=  rt.apply(rp);
35682                 }
35683                 return [lbuf, buf];
35684             } :
35685             function(cs, rs, ds, startRow, colCount, stripe){
35686                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35687                 // buffers
35688                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35689                 var hasListener = this.grid.hasListener('rowclass');
35690  
35691                 var rowcfg = {};
35692                 for(var j = 0, len = rs.length; j < len; j++){
35693                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35694                     for(var i = 0; i < colCount; i++){
35695                         c = cs[i];
35696                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35697                         p.id = c.id;
35698                         p.css = p.attr = "";
35699                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35700                         if(p.value == undefined || p.value === "") {
35701                             p.value = "&#160;";
35702                         }
35703                         //Roo.log(c);
35704                          if(c.has_editor){
35705                             p.css += ' x-grid-editable-cell';
35706                         }
35707                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35708                             p.css += ' x-grid-dirty-cell' 
35709                         }
35710                         
35711                         var markup = ct.apply(p);
35712                         if(!c.locked){
35713                             cb[cb.length] = markup;
35714                         }else{
35715                             lcb[lcb.length] = markup;
35716                         }
35717                     }
35718                     var alt = [];
35719                     if(stripe && ((rowIndex+1) % 2 == 0)){
35720                         alt.push( "x-grid-row-alt");
35721                     }
35722                     if(r.dirty){
35723                         alt.push(" x-grid-dirty-row");
35724                     }
35725                     rp.cells = lcb;
35726                     if(this.getRowClass){
35727                         alt.push( this.getRowClass(r, rowIndex));
35728                     }
35729                     if (hasListener) {
35730                         rowcfg = {
35731                              
35732                             record: r,
35733                             rowIndex : rowIndex,
35734                             rowClass : ''
35735                         };
35736                         this.grid.fireEvent('rowclass', this, rowcfg);
35737                         alt.push(rowcfg.rowClass);
35738                     }
35739                     
35740                     rp.alt = alt.join(" ");
35741                     rp.cells = lcb.join("");
35742                     lbuf[lbuf.length] = rt.apply(rp);
35743                     rp.cells = cb.join("");
35744                     buf[buf.length] =  rt.apply(rp);
35745                 }
35746                 return [lbuf.join(""), buf.join("")];
35747             },
35748
35749     renderBody : function(){
35750         var markup = this.renderRows();
35751         var bt = this.templates.body;
35752         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35753     },
35754
35755     /**
35756      * Refreshes the grid
35757      * @param {Boolean} headersToo
35758      */
35759     refresh : function(headersToo){
35760         this.fireEvent("beforerefresh", this);
35761         this.grid.stopEditing();
35762         var result = this.renderBody();
35763         this.lockedBody.update(result[0]);
35764         this.mainBody.update(result[1]);
35765         if(headersToo === true){
35766             this.updateHeaders();
35767             this.updateColumns();
35768             this.updateSplitters();
35769             this.updateHeaderSortState();
35770         }
35771         this.syncRowHeights();
35772         this.layout();
35773         this.fireEvent("refresh", this);
35774     },
35775
35776     handleColumnMove : function(cm, oldIndex, newIndex){
35777         this.indexMap = null;
35778         var s = this.getScrollState();
35779         this.refresh(true);
35780         this.restoreScroll(s);
35781         this.afterMove(newIndex);
35782     },
35783
35784     afterMove : function(colIndex){
35785         if(this.enableMoveAnim && Roo.enableFx){
35786             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35787         }
35788         // if multisort - fix sortOrder, and reload..
35789         if (this.grid.dataSource.multiSort) {
35790             // the we can call sort again..
35791             var dm = this.grid.dataSource;
35792             var cm = this.grid.colModel;
35793             var so = [];
35794             for(var i = 0; i < cm.config.length; i++ ) {
35795                 
35796                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35797                     continue; // dont' bother, it's not in sort list or being set.
35798                 }
35799                 
35800                 so.push(cm.config[i].dataIndex);
35801             };
35802             dm.sortOrder = so;
35803             dm.load(dm.lastOptions);
35804             
35805             
35806         }
35807         
35808     },
35809
35810     updateCell : function(dm, rowIndex, dataIndex){
35811         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35812         if(typeof colIndex == "undefined"){ // not present in grid
35813             return;
35814         }
35815         var cm = this.grid.colModel;
35816         var cell = this.getCell(rowIndex, colIndex);
35817         var cellText = this.getCellText(rowIndex, colIndex);
35818
35819         var p = {
35820             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35821             id : cm.getColumnId(colIndex),
35822             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35823         };
35824         var renderer = cm.getRenderer(colIndex);
35825         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35826         if(typeof val == "undefined" || val === "") {
35827             val = "&#160;";
35828         }
35829         cellText.innerHTML = val;
35830         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35831         this.syncRowHeights(rowIndex, rowIndex);
35832     },
35833
35834     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35835         var maxWidth = 0;
35836         if(this.grid.autoSizeHeaders){
35837             var h = this.getHeaderCellMeasure(colIndex);
35838             maxWidth = Math.max(maxWidth, h.scrollWidth);
35839         }
35840         var tb, index;
35841         if(this.cm.isLocked(colIndex)){
35842             tb = this.getLockedTable();
35843             index = colIndex;
35844         }else{
35845             tb = this.getBodyTable();
35846             index = colIndex - this.cm.getLockedCount();
35847         }
35848         if(tb && tb.rows){
35849             var rows = tb.rows;
35850             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35851             for(var i = 0; i < stopIndex; i++){
35852                 var cell = rows[i].childNodes[index].firstChild;
35853                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35854             }
35855         }
35856         return maxWidth + /*margin for error in IE*/ 5;
35857     },
35858     /**
35859      * Autofit a column to its content.
35860      * @param {Number} colIndex
35861      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35862      */
35863      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35864          if(this.cm.isHidden(colIndex)){
35865              return; // can't calc a hidden column
35866          }
35867         if(forceMinSize){
35868             var cid = this.cm.getColumnId(colIndex);
35869             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35870            if(this.grid.autoSizeHeaders){
35871                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35872            }
35873         }
35874         var newWidth = this.calcColumnWidth(colIndex);
35875         this.cm.setColumnWidth(colIndex,
35876             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35877         if(!suppressEvent){
35878             this.grid.fireEvent("columnresize", colIndex, newWidth);
35879         }
35880     },
35881
35882     /**
35883      * Autofits all columns to their content and then expands to fit any extra space in the grid
35884      */
35885      autoSizeColumns : function(){
35886         var cm = this.grid.colModel;
35887         var colCount = cm.getColumnCount();
35888         for(var i = 0; i < colCount; i++){
35889             this.autoSizeColumn(i, true, true);
35890         }
35891         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35892             this.fitColumns();
35893         }else{
35894             this.updateColumns();
35895             this.layout();
35896         }
35897     },
35898
35899     /**
35900      * Autofits all columns to the grid's width proportionate with their current size
35901      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35902      */
35903     fitColumns : function(reserveScrollSpace){
35904         var cm = this.grid.colModel;
35905         var colCount = cm.getColumnCount();
35906         var cols = [];
35907         var width = 0;
35908         var i, w;
35909         for (i = 0; i < colCount; i++){
35910             if(!cm.isHidden(i) && !cm.isFixed(i)){
35911                 w = cm.getColumnWidth(i);
35912                 cols.push(i);
35913                 cols.push(w);
35914                 width += w;
35915             }
35916         }
35917         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35918         if(reserveScrollSpace){
35919             avail -= 17;
35920         }
35921         var frac = (avail - cm.getTotalWidth())/width;
35922         while (cols.length){
35923             w = cols.pop();
35924             i = cols.pop();
35925             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35926         }
35927         this.updateColumns();
35928         this.layout();
35929     },
35930
35931     onRowSelect : function(rowIndex){
35932         var row = this.getRowComposite(rowIndex);
35933         row.addClass("x-grid-row-selected");
35934     },
35935
35936     onRowDeselect : function(rowIndex){
35937         var row = this.getRowComposite(rowIndex);
35938         row.removeClass("x-grid-row-selected");
35939     },
35940
35941     onCellSelect : function(row, col){
35942         var cell = this.getCell(row, col);
35943         if(cell){
35944             Roo.fly(cell).addClass("x-grid-cell-selected");
35945         }
35946     },
35947
35948     onCellDeselect : function(row, col){
35949         var cell = this.getCell(row, col);
35950         if(cell){
35951             Roo.fly(cell).removeClass("x-grid-cell-selected");
35952         }
35953     },
35954
35955     updateHeaderSortState : function(){
35956         
35957         // sort state can be single { field: xxx, direction : yyy}
35958         // or   { xxx=>ASC , yyy : DESC ..... }
35959         
35960         var mstate = {};
35961         if (!this.ds.multiSort) { 
35962             var state = this.ds.getSortState();
35963             if(!state){
35964                 return;
35965             }
35966             mstate[state.field] = state.direction;
35967             // FIXME... - this is not used here.. but might be elsewhere..
35968             this.sortState = state;
35969             
35970         } else {
35971             mstate = this.ds.sortToggle;
35972         }
35973         //remove existing sort classes..
35974         
35975         var sc = this.sortClasses;
35976         var hds = this.el.select(this.headerSelector).removeClass(sc);
35977         
35978         for(var f in mstate) {
35979         
35980             var sortColumn = this.cm.findColumnIndex(f);
35981             
35982             if(sortColumn != -1){
35983                 var sortDir = mstate[f];        
35984                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35985             }
35986         }
35987         
35988          
35989         
35990     },
35991
35992
35993     handleHeaderClick : function(g, index,e){
35994         
35995         Roo.log("header click");
35996         
35997         if (Roo.isTouch) {
35998             // touch events on header are handled by context
35999             this.handleHdCtx(g,index,e);
36000             return;
36001         }
36002         
36003         
36004         if(this.headersDisabled){
36005             return;
36006         }
36007         var dm = g.dataSource, cm = g.colModel;
36008         if(!cm.isSortable(index)){
36009             return;
36010         }
36011         g.stopEditing();
36012         
36013         if (dm.multiSort) {
36014             // update the sortOrder
36015             var so = [];
36016             for(var i = 0; i < cm.config.length; i++ ) {
36017                 
36018                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36019                     continue; // dont' bother, it's not in sort list or being set.
36020                 }
36021                 
36022                 so.push(cm.config[i].dataIndex);
36023             };
36024             dm.sortOrder = so;
36025         }
36026         
36027         
36028         dm.sort(cm.getDataIndex(index));
36029     },
36030
36031
36032     destroy : function(){
36033         if(this.colMenu){
36034             this.colMenu.removeAll();
36035             Roo.menu.MenuMgr.unregister(this.colMenu);
36036             this.colMenu.getEl().remove();
36037             delete this.colMenu;
36038         }
36039         if(this.hmenu){
36040             this.hmenu.removeAll();
36041             Roo.menu.MenuMgr.unregister(this.hmenu);
36042             this.hmenu.getEl().remove();
36043             delete this.hmenu;
36044         }
36045         if(this.grid.enableColumnMove){
36046             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36047             if(dds){
36048                 for(var dd in dds){
36049                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36050                         var elid = dds[dd].dragElId;
36051                         dds[dd].unreg();
36052                         Roo.get(elid).remove();
36053                     } else if(dds[dd].config.isTarget){
36054                         dds[dd].proxyTop.remove();
36055                         dds[dd].proxyBottom.remove();
36056                         dds[dd].unreg();
36057                     }
36058                     if(Roo.dd.DDM.locationCache[dd]){
36059                         delete Roo.dd.DDM.locationCache[dd];
36060                     }
36061                 }
36062                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36063             }
36064         }
36065         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36066         this.bind(null, null);
36067         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36068     },
36069
36070     handleLockChange : function(){
36071         this.refresh(true);
36072     },
36073
36074     onDenyColumnLock : function(){
36075
36076     },
36077
36078     onDenyColumnHide : function(){
36079
36080     },
36081
36082     handleHdMenuClick : function(item){
36083         var index = this.hdCtxIndex;
36084         var cm = this.cm, ds = this.ds;
36085         switch(item.id){
36086             case "asc":
36087                 ds.sort(cm.getDataIndex(index), "ASC");
36088                 break;
36089             case "desc":
36090                 ds.sort(cm.getDataIndex(index), "DESC");
36091                 break;
36092             case "lock":
36093                 var lc = cm.getLockedCount();
36094                 if(cm.getColumnCount(true) <= lc+1){
36095                     this.onDenyColumnLock();
36096                     return;
36097                 }
36098                 if(lc != index){
36099                     cm.setLocked(index, true, true);
36100                     cm.moveColumn(index, lc);
36101                     this.grid.fireEvent("columnmove", index, lc);
36102                 }else{
36103                     cm.setLocked(index, true);
36104                 }
36105             break;
36106             case "unlock":
36107                 var lc = cm.getLockedCount();
36108                 if((lc-1) != index){
36109                     cm.setLocked(index, false, true);
36110                     cm.moveColumn(index, lc-1);
36111                     this.grid.fireEvent("columnmove", index, lc-1);
36112                 }else{
36113                     cm.setLocked(index, false);
36114                 }
36115             break;
36116             case 'wider': // used to expand cols on touch..
36117             case 'narrow':
36118                 var cw = cm.getColumnWidth(index);
36119                 cw += (item.id == 'wider' ? 1 : -1) * 50;
36120                 cw = Math.max(0, cw);
36121                 cw = Math.min(cw,4000);
36122                 cm.setColumnWidth(index, cw);
36123                 break;
36124                 
36125             default:
36126                 index = cm.getIndexById(item.id.substr(4));
36127                 if(index != -1){
36128                     if(item.checked && cm.getColumnCount(true) <= 1){
36129                         this.onDenyColumnHide();
36130                         return false;
36131                     }
36132                     cm.setHidden(index, item.checked);
36133                 }
36134         }
36135         return true;
36136     },
36137
36138     beforeColMenuShow : function(){
36139         var cm = this.cm,  colCount = cm.getColumnCount();
36140         this.colMenu.removeAll();
36141         for(var i = 0; i < colCount; i++){
36142             this.colMenu.add(new Roo.menu.CheckItem({
36143                 id: "col-"+cm.getColumnId(i),
36144                 text: cm.getColumnHeader(i),
36145                 checked: !cm.isHidden(i),
36146                 hideOnClick:false
36147             }));
36148         }
36149     },
36150
36151     handleHdCtx : function(g, index, e){
36152         e.stopEvent();
36153         var hd = this.getHeaderCell(index);
36154         this.hdCtxIndex = index;
36155         var ms = this.hmenu.items, cm = this.cm;
36156         ms.get("asc").setDisabled(!cm.isSortable(index));
36157         ms.get("desc").setDisabled(!cm.isSortable(index));
36158         if(this.grid.enableColLock !== false){
36159             ms.get("lock").setDisabled(cm.isLocked(index));
36160             ms.get("unlock").setDisabled(!cm.isLocked(index));
36161         }
36162         this.hmenu.show(hd, "tl-bl");
36163     },
36164
36165     handleHdOver : function(e){
36166         var hd = this.findHeaderCell(e.getTarget());
36167         if(hd && !this.headersDisabled){
36168             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36169                this.fly(hd).addClass("x-grid-hd-over");
36170             }
36171         }
36172     },
36173
36174     handleHdOut : function(e){
36175         var hd = this.findHeaderCell(e.getTarget());
36176         if(hd){
36177             this.fly(hd).removeClass("x-grid-hd-over");
36178         }
36179     },
36180
36181     handleSplitDblClick : function(e, t){
36182         var i = this.getCellIndex(t);
36183         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36184             this.autoSizeColumn(i, true);
36185             this.layout();
36186         }
36187     },
36188
36189     render : function(){
36190
36191         var cm = this.cm;
36192         var colCount = cm.getColumnCount();
36193
36194         if(this.grid.monitorWindowResize === true){
36195             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36196         }
36197         var header = this.renderHeaders();
36198         var body = this.templates.body.apply({rows:""});
36199         var html = this.templates.master.apply({
36200             lockedBody: body,
36201             body: body,
36202             lockedHeader: header[0],
36203             header: header[1]
36204         });
36205
36206         //this.updateColumns();
36207
36208         this.grid.getGridEl().dom.innerHTML = html;
36209
36210         this.initElements();
36211         
36212         // a kludge to fix the random scolling effect in webkit
36213         this.el.on("scroll", function() {
36214             this.el.dom.scrollTop=0; // hopefully not recursive..
36215         },this);
36216
36217         this.scroller.on("scroll", this.handleScroll, this);
36218         this.lockedBody.on("mousewheel", this.handleWheel, this);
36219         this.mainBody.on("mousewheel", this.handleWheel, this);
36220
36221         this.mainHd.on("mouseover", this.handleHdOver, this);
36222         this.mainHd.on("mouseout", this.handleHdOut, this);
36223         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36224                 {delegate: "."+this.splitClass});
36225
36226         this.lockedHd.on("mouseover", this.handleHdOver, this);
36227         this.lockedHd.on("mouseout", this.handleHdOut, this);
36228         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36229                 {delegate: "."+this.splitClass});
36230
36231         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36232             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36233         }
36234
36235         this.updateSplitters();
36236
36237         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36238             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36239             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36240         }
36241
36242         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36243             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36244             this.hmenu.add(
36245                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36246                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36247             );
36248             if(this.grid.enableColLock !== false){
36249                 this.hmenu.add('-',
36250                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36251                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36252                 );
36253             }
36254             if (Roo.isTouch) {
36255                  this.hmenu.add('-',
36256                     {id:"wider", text: this.columnsWiderText},
36257                     {id:"narrow", text: this.columnsNarrowText }
36258                 );
36259                 
36260                  
36261             }
36262             
36263             if(this.grid.enableColumnHide !== false){
36264
36265                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36266                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36267                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36268
36269                 this.hmenu.add('-',
36270                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36271                 );
36272             }
36273             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36274
36275             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36276         }
36277
36278         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36279             this.dd = new Roo.grid.GridDragZone(this.grid, {
36280                 ddGroup : this.grid.ddGroup || 'GridDD'
36281             });
36282             
36283         }
36284
36285         /*
36286         for(var i = 0; i < colCount; i++){
36287             if(cm.isHidden(i)){
36288                 this.hideColumn(i);
36289             }
36290             if(cm.config[i].align){
36291                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36292                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36293             }
36294         }*/
36295         
36296         this.updateHeaderSortState();
36297
36298         this.beforeInitialResize();
36299         this.layout(true);
36300
36301         // two part rendering gives faster view to the user
36302         this.renderPhase2.defer(1, this);
36303     },
36304
36305     renderPhase2 : function(){
36306         // render the rows now
36307         this.refresh();
36308         if(this.grid.autoSizeColumns){
36309             this.autoSizeColumns();
36310         }
36311     },
36312
36313     beforeInitialResize : function(){
36314
36315     },
36316
36317     onColumnSplitterMoved : function(i, w){
36318         this.userResized = true;
36319         var cm = this.grid.colModel;
36320         cm.setColumnWidth(i, w, true);
36321         var cid = cm.getColumnId(i);
36322         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36323         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36324         this.updateSplitters();
36325         this.layout();
36326         this.grid.fireEvent("columnresize", i, w);
36327     },
36328
36329     syncRowHeights : function(startIndex, endIndex){
36330         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36331             startIndex = startIndex || 0;
36332             var mrows = this.getBodyTable().rows;
36333             var lrows = this.getLockedTable().rows;
36334             var len = mrows.length-1;
36335             endIndex = Math.min(endIndex || len, len);
36336             for(var i = startIndex; i <= endIndex; i++){
36337                 var m = mrows[i], l = lrows[i];
36338                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36339                 m.style.height = l.style.height = h + "px";
36340             }
36341         }
36342     },
36343
36344     layout : function(initialRender, is2ndPass)
36345     {
36346         var g = this.grid;
36347         var auto = g.autoHeight;
36348         var scrollOffset = 16;
36349         var c = g.getGridEl(), cm = this.cm,
36350                 expandCol = g.autoExpandColumn,
36351                 gv = this;
36352         //c.beginMeasure();
36353
36354         if(!c.dom.offsetWidth){ // display:none?
36355             if(initialRender){
36356                 this.lockedWrap.show();
36357                 this.mainWrap.show();
36358             }
36359             return;
36360         }
36361
36362         var hasLock = this.cm.isLocked(0);
36363
36364         var tbh = this.headerPanel.getHeight();
36365         var bbh = this.footerPanel.getHeight();
36366
36367         if(auto){
36368             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36369             var newHeight = ch + c.getBorderWidth("tb");
36370             if(g.maxHeight){
36371                 newHeight = Math.min(g.maxHeight, newHeight);
36372             }
36373             c.setHeight(newHeight);
36374         }
36375
36376         if(g.autoWidth){
36377             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36378         }
36379
36380         var s = this.scroller;
36381
36382         var csize = c.getSize(true);
36383
36384         this.el.setSize(csize.width, csize.height);
36385
36386         this.headerPanel.setWidth(csize.width);
36387         this.footerPanel.setWidth(csize.width);
36388
36389         var hdHeight = this.mainHd.getHeight();
36390         var vw = csize.width;
36391         var vh = csize.height - (tbh + bbh);
36392
36393         s.setSize(vw, vh);
36394
36395         var bt = this.getBodyTable();
36396         
36397         if(cm.getLockedCount() == cm.config.length){
36398             bt = this.getLockedTable();
36399         }
36400         
36401         var ltWidth = hasLock ?
36402                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36403
36404         var scrollHeight = bt.offsetHeight;
36405         var scrollWidth = ltWidth + bt.offsetWidth;
36406         var vscroll = false, hscroll = false;
36407
36408         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36409
36410         var lw = this.lockedWrap, mw = this.mainWrap;
36411         var lb = this.lockedBody, mb = this.mainBody;
36412
36413         setTimeout(function(){
36414             var t = s.dom.offsetTop;
36415             var w = s.dom.clientWidth,
36416                 h = s.dom.clientHeight;
36417
36418             lw.setTop(t);
36419             lw.setSize(ltWidth, h);
36420
36421             mw.setLeftTop(ltWidth, t);
36422             mw.setSize(w-ltWidth, h);
36423
36424             lb.setHeight(h-hdHeight);
36425             mb.setHeight(h-hdHeight);
36426
36427             if(is2ndPass !== true && !gv.userResized && expandCol){
36428                 // high speed resize without full column calculation
36429                 
36430                 var ci = cm.getIndexById(expandCol);
36431                 if (ci < 0) {
36432                     ci = cm.findColumnIndex(expandCol);
36433                 }
36434                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36435                 var expandId = cm.getColumnId(ci);
36436                 var  tw = cm.getTotalWidth(false);
36437                 var currentWidth = cm.getColumnWidth(ci);
36438                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36439                 if(currentWidth != cw){
36440                     cm.setColumnWidth(ci, cw, true);
36441                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36442                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36443                     gv.updateSplitters();
36444                     gv.layout(false, true);
36445                 }
36446             }
36447
36448             if(initialRender){
36449                 lw.show();
36450                 mw.show();
36451             }
36452             //c.endMeasure();
36453         }, 10);
36454     },
36455
36456     onWindowResize : function(){
36457         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36458             return;
36459         }
36460         this.layout();
36461     },
36462
36463     appendFooter : function(parentEl){
36464         return null;
36465     },
36466
36467     sortAscText : "Sort Ascending",
36468     sortDescText : "Sort Descending",
36469     lockText : "Lock Column",
36470     unlockText : "Unlock Column",
36471     columnsText : "Columns",
36472  
36473     columnsWiderText : "Wider",
36474     columnsNarrowText : "Thinner"
36475 });
36476
36477
36478 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36479     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36480     this.proxy.el.addClass('x-grid3-col-dd');
36481 };
36482
36483 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36484     handleMouseDown : function(e){
36485
36486     },
36487
36488     callHandleMouseDown : function(e){
36489         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36490     }
36491 });
36492 /*
36493  * Based on:
36494  * Ext JS Library 1.1.1
36495  * Copyright(c) 2006-2007, Ext JS, LLC.
36496  *
36497  * Originally Released Under LGPL - original licence link has changed is not relivant.
36498  *
36499  * Fork - LGPL
36500  * <script type="text/javascript">
36501  */
36502  /**
36503  * @extends Roo.dd.DDProxy
36504  * @class Roo.grid.SplitDragZone
36505  * Support for Column Header resizing
36506  * @constructor
36507  * @param {Object} config
36508  */
36509 // private
36510 // This is a support class used internally by the Grid components
36511 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36512     this.grid = grid;
36513     this.view = grid.getView();
36514     this.proxy = this.view.resizeProxy;
36515     Roo.grid.SplitDragZone.superclass.constructor.call(
36516         this,
36517         hd, // ID
36518         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
36519         {  // CONFIG
36520             dragElId : Roo.id(this.proxy.dom),
36521             resizeFrame:false
36522         }
36523     );
36524     
36525     this.setHandleElId(Roo.id(hd));
36526     if (hd2 !== false) {
36527         this.setOuterHandleElId(Roo.id(hd2));
36528     }
36529     
36530     this.scroll = false;
36531 };
36532 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36533     fly: Roo.Element.fly,
36534
36535     b4StartDrag : function(x, y){
36536         this.view.headersDisabled = true;
36537         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
36538                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
36539         );
36540         this.proxy.setHeight(h);
36541         
36542         // for old system colWidth really stored the actual width?
36543         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
36544         // which in reality did not work.. - it worked only for fixed sizes
36545         // for resizable we need to use actual sizes.
36546         var w = this.cm.getColumnWidth(this.cellIndex);
36547         if (!this.view.mainWrap) {
36548             // bootstrap.
36549             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
36550         }
36551         
36552         
36553         
36554         // this was w-this.grid.minColumnWidth;
36555         // doesnt really make sense? - w = thie curren width or the rendered one?
36556         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36557         this.resetConstraints();
36558         this.setXConstraint(minw, 1000);
36559         this.setYConstraint(0, 0);
36560         this.minX = x - minw;
36561         this.maxX = x + 1000;
36562         this.startPos = x;
36563         if (!this.view.mainWrap) { // this is Bootstrap code..
36564             this.getDragEl().style.display='block';
36565         }
36566         
36567         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36568     },
36569
36570
36571     handleMouseDown : function(e){
36572         ev = Roo.EventObject.setEvent(e);
36573         var t = this.fly(ev.getTarget());
36574         if(t.hasClass("x-grid-split")){
36575             this.cellIndex = this.view.getCellIndex(t.dom);
36576             this.split = t.dom;
36577             this.cm = this.grid.colModel;
36578             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36579                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36580             }
36581         }
36582     },
36583
36584     endDrag : function(e){
36585         this.view.headersDisabled = false;
36586         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36587         var diff = endX - this.startPos;
36588         // 
36589         var w = this.cm.getColumnWidth(this.cellIndex);
36590         if (!this.view.mainWrap) {
36591             w = 0;
36592         }
36593         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
36594     },
36595
36596     autoOffset : function(){
36597         this.setDelta(0,0);
36598     }
36599 });/*
36600  * Based on:
36601  * Ext JS Library 1.1.1
36602  * Copyright(c) 2006-2007, Ext JS, LLC.
36603  *
36604  * Originally Released Under LGPL - original licence link has changed is not relivant.
36605  *
36606  * Fork - LGPL
36607  * <script type="text/javascript">
36608  */
36609  
36610 // private
36611 // This is a support class used internally by the Grid components
36612 Roo.grid.GridDragZone = function(grid, config){
36613     this.view = grid.getView();
36614     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36615     if(this.view.lockedBody){
36616         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36617         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36618     }
36619     this.scroll = false;
36620     this.grid = grid;
36621     this.ddel = document.createElement('div');
36622     this.ddel.className = 'x-grid-dd-wrap';
36623 };
36624
36625 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36626     ddGroup : "GridDD",
36627
36628     getDragData : function(e){
36629         var t = Roo.lib.Event.getTarget(e);
36630         var rowIndex = this.view.findRowIndex(t);
36631         var sm = this.grid.selModel;
36632             
36633         //Roo.log(rowIndex);
36634         
36635         if (sm.getSelectedCell) {
36636             // cell selection..
36637             if (!sm.getSelectedCell()) {
36638                 return false;
36639             }
36640             if (rowIndex != sm.getSelectedCell()[0]) {
36641                 return false;
36642             }
36643         
36644         }
36645         if (sm.getSelections && sm.getSelections().length < 1) {
36646             return false;
36647         }
36648         
36649         
36650         // before it used to all dragging of unseleted... - now we dont do that.
36651         if(rowIndex !== false){
36652             
36653             // if editorgrid.. 
36654             
36655             
36656             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36657                
36658             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36659               //  
36660             //}
36661             if (e.hasModifier()){
36662                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36663             }
36664             
36665             Roo.log("getDragData");
36666             
36667             return {
36668                 grid: this.grid,
36669                 ddel: this.ddel,
36670                 rowIndex: rowIndex,
36671                 selections: sm.getSelections ? sm.getSelections() : (
36672                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
36673             };
36674         }
36675         return false;
36676     },
36677     
36678     
36679     onInitDrag : function(e){
36680         var data = this.dragData;
36681         this.ddel.innerHTML = this.grid.getDragDropText();
36682         this.proxy.update(this.ddel);
36683         // fire start drag?
36684     },
36685
36686     afterRepair : function(){
36687         this.dragging = false;
36688     },
36689
36690     getRepairXY : function(e, data){
36691         return false;
36692     },
36693
36694     onEndDrag : function(data, e){
36695         // fire end drag?
36696     },
36697
36698     onValidDrop : function(dd, e, id){
36699         // fire drag drop?
36700         this.hideProxy();
36701     },
36702
36703     beforeInvalidDrop : function(e, id){
36704
36705     }
36706 });/*
36707  * Based on:
36708  * Ext JS Library 1.1.1
36709  * Copyright(c) 2006-2007, Ext JS, LLC.
36710  *
36711  * Originally Released Under LGPL - original licence link has changed is not relivant.
36712  *
36713  * Fork - LGPL
36714  * <script type="text/javascript">
36715  */
36716  
36717
36718 /**
36719  * @class Roo.grid.ColumnModel
36720  * @extends Roo.util.Observable
36721  * This is the default implementation of a ColumnModel used by the Grid. It defines
36722  * the columns in the grid.
36723  * <br>Usage:<br>
36724  <pre><code>
36725  var colModel = new Roo.grid.ColumnModel([
36726         {header: "Ticker", width: 60, sortable: true, locked: true},
36727         {header: "Company Name", width: 150, sortable: true},
36728         {header: "Market Cap.", width: 100, sortable: true},
36729         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36730         {header: "Employees", width: 100, sortable: true, resizable: false}
36731  ]);
36732  </code></pre>
36733  * <p>
36734  
36735  * The config options listed for this class are options which may appear in each
36736  * individual column definition.
36737  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36738  * @constructor
36739  * @param {Object} config An Array of column config objects. See this class's
36740  * config objects for details.
36741 */
36742 Roo.grid.ColumnModel = function(config){
36743         /**
36744      * The config passed into the constructor
36745      */
36746     this.config = []; //config;
36747     this.lookup = {};
36748
36749     // if no id, create one
36750     // if the column does not have a dataIndex mapping,
36751     // map it to the order it is in the config
36752     for(var i = 0, len = config.length; i < len; i++){
36753         this.addColumn(config[i]);
36754         
36755     }
36756
36757     /**
36758      * The width of columns which have no width specified (defaults to 100)
36759      * @type Number
36760      */
36761     this.defaultWidth = 100;
36762
36763     /**
36764      * Default sortable of columns which have no sortable specified (defaults to false)
36765      * @type Boolean
36766      */
36767     this.defaultSortable = false;
36768
36769     this.addEvents({
36770         /**
36771              * @event widthchange
36772              * Fires when the width of a column changes.
36773              * @param {ColumnModel} this
36774              * @param {Number} columnIndex The column index
36775              * @param {Number} newWidth The new width
36776              */
36777             "widthchange": true,
36778         /**
36779              * @event headerchange
36780              * Fires when the text of a header changes.
36781              * @param {ColumnModel} this
36782              * @param {Number} columnIndex The column index
36783              * @param {Number} newText The new header text
36784              */
36785             "headerchange": true,
36786         /**
36787              * @event hiddenchange
36788              * Fires when a column is hidden or "unhidden".
36789              * @param {ColumnModel} this
36790              * @param {Number} columnIndex The column index
36791              * @param {Boolean} hidden true if hidden, false otherwise
36792              */
36793             "hiddenchange": true,
36794             /**
36795          * @event columnmoved
36796          * Fires when a column is moved.
36797          * @param {ColumnModel} this
36798          * @param {Number} oldIndex
36799          * @param {Number} newIndex
36800          */
36801         "columnmoved" : true,
36802         /**
36803          * @event columlockchange
36804          * Fires when a column's locked state is changed
36805          * @param {ColumnModel} this
36806          * @param {Number} colIndex
36807          * @param {Boolean} locked true if locked
36808          */
36809         "columnlockchange" : true
36810     });
36811     Roo.grid.ColumnModel.superclass.constructor.call(this);
36812 };
36813 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36814     /**
36815      * @cfg {String} header The header text to display in the Grid view.
36816      */
36817         /**
36818      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
36819      */
36820         /**
36821      * @cfg {String} smHeader Header at Bootsrap Small width
36822      */
36823         /**
36824      * @cfg {String} mdHeader Header at Bootsrap Medium width
36825      */
36826         /**
36827      * @cfg {String} lgHeader Header at Bootsrap Large width
36828      */
36829         /**
36830      * @cfg {String} xlHeader Header at Bootsrap extra Large width
36831      */
36832     /**
36833      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36834      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36835      * specified, the column's index is used as an index into the Record's data Array.
36836      */
36837     /**
36838      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36839      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36840      */
36841     /**
36842      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36843      * Defaults to the value of the {@link #defaultSortable} property.
36844      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36845      */
36846     /**
36847      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36848      */
36849     /**
36850      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36851      */
36852     /**
36853      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36854      */
36855     /**
36856      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36857      */
36858     /**
36859      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36860      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36861      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
36862      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
36863      */
36864        /**
36865      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36866      */
36867     /**
36868      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36869      */
36870     /**
36871      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
36872      */
36873     /**
36874      * @cfg {String} cursor (Optional)
36875      */
36876     /**
36877      * @cfg {String} tooltip (Optional)
36878      */
36879     /**
36880      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
36881      */
36882     /**
36883      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
36884      */
36885     /**
36886      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
36887      */
36888     /**
36889      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
36890      */
36891         /**
36892      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
36893      */
36894     /**
36895      * Returns the id of the column at the specified index.
36896      * @param {Number} index The column index
36897      * @return {String} the id
36898      */
36899     getColumnId : function(index){
36900         return this.config[index].id;
36901     },
36902
36903     /**
36904      * Returns the column for a specified id.
36905      * @param {String} id The column id
36906      * @return {Object} the column
36907      */
36908     getColumnById : function(id){
36909         return this.lookup[id];
36910     },
36911
36912     
36913     /**
36914      * Returns the column Object for a specified dataIndex.
36915      * @param {String} dataIndex The column dataIndex
36916      * @return {Object|Boolean} the column or false if not found
36917      */
36918     getColumnByDataIndex: function(dataIndex){
36919         var index = this.findColumnIndex(dataIndex);
36920         return index > -1 ? this.config[index] : false;
36921     },
36922     
36923     /**
36924      * Returns the index for a specified column id.
36925      * @param {String} id The column id
36926      * @return {Number} the index, or -1 if not found
36927      */
36928     getIndexById : function(id){
36929         for(var i = 0, len = this.config.length; i < len; i++){
36930             if(this.config[i].id == id){
36931                 return i;
36932             }
36933         }
36934         return -1;
36935     },
36936     
36937     /**
36938      * Returns the index for a specified column dataIndex.
36939      * @param {String} dataIndex The column dataIndex
36940      * @return {Number} the index, or -1 if not found
36941      */
36942     
36943     findColumnIndex : function(dataIndex){
36944         for(var i = 0, len = this.config.length; i < len; i++){
36945             if(this.config[i].dataIndex == dataIndex){
36946                 return i;
36947             }
36948         }
36949         return -1;
36950     },
36951     
36952     
36953     moveColumn : function(oldIndex, newIndex){
36954         var c = this.config[oldIndex];
36955         this.config.splice(oldIndex, 1);
36956         this.config.splice(newIndex, 0, c);
36957         this.dataMap = null;
36958         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36959     },
36960
36961     isLocked : function(colIndex){
36962         return this.config[colIndex].locked === true;
36963     },
36964
36965     setLocked : function(colIndex, value, suppressEvent){
36966         if(this.isLocked(colIndex) == value){
36967             return;
36968         }
36969         this.config[colIndex].locked = value;
36970         if(!suppressEvent){
36971             this.fireEvent("columnlockchange", this, colIndex, value);
36972         }
36973     },
36974
36975     getTotalLockedWidth : function(){
36976         var totalWidth = 0;
36977         for(var i = 0; i < this.config.length; i++){
36978             if(this.isLocked(i) && !this.isHidden(i)){
36979                 this.totalWidth += this.getColumnWidth(i);
36980             }
36981         }
36982         return totalWidth;
36983     },
36984
36985     getLockedCount : function(){
36986         for(var i = 0, len = this.config.length; i < len; i++){
36987             if(!this.isLocked(i)){
36988                 return i;
36989             }
36990         }
36991         
36992         return this.config.length;
36993     },
36994
36995     /**
36996      * Returns the number of columns.
36997      * @return {Number}
36998      */
36999     getColumnCount : function(visibleOnly){
37000         if(visibleOnly === true){
37001             var c = 0;
37002             for(var i = 0, len = this.config.length; i < len; i++){
37003                 if(!this.isHidden(i)){
37004                     c++;
37005                 }
37006             }
37007             return c;
37008         }
37009         return this.config.length;
37010     },
37011
37012     /**
37013      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37014      * @param {Function} fn
37015      * @param {Object} scope (optional)
37016      * @return {Array} result
37017      */
37018     getColumnsBy : function(fn, scope){
37019         var r = [];
37020         for(var i = 0, len = this.config.length; i < len; i++){
37021             var c = this.config[i];
37022             if(fn.call(scope||this, c, i) === true){
37023                 r[r.length] = c;
37024             }
37025         }
37026         return r;
37027     },
37028
37029     /**
37030      * Returns true if the specified column is sortable.
37031      * @param {Number} col The column index
37032      * @return {Boolean}
37033      */
37034     isSortable : function(col){
37035         if(typeof this.config[col].sortable == "undefined"){
37036             return this.defaultSortable;
37037         }
37038         return this.config[col].sortable;
37039     },
37040
37041     /**
37042      * Returns the rendering (formatting) function defined for the column.
37043      * @param {Number} col The column index.
37044      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37045      */
37046     getRenderer : function(col){
37047         if(!this.config[col].renderer){
37048             return Roo.grid.ColumnModel.defaultRenderer;
37049         }
37050         return this.config[col].renderer;
37051     },
37052
37053     /**
37054      * Sets the rendering (formatting) function for a column.
37055      * @param {Number} col The column index
37056      * @param {Function} fn The function to use to process the cell's raw data
37057      * to return HTML markup for the grid view. The render function is called with
37058      * the following parameters:<ul>
37059      * <li>Data value.</li>
37060      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37061      * <li>css A CSS style string to apply to the table cell.</li>
37062      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37063      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37064      * <li>Row index</li>
37065      * <li>Column index</li>
37066      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37067      */
37068     setRenderer : function(col, fn){
37069         this.config[col].renderer = fn;
37070     },
37071
37072     /**
37073      * Returns the width for the specified column.
37074      * @param {Number} col The column index
37075      * @param (optional) {String} gridSize bootstrap width size.
37076      * @return {Number}
37077      */
37078     getColumnWidth : function(col, gridSize)
37079         {
37080                 var cfg = this.config[col];
37081                 
37082                 if (typeof(gridSize) == 'undefined') {
37083                         return cfg.width * 1 || this.defaultWidth;
37084                 }
37085                 if (gridSize === false) { // if we set it..
37086                         return cfg.width || false;
37087                 }
37088                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
37089                 
37090                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
37091                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
37092                                 continue;
37093                         }
37094                         return cfg[ sizes[i] ];
37095                 }
37096                 return 1;
37097                 
37098     },
37099
37100     /**
37101      * Sets the width for a column.
37102      * @param {Number} col The column index
37103      * @param {Number} width The new width
37104      */
37105     setColumnWidth : function(col, width, suppressEvent){
37106         this.config[col].width = width;
37107         this.totalWidth = null;
37108         if(!suppressEvent){
37109              this.fireEvent("widthchange", this, col, width);
37110         }
37111     },
37112
37113     /**
37114      * Returns the total width of all columns.
37115      * @param {Boolean} includeHidden True to include hidden column widths
37116      * @return {Number}
37117      */
37118     getTotalWidth : function(includeHidden){
37119         if(!this.totalWidth){
37120             this.totalWidth = 0;
37121             for(var i = 0, len = this.config.length; i < len; i++){
37122                 if(includeHidden || !this.isHidden(i)){
37123                     this.totalWidth += this.getColumnWidth(i);
37124                 }
37125             }
37126         }
37127         return this.totalWidth;
37128     },
37129
37130     /**
37131      * Returns the header for the specified column.
37132      * @param {Number} col The column index
37133      * @return {String}
37134      */
37135     getColumnHeader : function(col){
37136         return this.config[col].header;
37137     },
37138
37139     /**
37140      * Sets the header for a column.
37141      * @param {Number} col The column index
37142      * @param {String} header The new header
37143      */
37144     setColumnHeader : function(col, header){
37145         this.config[col].header = header;
37146         this.fireEvent("headerchange", this, col, header);
37147     },
37148
37149     /**
37150      * Returns the tooltip for the specified column.
37151      * @param {Number} col The column index
37152      * @return {String}
37153      */
37154     getColumnTooltip : function(col){
37155             return this.config[col].tooltip;
37156     },
37157     /**
37158      * Sets the tooltip for a column.
37159      * @param {Number} col The column index
37160      * @param {String} tooltip The new tooltip
37161      */
37162     setColumnTooltip : function(col, tooltip){
37163             this.config[col].tooltip = tooltip;
37164     },
37165
37166     /**
37167      * Returns the dataIndex for the specified column.
37168      * @param {Number} col The column index
37169      * @return {Number}
37170      */
37171     getDataIndex : function(col){
37172         return this.config[col].dataIndex;
37173     },
37174
37175     /**
37176      * Sets the dataIndex for a column.
37177      * @param {Number} col The column index
37178      * @param {Number} dataIndex The new dataIndex
37179      */
37180     setDataIndex : function(col, dataIndex){
37181         this.config[col].dataIndex = dataIndex;
37182     },
37183
37184     
37185     
37186     /**
37187      * Returns true if the cell is editable.
37188      * @param {Number} colIndex The column index
37189      * @param {Number} rowIndex The row index - this is nto actually used..?
37190      * @return {Boolean}
37191      */
37192     isCellEditable : function(colIndex, rowIndex){
37193         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37194     },
37195
37196     /**
37197      * Returns the editor defined for the cell/column.
37198      * return false or null to disable editing.
37199      * @param {Number} colIndex The column index
37200      * @param {Number} rowIndex The row index
37201      * @return {Object}
37202      */
37203     getCellEditor : function(colIndex, rowIndex){
37204         return this.config[colIndex].editor;
37205     },
37206
37207     /**
37208      * Sets if a column is editable.
37209      * @param {Number} col The column index
37210      * @param {Boolean} editable True if the column is editable
37211      */
37212     setEditable : function(col, editable){
37213         this.config[col].editable = editable;
37214     },
37215
37216
37217     /**
37218      * Returns true if the column is hidden.
37219      * @param {Number} colIndex The column index
37220      * @return {Boolean}
37221      */
37222     isHidden : function(colIndex){
37223         return this.config[colIndex].hidden;
37224     },
37225
37226
37227     /**
37228      * Returns true if the column width cannot be changed
37229      */
37230     isFixed : function(colIndex){
37231         return this.config[colIndex].fixed;
37232     },
37233
37234     /**
37235      * Returns true if the column can be resized
37236      * @return {Boolean}
37237      */
37238     isResizable : function(colIndex){
37239         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37240     },
37241     /**
37242      * Sets if a column is hidden.
37243      * @param {Number} colIndex The column index
37244      * @param {Boolean} hidden True if the column is hidden
37245      */
37246     setHidden : function(colIndex, hidden){
37247         this.config[colIndex].hidden = hidden;
37248         this.totalWidth = null;
37249         this.fireEvent("hiddenchange", this, colIndex, hidden);
37250     },
37251
37252     /**
37253      * Sets the editor for a column.
37254      * @param {Number} col The column index
37255      * @param {Object} editor The editor object
37256      */
37257     setEditor : function(col, editor){
37258         this.config[col].editor = editor;
37259     },
37260     /**
37261      * Add a column (experimental...) - defaults to adding to the end..
37262      * @param {Object} config 
37263     */
37264     addColumn : function(c)
37265     {
37266     
37267         var i = this.config.length;
37268         this.config[i] = c;
37269         
37270         if(typeof c.dataIndex == "undefined"){
37271             c.dataIndex = i;
37272         }
37273         if(typeof c.renderer == "string"){
37274             c.renderer = Roo.util.Format[c.renderer];
37275         }
37276         if(typeof c.id == "undefined"){
37277             c.id = Roo.id();
37278         }
37279         if(c.editor && c.editor.xtype){
37280             c.editor  = Roo.factory(c.editor, Roo.grid);
37281         }
37282         if(c.editor && c.editor.isFormField){
37283             c.editor = new Roo.grid.GridEditor(c.editor);
37284         }
37285         this.lookup[c.id] = c;
37286     }
37287     
37288 });
37289
37290 Roo.grid.ColumnModel.defaultRenderer = function(value)
37291 {
37292     if(typeof value == "object") {
37293         return value;
37294     }
37295         if(typeof value == "string" && value.length < 1){
37296             return "&#160;";
37297         }
37298     
37299         return String.format("{0}", value);
37300 };
37301
37302 // Alias for backwards compatibility
37303 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37304 /*
37305  * Based on:
37306  * Ext JS Library 1.1.1
37307  * Copyright(c) 2006-2007, Ext JS, LLC.
37308  *
37309  * Originally Released Under LGPL - original licence link has changed is not relivant.
37310  *
37311  * Fork - LGPL
37312  * <script type="text/javascript">
37313  */
37314
37315 /**
37316  * @class Roo.grid.AbstractSelectionModel
37317  * @extends Roo.util.Observable
37318  * @abstract
37319  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37320  * implemented by descendant classes.  This class should not be directly instantiated.
37321  * @constructor
37322  */
37323 Roo.grid.AbstractSelectionModel = function(){
37324     this.locked = false;
37325     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37326 };
37327
37328 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37329     /** @ignore Called by the grid automatically. Do not call directly. */
37330     init : function(grid){
37331         this.grid = grid;
37332         this.initEvents();
37333     },
37334
37335     /**
37336      * Locks the selections.
37337      */
37338     lock : function(){
37339         this.locked = true;
37340     },
37341
37342     /**
37343      * Unlocks the selections.
37344      */
37345     unlock : function(){
37346         this.locked = false;
37347     },
37348
37349     /**
37350      * Returns true if the selections are locked.
37351      * @return {Boolean}
37352      */
37353     isLocked : function(){
37354         return this.locked;
37355     }
37356 });/*
37357  * Based on:
37358  * Ext JS Library 1.1.1
37359  * Copyright(c) 2006-2007, Ext JS, LLC.
37360  *
37361  * Originally Released Under LGPL - original licence link has changed is not relivant.
37362  *
37363  * Fork - LGPL
37364  * <script type="text/javascript">
37365  */
37366 /**
37367  * @extends Roo.grid.AbstractSelectionModel
37368  * @class Roo.grid.RowSelectionModel
37369  * The default SelectionModel used by {@link Roo.grid.Grid}.
37370  * It supports multiple selections and keyboard selection/navigation. 
37371  * @constructor
37372  * @param {Object} config
37373  */
37374 Roo.grid.RowSelectionModel = function(config){
37375     Roo.apply(this, config);
37376     this.selections = new Roo.util.MixedCollection(false, function(o){
37377         return o.id;
37378     });
37379
37380     this.last = false;
37381     this.lastActive = false;
37382
37383     this.addEvents({
37384         /**
37385         * @event selectionchange
37386         * Fires when the selection changes
37387         * @param {SelectionModel} this
37388         */
37389        "selectionchange" : true,
37390        /**
37391         * @event afterselectionchange
37392         * Fires after the selection changes (eg. by key press or clicking)
37393         * @param {SelectionModel} this
37394         */
37395        "afterselectionchange" : true,
37396        /**
37397         * @event beforerowselect
37398         * Fires when a row is selected being selected, return false to cancel.
37399         * @param {SelectionModel} this
37400         * @param {Number} rowIndex The selected index
37401         * @param {Boolean} keepExisting False if other selections will be cleared
37402         */
37403        "beforerowselect" : true,
37404        /**
37405         * @event rowselect
37406         * Fires when a row is selected.
37407         * @param {SelectionModel} this
37408         * @param {Number} rowIndex The selected index
37409         * @param {Roo.data.Record} r The record
37410         */
37411        "rowselect" : true,
37412        /**
37413         * @event rowdeselect
37414         * Fires when a row is deselected.
37415         * @param {SelectionModel} this
37416         * @param {Number} rowIndex The selected index
37417         */
37418         "rowdeselect" : true
37419     });
37420     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37421     this.locked = false;
37422 };
37423
37424 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37425     /**
37426      * @cfg {Boolean} singleSelect
37427      * True to allow selection of only one row at a time (defaults to false)
37428      */
37429     singleSelect : false,
37430
37431     // private
37432     initEvents : function(){
37433
37434         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37435             this.grid.on("mousedown", this.handleMouseDown, this);
37436         }else{ // allow click to work like normal
37437             this.grid.on("rowclick", this.handleDragableRowClick, this);
37438         }
37439         // bootstrap does not have a view..
37440         var view = this.grid.view ? this.grid.view : this.grid;
37441         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37442             "up" : function(e){
37443                 if(!e.shiftKey){
37444                     this.selectPrevious(e.shiftKey);
37445                 }else if(this.last !== false && this.lastActive !== false){
37446                     var last = this.last;
37447                     this.selectRange(this.last,  this.lastActive-1);
37448                     view.focusRow(this.lastActive);
37449                     if(last !== false){
37450                         this.last = last;
37451                     }
37452                 }else{
37453                     this.selectFirstRow();
37454                 }
37455                 this.fireEvent("afterselectionchange", this);
37456             },
37457             "down" : function(e){
37458                 if(!e.shiftKey){
37459                     this.selectNext(e.shiftKey);
37460                 }else if(this.last !== false && this.lastActive !== false){
37461                     var last = this.last;
37462                     this.selectRange(this.last,  this.lastActive+1);
37463                     view.focusRow(this.lastActive);
37464                     if(last !== false){
37465                         this.last = last;
37466                     }
37467                 }else{
37468                     this.selectFirstRow();
37469                 }
37470                 this.fireEvent("afterselectionchange", this);
37471             },
37472             scope: this
37473         });
37474
37475          
37476         view.on("refresh", this.onRefresh, this);
37477         view.on("rowupdated", this.onRowUpdated, this);
37478         view.on("rowremoved", this.onRemove, this);
37479     },
37480
37481     // private
37482     onRefresh : function(){
37483         var ds = this.grid.ds, i, v = this.grid.view;
37484         var s = this.selections;
37485         s.each(function(r){
37486             if((i = ds.indexOfId(r.id)) != -1){
37487                 v.onRowSelect(i);
37488                 s.add(ds.getAt(i)); // updating the selection relate data
37489             }else{
37490                 s.remove(r);
37491             }
37492         });
37493     },
37494
37495     // private
37496     onRemove : function(v, index, r){
37497         this.selections.remove(r);
37498     },
37499
37500     // private
37501     onRowUpdated : function(v, index, r){
37502         if(this.isSelected(r)){
37503             v.onRowSelect(index);
37504         }
37505     },
37506
37507     /**
37508      * Select records.
37509      * @param {Array} records The records to select
37510      * @param {Boolean} keepExisting (optional) True to keep existing selections
37511      */
37512     selectRecords : function(records, keepExisting){
37513         if(!keepExisting){
37514             this.clearSelections();
37515         }
37516         var ds = this.grid.ds;
37517         for(var i = 0, len = records.length; i < len; i++){
37518             this.selectRow(ds.indexOf(records[i]), true);
37519         }
37520     },
37521
37522     /**
37523      * Gets the number of selected rows.
37524      * @return {Number}
37525      */
37526     getCount : function(){
37527         return this.selections.length;
37528     },
37529
37530     /**
37531      * Selects the first row in the grid.
37532      */
37533     selectFirstRow : function(){
37534         this.selectRow(0);
37535     },
37536
37537     /**
37538      * Select the last row.
37539      * @param {Boolean} keepExisting (optional) True to keep existing selections
37540      */
37541     selectLastRow : function(keepExisting){
37542         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
37543     },
37544
37545     /**
37546      * Selects the row immediately following the last selected row.
37547      * @param {Boolean} keepExisting (optional) True to keep existing selections
37548      */
37549     selectNext : function(keepExisting){
37550         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
37551             this.selectRow(this.last+1, keepExisting);
37552             var view = this.grid.view ? this.grid.view : this.grid;
37553             view.focusRow(this.last);
37554         }
37555     },
37556
37557     /**
37558      * Selects the row that precedes the last selected row.
37559      * @param {Boolean} keepExisting (optional) True to keep existing selections
37560      */
37561     selectPrevious : function(keepExisting){
37562         if(this.last){
37563             this.selectRow(this.last-1, keepExisting);
37564             var view = this.grid.view ? this.grid.view : this.grid;
37565             view.focusRow(this.last);
37566         }
37567     },
37568
37569     /**
37570      * Returns the selected records
37571      * @return {Array} Array of selected records
37572      */
37573     getSelections : function(){
37574         return [].concat(this.selections.items);
37575     },
37576
37577     /**
37578      * Returns the first selected record.
37579      * @return {Record}
37580      */
37581     getSelected : function(){
37582         return this.selections.itemAt(0);
37583     },
37584
37585
37586     /**
37587      * Clears all selections.
37588      */
37589     clearSelections : function(fast){
37590         if(this.locked) {
37591             return;
37592         }
37593         if(fast !== true){
37594             var ds = this.grid.ds;
37595             var s = this.selections;
37596             s.each(function(r){
37597                 this.deselectRow(ds.indexOfId(r.id));
37598             }, this);
37599             s.clear();
37600         }else{
37601             this.selections.clear();
37602         }
37603         this.last = false;
37604     },
37605
37606
37607     /**
37608      * Selects all rows.
37609      */
37610     selectAll : function(){
37611         if(this.locked) {
37612             return;
37613         }
37614         this.selections.clear();
37615         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
37616             this.selectRow(i, true);
37617         }
37618     },
37619
37620     /**
37621      * Returns True if there is a selection.
37622      * @return {Boolean}
37623      */
37624     hasSelection : function(){
37625         return this.selections.length > 0;
37626     },
37627
37628     /**
37629      * Returns True if the specified row is selected.
37630      * @param {Number/Record} record The record or index of the record to check
37631      * @return {Boolean}
37632      */
37633     isSelected : function(index){
37634         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
37635         return (r && this.selections.key(r.id) ? true : false);
37636     },
37637
37638     /**
37639      * Returns True if the specified record id is selected.
37640      * @param {String} id The id of record to check
37641      * @return {Boolean}
37642      */
37643     isIdSelected : function(id){
37644         return (this.selections.key(id) ? true : false);
37645     },
37646
37647     // private
37648     handleMouseDown : function(e, t)
37649     {
37650         var view = this.grid.view ? this.grid.view : this.grid;
37651         var rowIndex;
37652         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37653             return;
37654         };
37655         if(e.shiftKey && this.last !== false){
37656             var last = this.last;
37657             this.selectRange(last, rowIndex, e.ctrlKey);
37658             this.last = last; // reset the last
37659             view.focusRow(rowIndex);
37660         }else{
37661             var isSelected = this.isSelected(rowIndex);
37662             if(e.button !== 0 && isSelected){
37663                 view.focusRow(rowIndex);
37664             }else if(e.ctrlKey && isSelected){
37665                 this.deselectRow(rowIndex);
37666             }else if(!isSelected){
37667                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37668                 view.focusRow(rowIndex);
37669             }
37670         }
37671         this.fireEvent("afterselectionchange", this);
37672     },
37673     // private
37674     handleDragableRowClick :  function(grid, rowIndex, e) 
37675     {
37676         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37677             this.selectRow(rowIndex, false);
37678             var view = this.grid.view ? this.grid.view : this.grid;
37679             view.focusRow(rowIndex);
37680              this.fireEvent("afterselectionchange", this);
37681         }
37682     },
37683     
37684     /**
37685      * Selects multiple rows.
37686      * @param {Array} rows Array of the indexes of the row to select
37687      * @param {Boolean} keepExisting (optional) True to keep existing selections
37688      */
37689     selectRows : function(rows, keepExisting){
37690         if(!keepExisting){
37691             this.clearSelections();
37692         }
37693         for(var i = 0, len = rows.length; i < len; i++){
37694             this.selectRow(rows[i], true);
37695         }
37696     },
37697
37698     /**
37699      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37700      * @param {Number} startRow The index of the first row in the range
37701      * @param {Number} endRow The index of the last row in the range
37702      * @param {Boolean} keepExisting (optional) True to retain existing selections
37703      */
37704     selectRange : function(startRow, endRow, keepExisting){
37705         if(this.locked) {
37706             return;
37707         }
37708         if(!keepExisting){
37709             this.clearSelections();
37710         }
37711         if(startRow <= endRow){
37712             for(var i = startRow; i <= endRow; i++){
37713                 this.selectRow(i, true);
37714             }
37715         }else{
37716             for(var i = startRow; i >= endRow; i--){
37717                 this.selectRow(i, true);
37718             }
37719         }
37720     },
37721
37722     /**
37723      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37724      * @param {Number} startRow The index of the first row in the range
37725      * @param {Number} endRow The index of the last row in the range
37726      */
37727     deselectRange : function(startRow, endRow, preventViewNotify){
37728         if(this.locked) {
37729             return;
37730         }
37731         for(var i = startRow; i <= endRow; i++){
37732             this.deselectRow(i, preventViewNotify);
37733         }
37734     },
37735
37736     /**
37737      * Selects a row.
37738      * @param {Number} row The index of the row to select
37739      * @param {Boolean} keepExisting (optional) True to keep existing selections
37740      */
37741     selectRow : function(index, keepExisting, preventViewNotify){
37742         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
37743             return;
37744         }
37745         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37746             if(!keepExisting || this.singleSelect){
37747                 this.clearSelections();
37748             }
37749             var r = this.grid.ds.getAt(index);
37750             this.selections.add(r);
37751             this.last = this.lastActive = index;
37752             if(!preventViewNotify){
37753                 var view = this.grid.view ? this.grid.view : this.grid;
37754                 view.onRowSelect(index);
37755             }
37756             this.fireEvent("rowselect", this, index, r);
37757             this.fireEvent("selectionchange", this);
37758         }
37759     },
37760
37761     /**
37762      * Deselects a row.
37763      * @param {Number} row The index of the row to deselect
37764      */
37765     deselectRow : function(index, preventViewNotify){
37766         if(this.locked) {
37767             return;
37768         }
37769         if(this.last == index){
37770             this.last = false;
37771         }
37772         if(this.lastActive == index){
37773             this.lastActive = false;
37774         }
37775         var r = this.grid.ds.getAt(index);
37776         this.selections.remove(r);
37777         if(!preventViewNotify){
37778             var view = this.grid.view ? this.grid.view : this.grid;
37779             view.onRowDeselect(index);
37780         }
37781         this.fireEvent("rowdeselect", this, index);
37782         this.fireEvent("selectionchange", this);
37783     },
37784
37785     // private
37786     restoreLast : function(){
37787         if(this._last){
37788             this.last = this._last;
37789         }
37790     },
37791
37792     // private
37793     acceptsNav : function(row, col, cm){
37794         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37795     },
37796
37797     // private
37798     onEditorKey : function(field, e){
37799         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37800         if(k == e.TAB){
37801             e.stopEvent();
37802             ed.completeEdit();
37803             if(e.shiftKey){
37804                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37805             }else{
37806                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37807             }
37808         }else if(k == e.ENTER && !e.ctrlKey){
37809             e.stopEvent();
37810             ed.completeEdit();
37811             if(e.shiftKey){
37812                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37813             }else{
37814                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37815             }
37816         }else if(k == e.ESC){
37817             ed.cancelEdit();
37818         }
37819         if(newCell){
37820             g.startEditing(newCell[0], newCell[1]);
37821         }
37822     }
37823 });/*
37824  * Based on:
37825  * Ext JS Library 1.1.1
37826  * Copyright(c) 2006-2007, Ext JS, LLC.
37827  *
37828  * Originally Released Under LGPL - original licence link has changed is not relivant.
37829  *
37830  * Fork - LGPL
37831  * <script type="text/javascript">
37832  */
37833 /**
37834  * @class Roo.grid.CellSelectionModel
37835  * @extends Roo.grid.AbstractSelectionModel
37836  * This class provides the basic implementation for cell selection in a grid.
37837  * @constructor
37838  * @param {Object} config The object containing the configuration of this model.
37839  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37840  */
37841 Roo.grid.CellSelectionModel = function(config){
37842     Roo.apply(this, config);
37843
37844     this.selection = null;
37845
37846     this.addEvents({
37847         /**
37848              * @event beforerowselect
37849              * Fires before a cell is selected.
37850              * @param {SelectionModel} this
37851              * @param {Number} rowIndex The selected row index
37852              * @param {Number} colIndex The selected cell index
37853              */
37854             "beforecellselect" : true,
37855         /**
37856              * @event cellselect
37857              * Fires when a cell is selected.
37858              * @param {SelectionModel} this
37859              * @param {Number} rowIndex The selected row index
37860              * @param {Number} colIndex The selected cell index
37861              */
37862             "cellselect" : true,
37863         /**
37864              * @event selectionchange
37865              * Fires when the active selection changes.
37866              * @param {SelectionModel} this
37867              * @param {Object} selection null for no selection or an object (o) with two properties
37868                 <ul>
37869                 <li>o.record: the record object for the row the selection is in</li>
37870                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37871                 </ul>
37872              */
37873             "selectionchange" : true,
37874         /**
37875              * @event tabend
37876              * Fires when the tab (or enter) was pressed on the last editable cell
37877              * You can use this to trigger add new row.
37878              * @param {SelectionModel} this
37879              */
37880             "tabend" : true,
37881          /**
37882              * @event beforeeditnext
37883              * Fires before the next editable sell is made active
37884              * You can use this to skip to another cell or fire the tabend
37885              *    if you set cell to false
37886              * @param {Object} eventdata object : { cell : [ row, col ] } 
37887              */
37888             "beforeeditnext" : true
37889     });
37890     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37891 };
37892
37893 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37894     
37895     enter_is_tab: false,
37896
37897     /** @ignore */
37898     initEvents : function(){
37899         this.grid.on("mousedown", this.handleMouseDown, this);
37900         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37901         var view = this.grid.view;
37902         view.on("refresh", this.onViewChange, this);
37903         view.on("rowupdated", this.onRowUpdated, this);
37904         view.on("beforerowremoved", this.clearSelections, this);
37905         view.on("beforerowsinserted", this.clearSelections, this);
37906         if(this.grid.isEditor){
37907             this.grid.on("beforeedit", this.beforeEdit,  this);
37908         }
37909     },
37910
37911         //private
37912     beforeEdit : function(e){
37913         this.select(e.row, e.column, false, true, e.record);
37914     },
37915
37916         //private
37917     onRowUpdated : function(v, index, r){
37918         if(this.selection && this.selection.record == r){
37919             v.onCellSelect(index, this.selection.cell[1]);
37920         }
37921     },
37922
37923         //private
37924     onViewChange : function(){
37925         this.clearSelections(true);
37926     },
37927
37928         /**
37929          * Returns the currently selected cell,.
37930          * @return {Array} The selected cell (row, column) or null if none selected.
37931          */
37932     getSelectedCell : function(){
37933         return this.selection ? this.selection.cell : null;
37934     },
37935
37936     /**
37937      * Clears all selections.
37938      * @param {Boolean} true to prevent the gridview from being notified about the change.
37939      */
37940     clearSelections : function(preventNotify){
37941         var s = this.selection;
37942         if(s){
37943             if(preventNotify !== true){
37944                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37945             }
37946             this.selection = null;
37947             this.fireEvent("selectionchange", this, null);
37948         }
37949     },
37950
37951     /**
37952      * Returns true if there is a selection.
37953      * @return {Boolean}
37954      */
37955     hasSelection : function(){
37956         return this.selection ? true : false;
37957     },
37958
37959     /** @ignore */
37960     handleMouseDown : function(e, t){
37961         var v = this.grid.getView();
37962         if(this.isLocked()){
37963             return;
37964         };
37965         var row = v.findRowIndex(t);
37966         var cell = v.findCellIndex(t);
37967         if(row !== false && cell !== false){
37968             this.select(row, cell);
37969         }
37970     },
37971
37972     /**
37973      * Selects a cell.
37974      * @param {Number} rowIndex
37975      * @param {Number} collIndex
37976      */
37977     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37978         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37979             this.clearSelections();
37980             r = r || this.grid.dataSource.getAt(rowIndex);
37981             this.selection = {
37982                 record : r,
37983                 cell : [rowIndex, colIndex]
37984             };
37985             if(!preventViewNotify){
37986                 var v = this.grid.getView();
37987                 v.onCellSelect(rowIndex, colIndex);
37988                 if(preventFocus !== true){
37989                     v.focusCell(rowIndex, colIndex);
37990                 }
37991             }
37992             this.fireEvent("cellselect", this, rowIndex, colIndex);
37993             this.fireEvent("selectionchange", this, this.selection);
37994         }
37995     },
37996
37997         //private
37998     isSelectable : function(rowIndex, colIndex, cm){
37999         return !cm.isHidden(colIndex);
38000     },
38001
38002     /** @ignore */
38003     handleKeyDown : function(e){
38004         //Roo.log('Cell Sel Model handleKeyDown');
38005         if(!e.isNavKeyPress()){
38006             return;
38007         }
38008         var g = this.grid, s = this.selection;
38009         if(!s){
38010             e.stopEvent();
38011             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38012             if(cell){
38013                 this.select(cell[0], cell[1]);
38014             }
38015             return;
38016         }
38017         var sm = this;
38018         var walk = function(row, col, step){
38019             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38020         };
38021         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38022         var newCell;
38023
38024       
38025
38026         switch(k){
38027             case e.TAB:
38028                 // handled by onEditorKey
38029                 if (g.isEditor && g.editing) {
38030                     return;
38031                 }
38032                 if(e.shiftKey) {
38033                     newCell = walk(r, c-1, -1);
38034                 } else {
38035                     newCell = walk(r, c+1, 1);
38036                 }
38037                 break;
38038             
38039             case e.DOWN:
38040                newCell = walk(r+1, c, 1);
38041                 break;
38042             
38043             case e.UP:
38044                 newCell = walk(r-1, c, -1);
38045                 break;
38046             
38047             case e.RIGHT:
38048                 newCell = walk(r, c+1, 1);
38049                 break;
38050             
38051             case e.LEFT:
38052                 newCell = walk(r, c-1, -1);
38053                 break;
38054             
38055             case e.ENTER:
38056                 
38057                 if(g.isEditor && !g.editing){
38058                    g.startEditing(r, c);
38059                    e.stopEvent();
38060                    return;
38061                 }
38062                 
38063                 
38064              break;
38065         };
38066         if(newCell){
38067             this.select(newCell[0], newCell[1]);
38068             e.stopEvent();
38069             
38070         }
38071     },
38072
38073     acceptsNav : function(row, col, cm){
38074         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38075     },
38076     /**
38077      * Selects a cell.
38078      * @param {Number} field (not used) - as it's normally used as a listener
38079      * @param {Number} e - event - fake it by using
38080      *
38081      * var e = Roo.EventObjectImpl.prototype;
38082      * e.keyCode = e.TAB
38083      *
38084      * 
38085      */
38086     onEditorKey : function(field, e){
38087         
38088         var k = e.getKey(),
38089             newCell,
38090             g = this.grid,
38091             ed = g.activeEditor,
38092             forward = false;
38093         ///Roo.log('onEditorKey' + k);
38094         
38095         
38096         if (this.enter_is_tab && k == e.ENTER) {
38097             k = e.TAB;
38098         }
38099         
38100         if(k == e.TAB){
38101             if(e.shiftKey){
38102                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38103             }else{
38104                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38105                 forward = true;
38106             }
38107             
38108             e.stopEvent();
38109             
38110         } else if(k == e.ENTER &&  !e.ctrlKey){
38111             ed.completeEdit();
38112             e.stopEvent();
38113             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38114         
38115                 } else if(k == e.ESC){
38116             ed.cancelEdit();
38117         }
38118                 
38119         if (newCell) {
38120             var ecall = { cell : newCell, forward : forward };
38121             this.fireEvent('beforeeditnext', ecall );
38122             newCell = ecall.cell;
38123                         forward = ecall.forward;
38124         }
38125                 
38126         if(newCell){
38127             //Roo.log('next cell after edit');
38128             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38129         } else if (forward) {
38130             // tabbed past last
38131             this.fireEvent.defer(100, this, ['tabend',this]);
38132         }
38133     }
38134 });/*
38135  * Based on:
38136  * Ext JS Library 1.1.1
38137  * Copyright(c) 2006-2007, Ext JS, LLC.
38138  *
38139  * Originally Released Under LGPL - original licence link has changed is not relivant.
38140  *
38141  * Fork - LGPL
38142  * <script type="text/javascript">
38143  */
38144  
38145 /**
38146  * @class Roo.grid.EditorGrid
38147  * @extends Roo.grid.Grid
38148  * Class for creating and editable grid.
38149  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38150  * The container MUST have some type of size defined for the grid to fill. The container will be 
38151  * automatically set to position relative if it isn't already.
38152  * @param {Object} dataSource The data model to bind to
38153  * @param {Object} colModel The column model with info about this grid's columns
38154  */
38155 Roo.grid.EditorGrid = function(container, config){
38156     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38157     this.getGridEl().addClass("xedit-grid");
38158
38159     if(!this.selModel){
38160         this.selModel = new Roo.grid.CellSelectionModel();
38161     }
38162
38163     this.activeEditor = null;
38164
38165         this.addEvents({
38166             /**
38167              * @event beforeedit
38168              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38169              * <ul style="padding:5px;padding-left:16px;">
38170              * <li>grid - This grid</li>
38171              * <li>record - The record being edited</li>
38172              * <li>field - The field name being edited</li>
38173              * <li>value - The value for the field being edited.</li>
38174              * <li>row - The grid row index</li>
38175              * <li>column - The grid column index</li>
38176              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38177              * </ul>
38178              * @param {Object} e An edit event (see above for description)
38179              */
38180             "beforeedit" : true,
38181             /**
38182              * @event afteredit
38183              * Fires after a cell is edited. <br />
38184              * <ul style="padding:5px;padding-left:16px;">
38185              * <li>grid - This grid</li>
38186              * <li>record - The record being edited</li>
38187              * <li>field - The field name being edited</li>
38188              * <li>value - The value being set</li>
38189              * <li>originalValue - The original value for the field, before the edit.</li>
38190              * <li>row - The grid row index</li>
38191              * <li>column - The grid column index</li>
38192              * </ul>
38193              * @param {Object} e An edit event (see above for description)
38194              */
38195             "afteredit" : true,
38196             /**
38197              * @event validateedit
38198              * Fires after a cell is edited, but before the value is set in the record. 
38199          * You can use this to modify the value being set in the field, Return false
38200              * to cancel the change. The edit event object has the following properties <br />
38201              * <ul style="padding:5px;padding-left:16px;">
38202          * <li>editor - This editor</li>
38203              * <li>grid - This grid</li>
38204              * <li>record - The record being edited</li>
38205              * <li>field - The field name being edited</li>
38206              * <li>value - The value being set</li>
38207              * <li>originalValue - The original value for the field, before the edit.</li>
38208              * <li>row - The grid row index</li>
38209              * <li>column - The grid column index</li>
38210              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38211              * </ul>
38212              * @param {Object} e An edit event (see above for description)
38213              */
38214             "validateedit" : true
38215         });
38216     this.on("bodyscroll", this.stopEditing,  this);
38217     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38218 };
38219
38220 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38221     /**
38222      * @cfg {Number} clicksToEdit
38223      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38224      */
38225     clicksToEdit: 2,
38226
38227     // private
38228     isEditor : true,
38229     // private
38230     trackMouseOver: false, // causes very odd FF errors
38231
38232     onCellDblClick : function(g, row, col){
38233         this.startEditing(row, col);
38234     },
38235
38236     onEditComplete : function(ed, value, startValue){
38237         this.editing = false;
38238         this.activeEditor = null;
38239         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38240         var r = ed.record;
38241         var field = this.colModel.getDataIndex(ed.col);
38242         var e = {
38243             grid: this,
38244             record: r,
38245             field: field,
38246             originalValue: startValue,
38247             value: value,
38248             row: ed.row,
38249             column: ed.col,
38250             cancel:false,
38251             editor: ed
38252         };
38253         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
38254         cell.show();
38255           
38256         if(String(value) !== String(startValue)){
38257             
38258             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38259                 r.set(field, e.value);
38260                 // if we are dealing with a combo box..
38261                 // then we also set the 'name' colum to be the displayField
38262                 if (ed.field.displayField && ed.field.name) {
38263                     r.set(ed.field.name, ed.field.el.dom.value);
38264                 }
38265                 
38266                 delete e.cancel; //?? why!!!
38267                 this.fireEvent("afteredit", e);
38268             }
38269         } else {
38270             this.fireEvent("afteredit", e); // always fire it!
38271         }
38272         this.view.focusCell(ed.row, ed.col);
38273     },
38274
38275     /**
38276      * Starts editing the specified for the specified row/column
38277      * @param {Number} rowIndex
38278      * @param {Number} colIndex
38279      */
38280     startEditing : function(row, col){
38281         this.stopEditing();
38282         if(this.colModel.isCellEditable(col, row)){
38283             this.view.ensureVisible(row, col, true);
38284           
38285             var r = this.dataSource.getAt(row);
38286             var field = this.colModel.getDataIndex(col);
38287             var cell = Roo.get(this.view.getCell(row,col));
38288             var e = {
38289                 grid: this,
38290                 record: r,
38291                 field: field,
38292                 value: r.data[field],
38293                 row: row,
38294                 column: col,
38295                 cancel:false 
38296             };
38297             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38298                 this.editing = true;
38299                 var ed = this.colModel.getCellEditor(col, row);
38300                 
38301                 if (!ed) {
38302                     return;
38303                 }
38304                 if(!ed.rendered){
38305                     ed.render(ed.parentEl || document.body);
38306                 }
38307                 ed.field.reset();
38308                
38309                 cell.hide();
38310                 
38311                 (function(){ // complex but required for focus issues in safari, ie and opera
38312                     ed.row = row;
38313                     ed.col = col;
38314                     ed.record = r;
38315                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38316                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38317                     this.activeEditor = ed;
38318                     var v = r.data[field];
38319                     ed.startEdit(this.view.getCell(row, col), v);
38320                     // combo's with 'displayField and name set
38321                     if (ed.field.displayField && ed.field.name) {
38322                         ed.field.el.dom.value = r.data[ed.field.name];
38323                     }
38324                     
38325                     
38326                 }).defer(50, this);
38327             }
38328         }
38329     },
38330         
38331     /**
38332      * Stops any active editing
38333      */
38334     stopEditing : function(){
38335         if(this.activeEditor){
38336             this.activeEditor.completeEdit();
38337         }
38338         this.activeEditor = null;
38339     },
38340         
38341          /**
38342      * Called to get grid's drag proxy text, by default returns this.ddText.
38343      * @return {String}
38344      */
38345     getDragDropText : function(){
38346         var count = this.selModel.getSelectedCell() ? 1 : 0;
38347         return String.format(this.ddText, count, count == 1 ? '' : 's');
38348     }
38349         
38350 });/*
38351  * Based on:
38352  * Ext JS Library 1.1.1
38353  * Copyright(c) 2006-2007, Ext JS, LLC.
38354  *
38355  * Originally Released Under LGPL - original licence link has changed is not relivant.
38356  *
38357  * Fork - LGPL
38358  * <script type="text/javascript">
38359  */
38360
38361 // private - not really -- you end up using it !
38362 // This is a support class used internally by the Grid components
38363
38364 /**
38365  * @class Roo.grid.GridEditor
38366  * @extends Roo.Editor
38367  * Class for creating and editable grid elements.
38368  * @param {Object} config any settings (must include field)
38369  */
38370 Roo.grid.GridEditor = function(field, config){
38371     if (!config && field.field) {
38372         config = field;
38373         field = Roo.factory(config.field, Roo.form);
38374     }
38375     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38376     field.monitorTab = false;
38377 };
38378
38379 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38380     
38381     /**
38382      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38383      */
38384     
38385     alignment: "tl-tl",
38386     autoSize: "width",
38387     hideEl : false,
38388     cls: "x-small-editor x-grid-editor",
38389     shim:false,
38390     shadow:"frame"
38391 });/*
38392  * Based on:
38393  * Ext JS Library 1.1.1
38394  * Copyright(c) 2006-2007, Ext JS, LLC.
38395  *
38396  * Originally Released Under LGPL - original licence link has changed is not relivant.
38397  *
38398  * Fork - LGPL
38399  * <script type="text/javascript">
38400  */
38401   
38402
38403   
38404 Roo.grid.PropertyRecord = Roo.data.Record.create([
38405     {name:'name',type:'string'},  'value'
38406 ]);
38407
38408
38409 Roo.grid.PropertyStore = function(grid, source){
38410     this.grid = grid;
38411     this.store = new Roo.data.Store({
38412         recordType : Roo.grid.PropertyRecord
38413     });
38414     this.store.on('update', this.onUpdate,  this);
38415     if(source){
38416         this.setSource(source);
38417     }
38418     Roo.grid.PropertyStore.superclass.constructor.call(this);
38419 };
38420
38421
38422
38423 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38424     setSource : function(o){
38425         this.source = o;
38426         this.store.removeAll();
38427         var data = [];
38428         for(var k in o){
38429             if(this.isEditableValue(o[k])){
38430                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38431             }
38432         }
38433         this.store.loadRecords({records: data}, {}, true);
38434     },
38435
38436     onUpdate : function(ds, record, type){
38437         if(type == Roo.data.Record.EDIT){
38438             var v = record.data['value'];
38439             var oldValue = record.modified['value'];
38440             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38441                 this.source[record.id] = v;
38442                 record.commit();
38443                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38444             }else{
38445                 record.reject();
38446             }
38447         }
38448     },
38449
38450     getProperty : function(row){
38451        return this.store.getAt(row);
38452     },
38453
38454     isEditableValue: function(val){
38455         if(val && val instanceof Date){
38456             return true;
38457         }else if(typeof val == 'object' || typeof val == 'function'){
38458             return false;
38459         }
38460         return true;
38461     },
38462
38463     setValue : function(prop, value){
38464         this.source[prop] = value;
38465         this.store.getById(prop).set('value', value);
38466     },
38467
38468     getSource : function(){
38469         return this.source;
38470     }
38471 });
38472
38473 Roo.grid.PropertyColumnModel = function(grid, store){
38474     this.grid = grid;
38475     var g = Roo.grid;
38476     g.PropertyColumnModel.superclass.constructor.call(this, [
38477         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38478         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38479     ]);
38480     this.store = store;
38481     this.bselect = Roo.DomHelper.append(document.body, {
38482         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38483             {tag: 'option', value: 'true', html: 'true'},
38484             {tag: 'option', value: 'false', html: 'false'}
38485         ]
38486     });
38487     Roo.id(this.bselect);
38488     var f = Roo.form;
38489     this.editors = {
38490         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38491         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38492         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38493         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38494         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38495     };
38496     this.renderCellDelegate = this.renderCell.createDelegate(this);
38497     this.renderPropDelegate = this.renderProp.createDelegate(this);
38498 };
38499
38500 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38501     
38502     
38503     nameText : 'Name',
38504     valueText : 'Value',
38505     
38506     dateFormat : 'm/j/Y',
38507     
38508     
38509     renderDate : function(dateVal){
38510         return dateVal.dateFormat(this.dateFormat);
38511     },
38512
38513     renderBool : function(bVal){
38514         return bVal ? 'true' : 'false';
38515     },
38516
38517     isCellEditable : function(colIndex, rowIndex){
38518         return colIndex == 1;
38519     },
38520
38521     getRenderer : function(col){
38522         return col == 1 ?
38523             this.renderCellDelegate : this.renderPropDelegate;
38524     },
38525
38526     renderProp : function(v){
38527         return this.getPropertyName(v);
38528     },
38529
38530     renderCell : function(val){
38531         var rv = val;
38532         if(val instanceof Date){
38533             rv = this.renderDate(val);
38534         }else if(typeof val == 'boolean'){
38535             rv = this.renderBool(val);
38536         }
38537         return Roo.util.Format.htmlEncode(rv);
38538     },
38539
38540     getPropertyName : function(name){
38541         var pn = this.grid.propertyNames;
38542         return pn && pn[name] ? pn[name] : name;
38543     },
38544
38545     getCellEditor : function(colIndex, rowIndex){
38546         var p = this.store.getProperty(rowIndex);
38547         var n = p.data['name'], val = p.data['value'];
38548         
38549         if(typeof(this.grid.customEditors[n]) == 'string'){
38550             return this.editors[this.grid.customEditors[n]];
38551         }
38552         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38553             return this.grid.customEditors[n];
38554         }
38555         if(val instanceof Date){
38556             return this.editors['date'];
38557         }else if(typeof val == 'number'){
38558             return this.editors['number'];
38559         }else if(typeof val == 'boolean'){
38560             return this.editors['boolean'];
38561         }else{
38562             return this.editors['string'];
38563         }
38564     }
38565 });
38566
38567 /**
38568  * @class Roo.grid.PropertyGrid
38569  * @extends Roo.grid.EditorGrid
38570  * This class represents the  interface of a component based property grid control.
38571  * <br><br>Usage:<pre><code>
38572  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38573       
38574  });
38575  // set any options
38576  grid.render();
38577  * </code></pre>
38578   
38579  * @constructor
38580  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38581  * The container MUST have some type of size defined for the grid to fill. The container will be
38582  * automatically set to position relative if it isn't already.
38583  * @param {Object} config A config object that sets properties on this grid.
38584  */
38585 Roo.grid.PropertyGrid = function(container, config){
38586     config = config || {};
38587     var store = new Roo.grid.PropertyStore(this);
38588     this.store = store;
38589     var cm = new Roo.grid.PropertyColumnModel(this, store);
38590     store.store.sort('name', 'ASC');
38591     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38592         ds: store.store,
38593         cm: cm,
38594         enableColLock:false,
38595         enableColumnMove:false,
38596         stripeRows:false,
38597         trackMouseOver: false,
38598         clicksToEdit:1
38599     }, config));
38600     this.getGridEl().addClass('x-props-grid');
38601     this.lastEditRow = null;
38602     this.on('columnresize', this.onColumnResize, this);
38603     this.addEvents({
38604          /**
38605              * @event beforepropertychange
38606              * Fires before a property changes (return false to stop?)
38607              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38608              * @param {String} id Record Id
38609              * @param {String} newval New Value
38610          * @param {String} oldval Old Value
38611              */
38612         "beforepropertychange": true,
38613         /**
38614              * @event propertychange
38615              * Fires after a property changes
38616              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38617              * @param {String} id Record Id
38618              * @param {String} newval New Value
38619          * @param {String} oldval Old Value
38620              */
38621         "propertychange": true
38622     });
38623     this.customEditors = this.customEditors || {};
38624 };
38625 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38626     
38627      /**
38628      * @cfg {Object} customEditors map of colnames=> custom editors.
38629      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38630      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38631      * false disables editing of the field.
38632          */
38633     
38634       /**
38635      * @cfg {Object} propertyNames map of property Names to their displayed value
38636          */
38637     
38638     render : function(){
38639         Roo.grid.PropertyGrid.superclass.render.call(this);
38640         this.autoSize.defer(100, this);
38641     },
38642
38643     autoSize : function(){
38644         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38645         if(this.view){
38646             this.view.fitColumns();
38647         }
38648     },
38649
38650     onColumnResize : function(){
38651         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38652         this.autoSize();
38653     },
38654     /**
38655      * Sets the data for the Grid
38656      * accepts a Key => Value object of all the elements avaiable.
38657      * @param {Object} data  to appear in grid.
38658      */
38659     setSource : function(source){
38660         this.store.setSource(source);
38661         //this.autoSize();
38662     },
38663     /**
38664      * Gets all the data from the grid.
38665      * @return {Object} data  data stored in grid
38666      */
38667     getSource : function(){
38668         return this.store.getSource();
38669     }
38670 });/*
38671   
38672  * Licence LGPL
38673  
38674  */
38675  
38676 /**
38677  * @class Roo.grid.Calendar
38678  * @extends Roo.grid.Grid
38679  * This class extends the Grid to provide a calendar widget
38680  * <br><br>Usage:<pre><code>
38681  var grid = new Roo.grid.Calendar("my-container-id", {
38682      ds: myDataStore,
38683      cm: myColModel,
38684      selModel: mySelectionModel,
38685      autoSizeColumns: true,
38686      monitorWindowResize: false,
38687      trackMouseOver: true
38688      eventstore : real data store..
38689  });
38690  // set any options
38691  grid.render();
38692   
38693   * @constructor
38694  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38695  * The container MUST have some type of size defined for the grid to fill. The container will be
38696  * automatically set to position relative if it isn't already.
38697  * @param {Object} config A config object that sets properties on this grid.
38698  */
38699 Roo.grid.Calendar = function(container, config){
38700         // initialize the container
38701         this.container = Roo.get(container);
38702         this.container.update("");
38703         this.container.setStyle("overflow", "hidden");
38704     this.container.addClass('x-grid-container');
38705
38706     this.id = this.container.id;
38707
38708     Roo.apply(this, config);
38709     // check and correct shorthanded configs
38710     
38711     var rows = [];
38712     var d =1;
38713     for (var r = 0;r < 6;r++) {
38714         
38715         rows[r]=[];
38716         for (var c =0;c < 7;c++) {
38717             rows[r][c]= '';
38718         }
38719     }
38720     if (this.eventStore) {
38721         this.eventStore= Roo.factory(this.eventStore, Roo.data);
38722         this.eventStore.on('load',this.onLoad, this);
38723         this.eventStore.on('beforeload',this.clearEvents, this);
38724          
38725     }
38726     
38727     this.dataSource = new Roo.data.Store({
38728             proxy: new Roo.data.MemoryProxy(rows),
38729             reader: new Roo.data.ArrayReader({}, [
38730                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
38731     });
38732
38733     this.dataSource.load();
38734     this.ds = this.dataSource;
38735     this.ds.xmodule = this.xmodule || false;
38736     
38737     
38738     var cellRender = function(v,x,r)
38739     {
38740         return String.format(
38741             '<div class="fc-day  fc-widget-content"><div>' +
38742                 '<div class="fc-event-container"></div>' +
38743                 '<div class="fc-day-number">{0}</div>'+
38744                 
38745                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
38746             '</div></div>', v);
38747     
38748     }
38749     
38750     
38751     this.colModel = new Roo.grid.ColumnModel( [
38752         {
38753             xtype: 'ColumnModel',
38754             xns: Roo.grid,
38755             dataIndex : 'weekday0',
38756             header : 'Sunday',
38757             renderer : cellRender
38758         },
38759         {
38760             xtype: 'ColumnModel',
38761             xns: Roo.grid,
38762             dataIndex : 'weekday1',
38763             header : 'Monday',
38764             renderer : cellRender
38765         },
38766         {
38767             xtype: 'ColumnModel',
38768             xns: Roo.grid,
38769             dataIndex : 'weekday2',
38770             header : 'Tuesday',
38771             renderer : cellRender
38772         },
38773         {
38774             xtype: 'ColumnModel',
38775             xns: Roo.grid,
38776             dataIndex : 'weekday3',
38777             header : 'Wednesday',
38778             renderer : cellRender
38779         },
38780         {
38781             xtype: 'ColumnModel',
38782             xns: Roo.grid,
38783             dataIndex : 'weekday4',
38784             header : 'Thursday',
38785             renderer : cellRender
38786         },
38787         {
38788             xtype: 'ColumnModel',
38789             xns: Roo.grid,
38790             dataIndex : 'weekday5',
38791             header : 'Friday',
38792             renderer : cellRender
38793         },
38794         {
38795             xtype: 'ColumnModel',
38796             xns: Roo.grid,
38797             dataIndex : 'weekday6',
38798             header : 'Saturday',
38799             renderer : cellRender
38800         }
38801     ]);
38802     this.cm = this.colModel;
38803     this.cm.xmodule = this.xmodule || false;
38804  
38805         
38806           
38807     //this.selModel = new Roo.grid.CellSelectionModel();
38808     //this.sm = this.selModel;
38809     //this.selModel.init(this);
38810     
38811     
38812     if(this.width){
38813         this.container.setWidth(this.width);
38814     }
38815
38816     if(this.height){
38817         this.container.setHeight(this.height);
38818     }
38819     /** @private */
38820         this.addEvents({
38821         // raw events
38822         /**
38823          * @event click
38824          * The raw click event for the entire grid.
38825          * @param {Roo.EventObject} e
38826          */
38827         "click" : true,
38828         /**
38829          * @event dblclick
38830          * The raw dblclick event for the entire grid.
38831          * @param {Roo.EventObject} e
38832          */
38833         "dblclick" : true,
38834         /**
38835          * @event contextmenu
38836          * The raw contextmenu event for the entire grid.
38837          * @param {Roo.EventObject} e
38838          */
38839         "contextmenu" : true,
38840         /**
38841          * @event mousedown
38842          * The raw mousedown event for the entire grid.
38843          * @param {Roo.EventObject} e
38844          */
38845         "mousedown" : true,
38846         /**
38847          * @event mouseup
38848          * The raw mouseup event for the entire grid.
38849          * @param {Roo.EventObject} e
38850          */
38851         "mouseup" : true,
38852         /**
38853          * @event mouseover
38854          * The raw mouseover event for the entire grid.
38855          * @param {Roo.EventObject} e
38856          */
38857         "mouseover" : true,
38858         /**
38859          * @event mouseout
38860          * The raw mouseout event for the entire grid.
38861          * @param {Roo.EventObject} e
38862          */
38863         "mouseout" : true,
38864         /**
38865          * @event keypress
38866          * The raw keypress event for the entire grid.
38867          * @param {Roo.EventObject} e
38868          */
38869         "keypress" : true,
38870         /**
38871          * @event keydown
38872          * The raw keydown event for the entire grid.
38873          * @param {Roo.EventObject} e
38874          */
38875         "keydown" : true,
38876
38877         // custom events
38878
38879         /**
38880          * @event cellclick
38881          * Fires when a cell is clicked
38882          * @param {Grid} this
38883          * @param {Number} rowIndex
38884          * @param {Number} columnIndex
38885          * @param {Roo.EventObject} e
38886          */
38887         "cellclick" : true,
38888         /**
38889          * @event celldblclick
38890          * Fires when a cell is double clicked
38891          * @param {Grid} this
38892          * @param {Number} rowIndex
38893          * @param {Number} columnIndex
38894          * @param {Roo.EventObject} e
38895          */
38896         "celldblclick" : true,
38897         /**
38898          * @event rowclick
38899          * Fires when a row is clicked
38900          * @param {Grid} this
38901          * @param {Number} rowIndex
38902          * @param {Roo.EventObject} e
38903          */
38904         "rowclick" : true,
38905         /**
38906          * @event rowdblclick
38907          * Fires when a row is double clicked
38908          * @param {Grid} this
38909          * @param {Number} rowIndex
38910          * @param {Roo.EventObject} e
38911          */
38912         "rowdblclick" : true,
38913         /**
38914          * @event headerclick
38915          * Fires when a header is clicked
38916          * @param {Grid} this
38917          * @param {Number} columnIndex
38918          * @param {Roo.EventObject} e
38919          */
38920         "headerclick" : true,
38921         /**
38922          * @event headerdblclick
38923          * Fires when a header cell is double clicked
38924          * @param {Grid} this
38925          * @param {Number} columnIndex
38926          * @param {Roo.EventObject} e
38927          */
38928         "headerdblclick" : true,
38929         /**
38930          * @event rowcontextmenu
38931          * Fires when a row is right clicked
38932          * @param {Grid} this
38933          * @param {Number} rowIndex
38934          * @param {Roo.EventObject} e
38935          */
38936         "rowcontextmenu" : true,
38937         /**
38938          * @event cellcontextmenu
38939          * Fires when a cell is right clicked
38940          * @param {Grid} this
38941          * @param {Number} rowIndex
38942          * @param {Number} cellIndex
38943          * @param {Roo.EventObject} e
38944          */
38945          "cellcontextmenu" : true,
38946         /**
38947          * @event headercontextmenu
38948          * Fires when a header is right clicked
38949          * @param {Grid} this
38950          * @param {Number} columnIndex
38951          * @param {Roo.EventObject} e
38952          */
38953         "headercontextmenu" : true,
38954         /**
38955          * @event bodyscroll
38956          * Fires when the body element is scrolled
38957          * @param {Number} scrollLeft
38958          * @param {Number} scrollTop
38959          */
38960         "bodyscroll" : true,
38961         /**
38962          * @event columnresize
38963          * Fires when the user resizes a column
38964          * @param {Number} columnIndex
38965          * @param {Number} newSize
38966          */
38967         "columnresize" : true,
38968         /**
38969          * @event columnmove
38970          * Fires when the user moves a column
38971          * @param {Number} oldIndex
38972          * @param {Number} newIndex
38973          */
38974         "columnmove" : true,
38975         /**
38976          * @event startdrag
38977          * Fires when row(s) start being dragged
38978          * @param {Grid} this
38979          * @param {Roo.GridDD} dd The drag drop object
38980          * @param {event} e The raw browser event
38981          */
38982         "startdrag" : true,
38983         /**
38984          * @event enddrag
38985          * Fires when a drag operation is complete
38986          * @param {Grid} this
38987          * @param {Roo.GridDD} dd The drag drop object
38988          * @param {event} e The raw browser event
38989          */
38990         "enddrag" : true,
38991         /**
38992          * @event dragdrop
38993          * Fires when dragged row(s) are dropped on a valid DD target
38994          * @param {Grid} this
38995          * @param {Roo.GridDD} dd The drag drop object
38996          * @param {String} targetId The target drag drop object
38997          * @param {event} e The raw browser event
38998          */
38999         "dragdrop" : true,
39000         /**
39001          * @event dragover
39002          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
39003          * @param {Grid} this
39004          * @param {Roo.GridDD} dd The drag drop object
39005          * @param {String} targetId The target drag drop object
39006          * @param {event} e The raw browser event
39007          */
39008         "dragover" : true,
39009         /**
39010          * @event dragenter
39011          *  Fires when the dragged row(s) first cross another DD target while being dragged
39012          * @param {Grid} this
39013          * @param {Roo.GridDD} dd The drag drop object
39014          * @param {String} targetId The target drag drop object
39015          * @param {event} e The raw browser event
39016          */
39017         "dragenter" : true,
39018         /**
39019          * @event dragout
39020          * Fires when the dragged row(s) leave another DD target while being dragged
39021          * @param {Grid} this
39022          * @param {Roo.GridDD} dd The drag drop object
39023          * @param {String} targetId The target drag drop object
39024          * @param {event} e The raw browser event
39025          */
39026         "dragout" : true,
39027         /**
39028          * @event rowclass
39029          * Fires when a row is rendered, so you can change add a style to it.
39030          * @param {GridView} gridview   The grid view
39031          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
39032          */
39033         'rowclass' : true,
39034
39035         /**
39036          * @event render
39037          * Fires when the grid is rendered
39038          * @param {Grid} grid
39039          */
39040         'render' : true,
39041             /**
39042              * @event select
39043              * Fires when a date is selected
39044              * @param {DatePicker} this
39045              * @param {Date} date The selected date
39046              */
39047         'select': true,
39048         /**
39049              * @event monthchange
39050              * Fires when the displayed month changes 
39051              * @param {DatePicker} this
39052              * @param {Date} date The selected month
39053              */
39054         'monthchange': true,
39055         /**
39056              * @event evententer
39057              * Fires when mouse over an event
39058              * @param {Calendar} this
39059              * @param {event} Event
39060              */
39061         'evententer': true,
39062         /**
39063              * @event eventleave
39064              * Fires when the mouse leaves an
39065              * @param {Calendar} this
39066              * @param {event}
39067              */
39068         'eventleave': true,
39069         /**
39070              * @event eventclick
39071              * Fires when the mouse click an
39072              * @param {Calendar} this
39073              * @param {event}
39074              */
39075         'eventclick': true,
39076         /**
39077              * @event eventrender
39078              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
39079              * @param {Calendar} this
39080              * @param {data} data to be modified
39081              */
39082         'eventrender': true
39083         
39084     });
39085
39086     Roo.grid.Grid.superclass.constructor.call(this);
39087     this.on('render', function() {
39088         this.view.el.addClass('x-grid-cal'); 
39089         
39090         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
39091
39092     },this);
39093     
39094     if (!Roo.grid.Calendar.style) {
39095         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
39096             
39097             
39098             '.x-grid-cal .x-grid-col' :  {
39099                 height: 'auto !important',
39100                 'vertical-align': 'top'
39101             },
39102             '.x-grid-cal  .fc-event-hori' : {
39103                 height: '14px'
39104             }
39105              
39106             
39107         }, Roo.id());
39108     }
39109
39110     
39111     
39112 };
39113 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
39114     /**
39115      * @cfg {Store} eventStore The store that loads events.
39116      */
39117     eventStore : 25,
39118
39119      
39120     activeDate : false,
39121     startDay : 0,
39122     autoWidth : true,
39123     monitorWindowResize : false,
39124
39125     
39126     resizeColumns : function() {
39127         var col = (this.view.el.getWidth() / 7) - 3;
39128         // loop through cols, and setWidth
39129         for(var i =0 ; i < 7 ; i++){
39130             this.cm.setColumnWidth(i, col);
39131         }
39132     },
39133      setDate :function(date) {
39134         
39135         Roo.log('setDate?');
39136         
39137         this.resizeColumns();
39138         var vd = this.activeDate;
39139         this.activeDate = date;
39140 //        if(vd && this.el){
39141 //            var t = date.getTime();
39142 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
39143 //                Roo.log('using add remove');
39144 //                
39145 //                this.fireEvent('monthchange', this, date);
39146 //                
39147 //                this.cells.removeClass("fc-state-highlight");
39148 //                this.cells.each(function(c){
39149 //                   if(c.dateValue == t){
39150 //                       c.addClass("fc-state-highlight");
39151 //                       setTimeout(function(){
39152 //                            try{c.dom.firstChild.focus();}catch(e){}
39153 //                       }, 50);
39154 //                       return false;
39155 //                   }
39156 //                   return true;
39157 //                });
39158 //                return;
39159 //            }
39160 //        }
39161         
39162         var days = date.getDaysInMonth();
39163         
39164         var firstOfMonth = date.getFirstDateOfMonth();
39165         var startingPos = firstOfMonth.getDay()-this.startDay;
39166         
39167         if(startingPos < this.startDay){
39168             startingPos += 7;
39169         }
39170         
39171         var pm = date.add(Date.MONTH, -1);
39172         var prevStart = pm.getDaysInMonth()-startingPos;
39173 //        
39174         
39175         
39176         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39177         
39178         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
39179         //this.cells.addClassOnOver('fc-state-hover');
39180         
39181         var cells = this.cells.elements;
39182         var textEls = this.textNodes;
39183         
39184         //Roo.each(cells, function(cell){
39185         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
39186         //});
39187         
39188         days += startingPos;
39189
39190         // convert everything to numbers so it's fast
39191         var day = 86400000;
39192         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
39193         //Roo.log(d);
39194         //Roo.log(pm);
39195         //Roo.log(prevStart);
39196         
39197         var today = new Date().clearTime().getTime();
39198         var sel = date.clearTime().getTime();
39199         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
39200         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
39201         var ddMatch = this.disabledDatesRE;
39202         var ddText = this.disabledDatesText;
39203         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
39204         var ddaysText = this.disabledDaysText;
39205         var format = this.format;
39206         
39207         var setCellClass = function(cal, cell){
39208             
39209             //Roo.log('set Cell Class');
39210             cell.title = "";
39211             var t = d.getTime();
39212             
39213             //Roo.log(d);
39214             
39215             
39216             cell.dateValue = t;
39217             if(t == today){
39218                 cell.className += " fc-today";
39219                 cell.className += " fc-state-highlight";
39220                 cell.title = cal.todayText;
39221             }
39222             if(t == sel){
39223                 // disable highlight in other month..
39224                 cell.className += " fc-state-highlight";
39225                 
39226             }
39227             // disabling
39228             if(t < min) {
39229                 //cell.className = " fc-state-disabled";
39230                 cell.title = cal.minText;
39231                 return;
39232             }
39233             if(t > max) {
39234                 //cell.className = " fc-state-disabled";
39235                 cell.title = cal.maxText;
39236                 return;
39237             }
39238             if(ddays){
39239                 if(ddays.indexOf(d.getDay()) != -1){
39240                     // cell.title = ddaysText;
39241                    // cell.className = " fc-state-disabled";
39242                 }
39243             }
39244             if(ddMatch && format){
39245                 var fvalue = d.dateFormat(format);
39246                 if(ddMatch.test(fvalue)){
39247                     cell.title = ddText.replace("%0", fvalue);
39248                    cell.className = " fc-state-disabled";
39249                 }
39250             }
39251             
39252             if (!cell.initialClassName) {
39253                 cell.initialClassName = cell.dom.className;
39254             }
39255             
39256             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
39257         };
39258
39259         var i = 0;
39260         
39261         for(; i < startingPos; i++) {
39262             cells[i].dayName =  (++prevStart);
39263             Roo.log(textEls[i]);
39264             d.setDate(d.getDate()+1);
39265             
39266             //cells[i].className = "fc-past fc-other-month";
39267             setCellClass(this, cells[i]);
39268         }
39269         
39270         var intDay = 0;
39271         
39272         for(; i < days; i++){
39273             intDay = i - startingPos + 1;
39274             cells[i].dayName =  (intDay);
39275             d.setDate(d.getDate()+1);
39276             
39277             cells[i].className = ''; // "x-date-active";
39278             setCellClass(this, cells[i]);
39279         }
39280         var extraDays = 0;
39281         
39282         for(; i < 42; i++) {
39283             //textEls[i].innerHTML = (++extraDays);
39284             
39285             d.setDate(d.getDate()+1);
39286             cells[i].dayName = (++extraDays);
39287             cells[i].className = "fc-future fc-other-month";
39288             setCellClass(this, cells[i]);
39289         }
39290         
39291         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
39292         
39293         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
39294         
39295         // this will cause all the cells to mis
39296         var rows= [];
39297         var i =0;
39298         for (var r = 0;r < 6;r++) {
39299             for (var c =0;c < 7;c++) {
39300                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
39301             }    
39302         }
39303         
39304         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39305         for(i=0;i<cells.length;i++) {
39306             
39307             this.cells.elements[i].dayName = cells[i].dayName ;
39308             this.cells.elements[i].className = cells[i].className;
39309             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
39310             this.cells.elements[i].title = cells[i].title ;
39311             this.cells.elements[i].dateValue = cells[i].dateValue ;
39312         }
39313         
39314         
39315         
39316         
39317         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
39318         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
39319         
39320         ////if(totalRows != 6){
39321             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
39322            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
39323        // }
39324         
39325         this.fireEvent('monthchange', this, date);
39326         
39327         
39328     },
39329  /**
39330      * Returns the grid's SelectionModel.
39331      * @return {SelectionModel}
39332      */
39333     getSelectionModel : function(){
39334         if(!this.selModel){
39335             this.selModel = new Roo.grid.CellSelectionModel();
39336         }
39337         return this.selModel;
39338     },
39339
39340     load: function() {
39341         this.eventStore.load()
39342         
39343         
39344         
39345     },
39346     
39347     findCell : function(dt) {
39348         dt = dt.clearTime().getTime();
39349         var ret = false;
39350         this.cells.each(function(c){
39351             //Roo.log("check " +c.dateValue + '?=' + dt);
39352             if(c.dateValue == dt){
39353                 ret = c;
39354                 return false;
39355             }
39356             return true;
39357         });
39358         
39359         return ret;
39360     },
39361     
39362     findCells : function(rec) {
39363         var s = rec.data.start_dt.clone().clearTime().getTime();
39364        // Roo.log(s);
39365         var e= rec.data.end_dt.clone().clearTime().getTime();
39366        // Roo.log(e);
39367         var ret = [];
39368         this.cells.each(function(c){
39369              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
39370             
39371             if(c.dateValue > e){
39372                 return ;
39373             }
39374             if(c.dateValue < s){
39375                 return ;
39376             }
39377             ret.push(c);
39378         });
39379         
39380         return ret;    
39381     },
39382     
39383     findBestRow: function(cells)
39384     {
39385         var ret = 0;
39386         
39387         for (var i =0 ; i < cells.length;i++) {
39388             ret  = Math.max(cells[i].rows || 0,ret);
39389         }
39390         return ret;
39391         
39392     },
39393     
39394     
39395     addItem : function(rec)
39396     {
39397         // look for vertical location slot in
39398         var cells = this.findCells(rec);
39399         
39400         rec.row = this.findBestRow(cells);
39401         
39402         // work out the location.
39403         
39404         var crow = false;
39405         var rows = [];
39406         for(var i =0; i < cells.length; i++) {
39407             if (!crow) {
39408                 crow = {
39409                     start : cells[i],
39410                     end :  cells[i]
39411                 };
39412                 continue;
39413             }
39414             if (crow.start.getY() == cells[i].getY()) {
39415                 // on same row.
39416                 crow.end = cells[i];
39417                 continue;
39418             }
39419             // different row.
39420             rows.push(crow);
39421             crow = {
39422                 start: cells[i],
39423                 end : cells[i]
39424             };
39425             
39426         }
39427         
39428         rows.push(crow);
39429         rec.els = [];
39430         rec.rows = rows;
39431         rec.cells = cells;
39432         for (var i = 0; i < cells.length;i++) {
39433             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
39434             
39435         }
39436         
39437         
39438     },
39439     
39440     clearEvents: function() {
39441         
39442         if (!this.eventStore.getCount()) {
39443             return;
39444         }
39445         // reset number of rows in cells.
39446         Roo.each(this.cells.elements, function(c){
39447             c.rows = 0;
39448         });
39449         
39450         this.eventStore.each(function(e) {
39451             this.clearEvent(e);
39452         },this);
39453         
39454     },
39455     
39456     clearEvent : function(ev)
39457     {
39458         if (ev.els) {
39459             Roo.each(ev.els, function(el) {
39460                 el.un('mouseenter' ,this.onEventEnter, this);
39461                 el.un('mouseleave' ,this.onEventLeave, this);
39462                 el.remove();
39463             },this);
39464             ev.els = [];
39465         }
39466     },
39467     
39468     
39469     renderEvent : function(ev,ctr) {
39470         if (!ctr) {
39471              ctr = this.view.el.select('.fc-event-container',true).first();
39472         }
39473         
39474          
39475         this.clearEvent(ev);
39476             //code
39477        
39478         
39479         
39480         ev.els = [];
39481         var cells = ev.cells;
39482         var rows = ev.rows;
39483         this.fireEvent('eventrender', this, ev);
39484         
39485         for(var i =0; i < rows.length; i++) {
39486             
39487             cls = '';
39488             if (i == 0) {
39489                 cls += ' fc-event-start';
39490             }
39491             if ((i+1) == rows.length) {
39492                 cls += ' fc-event-end';
39493             }
39494             
39495             //Roo.log(ev.data);
39496             // how many rows should it span..
39497             var cg = this.eventTmpl.append(ctr,Roo.apply({
39498                 fccls : cls
39499                 
39500             }, ev.data) , true);
39501             
39502             
39503             cg.on('mouseenter' ,this.onEventEnter, this, ev);
39504             cg.on('mouseleave' ,this.onEventLeave, this, ev);
39505             cg.on('click', this.onEventClick, this, ev);
39506             
39507             ev.els.push(cg);
39508             
39509             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
39510             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
39511             //Roo.log(cg);
39512              
39513             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
39514             cg.setWidth(ebox.right - sbox.x -2);
39515         }
39516     },
39517     
39518     renderEvents: function()
39519     {   
39520         // first make sure there is enough space..
39521         
39522         if (!this.eventTmpl) {
39523             this.eventTmpl = new Roo.Template(
39524                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
39525                     '<div class="fc-event-inner">' +
39526                         '<span class="fc-event-time">{time}</span>' +
39527                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
39528                     '</div>' +
39529                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
39530                 '</div>'
39531             );
39532                 
39533         }
39534                
39535         
39536         
39537         this.cells.each(function(c) {
39538             //Roo.log(c.select('.fc-day-content div',true).first());
39539             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
39540         });
39541         
39542         var ctr = this.view.el.select('.fc-event-container',true).first();
39543         
39544         var cls;
39545         this.eventStore.each(function(ev){
39546             
39547             this.renderEvent(ev);
39548              
39549              
39550         }, this);
39551         this.view.layout();
39552         
39553     },
39554     
39555     onEventEnter: function (e, el,event,d) {
39556         this.fireEvent('evententer', this, el, event);
39557     },
39558     
39559     onEventLeave: function (e, el,event,d) {
39560         this.fireEvent('eventleave', this, el, event);
39561     },
39562     
39563     onEventClick: function (e, el,event,d) {
39564         this.fireEvent('eventclick', this, el, event);
39565     },
39566     
39567     onMonthChange: function () {
39568         this.store.load();
39569     },
39570     
39571     onLoad: function () {
39572         
39573         //Roo.log('calendar onload');
39574 //         
39575         if(this.eventStore.getCount() > 0){
39576             
39577            
39578             
39579             this.eventStore.each(function(d){
39580                 
39581                 
39582                 // FIXME..
39583                 var add =   d.data;
39584                 if (typeof(add.end_dt) == 'undefined')  {
39585                     Roo.log("Missing End time in calendar data: ");
39586                     Roo.log(d);
39587                     return;
39588                 }
39589                 if (typeof(add.start_dt) == 'undefined')  {
39590                     Roo.log("Missing Start time in calendar data: ");
39591                     Roo.log(d);
39592                     return;
39593                 }
39594                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
39595                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
39596                 add.id = add.id || d.id;
39597                 add.title = add.title || '??';
39598                 
39599                 this.addItem(d);
39600                 
39601              
39602             },this);
39603         }
39604         
39605         this.renderEvents();
39606     }
39607     
39608
39609 });
39610 /*
39611  grid : {
39612                 xtype: 'Grid',
39613                 xns: Roo.grid,
39614                 listeners : {
39615                     render : function ()
39616                     {
39617                         _this.grid = this;
39618                         
39619                         if (!this.view.el.hasClass('course-timesheet')) {
39620                             this.view.el.addClass('course-timesheet');
39621                         }
39622                         if (this.tsStyle) {
39623                             this.ds.load({});
39624                             return; 
39625                         }
39626                         Roo.log('width');
39627                         Roo.log(_this.grid.view.el.getWidth());
39628                         
39629                         
39630                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
39631                             '.course-timesheet .x-grid-row' : {
39632                                 height: '80px'
39633                             },
39634                             '.x-grid-row td' : {
39635                                 'vertical-align' : 0
39636                             },
39637                             '.course-edit-link' : {
39638                                 'color' : 'blue',
39639                                 'text-overflow' : 'ellipsis',
39640                                 'overflow' : 'hidden',
39641                                 'white-space' : 'nowrap',
39642                                 'cursor' : 'pointer'
39643                             },
39644                             '.sub-link' : {
39645                                 'color' : 'green'
39646                             },
39647                             '.de-act-sup-link' : {
39648                                 'color' : 'purple',
39649                                 'text-decoration' : 'line-through'
39650                             },
39651                             '.de-act-link' : {
39652                                 'color' : 'red',
39653                                 'text-decoration' : 'line-through'
39654                             },
39655                             '.course-timesheet .course-highlight' : {
39656                                 'border-top-style': 'dashed !important',
39657                                 'border-bottom-bottom': 'dashed !important'
39658                             },
39659                             '.course-timesheet .course-item' : {
39660                                 'font-family'   : 'tahoma, arial, helvetica',
39661                                 'font-size'     : '11px',
39662                                 'overflow'      : 'hidden',
39663                                 'padding-left'  : '10px',
39664                                 'padding-right' : '10px',
39665                                 'padding-top' : '10px' 
39666                             }
39667                             
39668                         }, Roo.id());
39669                                 this.ds.load({});
39670                     }
39671                 },
39672                 autoWidth : true,
39673                 monitorWindowResize : false,
39674                 cellrenderer : function(v,x,r)
39675                 {
39676                     return v;
39677                 },
39678                 sm : {
39679                     xtype: 'CellSelectionModel',
39680                     xns: Roo.grid
39681                 },
39682                 dataSource : {
39683                     xtype: 'Store',
39684                     xns: Roo.data,
39685                     listeners : {
39686                         beforeload : function (_self, options)
39687                         {
39688                             options.params = options.params || {};
39689                             options.params._month = _this.monthField.getValue();
39690                             options.params.limit = 9999;
39691                             options.params['sort'] = 'when_dt';    
39692                             options.params['dir'] = 'ASC';    
39693                             this.proxy.loadResponse = this.loadResponse;
39694                             Roo.log("load?");
39695                             //this.addColumns();
39696                         },
39697                         load : function (_self, records, options)
39698                         {
39699                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
39700                                 // if you click on the translation.. you can edit it...
39701                                 var el = Roo.get(this);
39702                                 var id = el.dom.getAttribute('data-id');
39703                                 var d = el.dom.getAttribute('data-date');
39704                                 var t = el.dom.getAttribute('data-time');
39705                                 //var id = this.child('span').dom.textContent;
39706                                 
39707                                 //Roo.log(this);
39708                                 Pman.Dialog.CourseCalendar.show({
39709                                     id : id,
39710                                     when_d : d,
39711                                     when_t : t,
39712                                     productitem_active : id ? 1 : 0
39713                                 }, function() {
39714                                     _this.grid.ds.load({});
39715                                 });
39716                            
39717                            });
39718                            
39719                            _this.panel.fireEvent('resize', [ '', '' ]);
39720                         }
39721                     },
39722                     loadResponse : function(o, success, response){
39723                             // this is overridden on before load..
39724                             
39725                             Roo.log("our code?");       
39726                             //Roo.log(success);
39727                             //Roo.log(response)
39728                             delete this.activeRequest;
39729                             if(!success){
39730                                 this.fireEvent("loadexception", this, o, response);
39731                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39732                                 return;
39733                             }
39734                             var result;
39735                             try {
39736                                 result = o.reader.read(response);
39737                             }catch(e){
39738                                 Roo.log("load exception?");
39739                                 this.fireEvent("loadexception", this, o, response, e);
39740                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39741                                 return;
39742                             }
39743                             Roo.log("ready...");        
39744                             // loop through result.records;
39745                             // and set this.tdate[date] = [] << array of records..
39746                             _this.tdata  = {};
39747                             Roo.each(result.records, function(r){
39748                                 //Roo.log(r.data);
39749                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
39750                                     _this.tdata[r.data.when_dt.format('j')] = [];
39751                                 }
39752                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
39753                             });
39754                             
39755                             //Roo.log(_this.tdata);
39756                             
39757                             result.records = [];
39758                             result.totalRecords = 6;
39759                     
39760                             // let's generate some duumy records for the rows.
39761                             //var st = _this.dateField.getValue();
39762                             
39763                             // work out monday..
39764                             //st = st.add(Date.DAY, -1 * st.format('w'));
39765                             
39766                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39767                             
39768                             var firstOfMonth = date.getFirstDayOfMonth();
39769                             var days = date.getDaysInMonth();
39770                             var d = 1;
39771                             var firstAdded = false;
39772                             for (var i = 0; i < result.totalRecords ; i++) {
39773                                 //var d= st.add(Date.DAY, i);
39774                                 var row = {};
39775                                 var added = 0;
39776                                 for(var w = 0 ; w < 7 ; w++){
39777                                     if(!firstAdded && firstOfMonth != w){
39778                                         continue;
39779                                     }
39780                                     if(d > days){
39781                                         continue;
39782                                     }
39783                                     firstAdded = true;
39784                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
39785                                     row['weekday'+w] = String.format(
39786                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
39787                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
39788                                                     d,
39789                                                     date.format('Y-m-')+dd
39790                                                 );
39791                                     added++;
39792                                     if(typeof(_this.tdata[d]) != 'undefined'){
39793                                         Roo.each(_this.tdata[d], function(r){
39794                                             var is_sub = '';
39795                                             var deactive = '';
39796                                             var id = r.id;
39797                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
39798                                             if(r.parent_id*1>0){
39799                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
39800                                                 id = r.parent_id;
39801                                             }
39802                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
39803                                                 deactive = 'de-act-link';
39804                                             }
39805                                             
39806                                             row['weekday'+w] += String.format(
39807                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
39808                                                     id, //0
39809                                                     r.product_id_name, //1
39810                                                     r.when_dt.format('h:ia'), //2
39811                                                     is_sub, //3
39812                                                     deactive, //4
39813                                                     desc // 5
39814                                             );
39815                                         });
39816                                     }
39817                                     d++;
39818                                 }
39819                                 
39820                                 // only do this if something added..
39821                                 if(added > 0){ 
39822                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
39823                                 }
39824                                 
39825                                 
39826                                 // push it twice. (second one with an hour..
39827                                 
39828                             }
39829                             //Roo.log(result);
39830                             this.fireEvent("load", this, o, o.request.arg);
39831                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
39832                         },
39833                     sortInfo : {field: 'when_dt', direction : 'ASC' },
39834                     proxy : {
39835                         xtype: 'HttpProxy',
39836                         xns: Roo.data,
39837                         method : 'GET',
39838                         url : baseURL + '/Roo/Shop_course.php'
39839                     },
39840                     reader : {
39841                         xtype: 'JsonReader',
39842                         xns: Roo.data,
39843                         id : 'id',
39844                         fields : [
39845                             {
39846                                 'name': 'id',
39847                                 'type': 'int'
39848                             },
39849                             {
39850                                 'name': 'when_dt',
39851                                 'type': 'string'
39852                             },
39853                             {
39854                                 'name': 'end_dt',
39855                                 'type': 'string'
39856                             },
39857                             {
39858                                 'name': 'parent_id',
39859                                 'type': 'int'
39860                             },
39861                             {
39862                                 'name': 'product_id',
39863                                 'type': 'int'
39864                             },
39865                             {
39866                                 'name': 'productitem_id',
39867                                 'type': 'int'
39868                             },
39869                             {
39870                                 'name': 'guid',
39871                                 'type': 'int'
39872                             }
39873                         ]
39874                     }
39875                 },
39876                 toolbar : {
39877                     xtype: 'Toolbar',
39878                     xns: Roo,
39879                     items : [
39880                         {
39881                             xtype: 'Button',
39882                             xns: Roo.Toolbar,
39883                             listeners : {
39884                                 click : function (_self, e)
39885                                 {
39886                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39887                                     sd.setMonth(sd.getMonth()-1);
39888                                     _this.monthField.setValue(sd.format('Y-m-d'));
39889                                     _this.grid.ds.load({});
39890                                 }
39891                             },
39892                             text : "Back"
39893                         },
39894                         {
39895                             xtype: 'Separator',
39896                             xns: Roo.Toolbar
39897                         },
39898                         {
39899                             xtype: 'MonthField',
39900                             xns: Roo.form,
39901                             listeners : {
39902                                 render : function (_self)
39903                                 {
39904                                     _this.monthField = _self;
39905                                    // _this.monthField.set  today
39906                                 },
39907                                 select : function (combo, date)
39908                                 {
39909                                     _this.grid.ds.load({});
39910                                 }
39911                             },
39912                             value : (function() { return new Date(); })()
39913                         },
39914                         {
39915                             xtype: 'Separator',
39916                             xns: Roo.Toolbar
39917                         },
39918                         {
39919                             xtype: 'TextItem',
39920                             xns: Roo.Toolbar,
39921                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
39922                         },
39923                         {
39924                             xtype: 'Fill',
39925                             xns: Roo.Toolbar
39926                         },
39927                         {
39928                             xtype: 'Button',
39929                             xns: Roo.Toolbar,
39930                             listeners : {
39931                                 click : function (_self, e)
39932                                 {
39933                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39934                                     sd.setMonth(sd.getMonth()+1);
39935                                     _this.monthField.setValue(sd.format('Y-m-d'));
39936                                     _this.grid.ds.load({});
39937                                 }
39938                             },
39939                             text : "Next"
39940                         }
39941                     ]
39942                 },
39943                  
39944             }
39945         };
39946         
39947         *//*
39948  * Based on:
39949  * Ext JS Library 1.1.1
39950  * Copyright(c) 2006-2007, Ext JS, LLC.
39951  *
39952  * Originally Released Under LGPL - original licence link has changed is not relivant.
39953  *
39954  * Fork - LGPL
39955  * <script type="text/javascript">
39956  */
39957  
39958 /**
39959  * @class Roo.LoadMask
39960  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39961  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39962  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39963  * element's UpdateManager load indicator and will be destroyed after the initial load.
39964  * @constructor
39965  * Create a new LoadMask
39966  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39967  * @param {Object} config The config object
39968  */
39969 Roo.LoadMask = function(el, config){
39970     this.el = Roo.get(el);
39971     Roo.apply(this, config);
39972     if(this.store){
39973         this.store.on('beforeload', this.onBeforeLoad, this);
39974         this.store.on('load', this.onLoad, this);
39975         this.store.on('loadexception', this.onLoadException, this);
39976         this.removeMask = false;
39977     }else{
39978         var um = this.el.getUpdateManager();
39979         um.showLoadIndicator = false; // disable the default indicator
39980         um.on('beforeupdate', this.onBeforeLoad, this);
39981         um.on('update', this.onLoad, this);
39982         um.on('failure', this.onLoad, this);
39983         this.removeMask = true;
39984     }
39985 };
39986
39987 Roo.LoadMask.prototype = {
39988     /**
39989      * @cfg {Boolean} removeMask
39990      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39991      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39992      */
39993     removeMask : false,
39994     /**
39995      * @cfg {String} msg
39996      * The text to display in a centered loading message box (defaults to 'Loading...')
39997      */
39998     msg : 'Loading...',
39999     /**
40000      * @cfg {String} msgCls
40001      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40002      */
40003     msgCls : 'x-mask-loading',
40004
40005     /**
40006      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40007      * @type Boolean
40008      */
40009     disabled: false,
40010
40011     /**
40012      * Disables the mask to prevent it from being displayed
40013      */
40014     disable : function(){
40015        this.disabled = true;
40016     },
40017
40018     /**
40019      * Enables the mask so that it can be displayed
40020      */
40021     enable : function(){
40022         this.disabled = false;
40023     },
40024     
40025     onLoadException : function()
40026     {
40027         Roo.log(arguments);
40028         
40029         if (typeof(arguments[3]) != 'undefined') {
40030             Roo.MessageBox.alert("Error loading",arguments[3]);
40031         } 
40032         /*
40033         try {
40034             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40035                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40036             }   
40037         } catch(e) {
40038             
40039         }
40040         */
40041     
40042         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40043     },
40044     // private
40045     onLoad : function()
40046     {
40047         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40048     },
40049
40050     // private
40051     onBeforeLoad : function(){
40052         if(!this.disabled){
40053             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
40054         }
40055     },
40056
40057     // private
40058     destroy : function(){
40059         if(this.store){
40060             this.store.un('beforeload', this.onBeforeLoad, this);
40061             this.store.un('load', this.onLoad, this);
40062             this.store.un('loadexception', this.onLoadException, this);
40063         }else{
40064             var um = this.el.getUpdateManager();
40065             um.un('beforeupdate', this.onBeforeLoad, this);
40066             um.un('update', this.onLoad, this);
40067             um.un('failure', this.onLoad, this);
40068         }
40069     }
40070 };/*
40071  * Based on:
40072  * Ext JS Library 1.1.1
40073  * Copyright(c) 2006-2007, Ext JS, LLC.
40074  *
40075  * Originally Released Under LGPL - original licence link has changed is not relivant.
40076  *
40077  * Fork - LGPL
40078  * <script type="text/javascript">
40079  */
40080
40081
40082 /**
40083  * @class Roo.XTemplate
40084  * @extends Roo.Template
40085  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40086 <pre><code>
40087 var t = new Roo.XTemplate(
40088         '&lt;select name="{name}"&gt;',
40089                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40090         '&lt;/select&gt;'
40091 );
40092  
40093 // then append, applying the master template values
40094  </code></pre>
40095  *
40096  * Supported features:
40097  *
40098  *  Tags:
40099
40100 <pre><code>
40101       {a_variable} - output encoded.
40102       {a_variable.format:("Y-m-d")} - call a method on the variable
40103       {a_variable:raw} - unencoded output
40104       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40105       {a_variable:this.method_on_template(...)} - call a method on the template object.
40106  
40107 </code></pre>
40108  *  The tpl tag:
40109 <pre><code>
40110         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40111         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40112         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40113         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40114   
40115         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40116         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40117 </code></pre>
40118  *      
40119  */
40120 Roo.XTemplate = function()
40121 {
40122     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40123     if (this.html) {
40124         this.compile();
40125     }
40126 };
40127
40128
40129 Roo.extend(Roo.XTemplate, Roo.Template, {
40130
40131     /**
40132      * The various sub templates
40133      */
40134     tpls : false,
40135     /**
40136      *
40137      * basic tag replacing syntax
40138      * WORD:WORD()
40139      *
40140      * // you can fake an object call by doing this
40141      *  x.t:(test,tesT) 
40142      * 
40143      */
40144     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40145
40146     /**
40147      * compile the template
40148      *
40149      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40150      *
40151      */
40152     compile: function()
40153     {
40154         var s = this.html;
40155      
40156         s = ['<tpl>', s, '</tpl>'].join('');
40157     
40158         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40159             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40160             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40161             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40162             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40163             m,
40164             id     = 0,
40165             tpls   = [];
40166     
40167         while(true == !!(m = s.match(re))){
40168             var forMatch   = m[0].match(nameRe),
40169                 ifMatch   = m[0].match(ifRe),
40170                 execMatch   = m[0].match(execRe),
40171                 namedMatch   = m[0].match(namedRe),
40172                 
40173                 exp  = null, 
40174                 fn   = null,
40175                 exec = null,
40176                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40177                 
40178             if (ifMatch) {
40179                 // if - puts fn into test..
40180                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40181                 if(exp){
40182                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40183                 }
40184             }
40185             
40186             if (execMatch) {
40187                 // exec - calls a function... returns empty if true is  returned.
40188                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40189                 if(exp){
40190                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40191                 }
40192             }
40193             
40194             
40195             if (name) {
40196                 // for = 
40197                 switch(name){
40198                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40199                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40200                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40201                 }
40202             }
40203             var uid = namedMatch ? namedMatch[1] : id;
40204             
40205             
40206             tpls.push({
40207                 id:     namedMatch ? namedMatch[1] : id,
40208                 target: name,
40209                 exec:   exec,
40210                 test:   fn,
40211                 body:   m[1] || ''
40212             });
40213             if (namedMatch) {
40214                 s = s.replace(m[0], '');
40215             } else { 
40216                 s = s.replace(m[0], '{xtpl'+ id + '}');
40217             }
40218             ++id;
40219         }
40220         this.tpls = [];
40221         for(var i = tpls.length-1; i >= 0; --i){
40222             this.compileTpl(tpls[i]);
40223             this.tpls[tpls[i].id] = tpls[i];
40224         }
40225         this.master = tpls[tpls.length-1];
40226         return this;
40227     },
40228     /**
40229      * same as applyTemplate, except it's done to one of the subTemplates
40230      * when using named templates, you can do:
40231      *
40232      * var str = pl.applySubTemplate('your-name', values);
40233      *
40234      * 
40235      * @param {Number} id of the template
40236      * @param {Object} values to apply to template
40237      * @param {Object} parent (normaly the instance of this object)
40238      */
40239     applySubTemplate : function(id, values, parent)
40240     {
40241         
40242         
40243         var t = this.tpls[id];
40244         
40245         
40246         try { 
40247             if(t.test && !t.test.call(this, values, parent)){
40248                 return '';
40249             }
40250         } catch(e) {
40251             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40252             Roo.log(e.toString());
40253             Roo.log(t.test);
40254             return ''
40255         }
40256         try { 
40257             
40258             if(t.exec && t.exec.call(this, values, parent)){
40259                 return '';
40260             }
40261         } catch(e) {
40262             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40263             Roo.log(e.toString());
40264             Roo.log(t.exec);
40265             return ''
40266         }
40267         try {
40268             var vs = t.target ? t.target.call(this, values, parent) : values;
40269             parent = t.target ? values : parent;
40270             if(t.target && vs instanceof Array){
40271                 var buf = [];
40272                 for(var i = 0, len = vs.length; i < len; i++){
40273                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40274                 }
40275                 return buf.join('');
40276             }
40277             return t.compiled.call(this, vs, parent);
40278         } catch (e) {
40279             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40280             Roo.log(e.toString());
40281             Roo.log(t.compiled);
40282             return '';
40283         }
40284     },
40285
40286     compileTpl : function(tpl)
40287     {
40288         var fm = Roo.util.Format;
40289         var useF = this.disableFormats !== true;
40290         var sep = Roo.isGecko ? "+" : ",";
40291         var undef = function(str) {
40292             Roo.log("Property not found :"  + str);
40293             return '';
40294         };
40295         
40296         var fn = function(m, name, format, args)
40297         {
40298             //Roo.log(arguments);
40299             args = args ? args.replace(/\\'/g,"'") : args;
40300             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40301             if (typeof(format) == 'undefined') {
40302                 format= 'htmlEncode';
40303             }
40304             if (format == 'raw' ) {
40305                 format = false;
40306             }
40307             
40308             if(name.substr(0, 4) == 'xtpl'){
40309                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40310             }
40311             
40312             // build an array of options to determine if value is undefined..
40313             
40314             // basically get 'xxxx.yyyy' then do
40315             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40316             //    (function () { Roo.log("Property not found"); return ''; })() :
40317             //    ......
40318             
40319             var udef_ar = [];
40320             var lookfor = '';
40321             Roo.each(name.split('.'), function(st) {
40322                 lookfor += (lookfor.length ? '.': '') + st;
40323                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40324             });
40325             
40326             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40327             
40328             
40329             if(format && useF){
40330                 
40331                 args = args ? ',' + args : "";
40332                  
40333                 if(format.substr(0, 5) != "this."){
40334                     format = "fm." + format + '(';
40335                 }else{
40336                     format = 'this.call("'+ format.substr(5) + '", ';
40337                     args = ", values";
40338                 }
40339                 
40340                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40341             }
40342              
40343             if (args.length) {
40344                 // called with xxyx.yuu:(test,test)
40345                 // change to ()
40346                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40347             }
40348             // raw.. - :raw modifier..
40349             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40350             
40351         };
40352         var body;
40353         // branched to use + in gecko and [].join() in others
40354         if(Roo.isGecko){
40355             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40356                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40357                     "';};};";
40358         }else{
40359             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40360             body.push(tpl.body.replace(/(\r\n|\n)/g,
40361                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40362             body.push("'].join('');};};");
40363             body = body.join('');
40364         }
40365         
40366         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40367        
40368         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40369         eval(body);
40370         
40371         return this;
40372     },
40373
40374     applyTemplate : function(values){
40375         return this.master.compiled.call(this, values, {});
40376         //var s = this.subs;
40377     },
40378
40379     apply : function(){
40380         return this.applyTemplate.apply(this, arguments);
40381     }
40382
40383  });
40384
40385 Roo.XTemplate.from = function(el){
40386     el = Roo.getDom(el);
40387     return new Roo.XTemplate(el.value || el.innerHTML);
40388 };